解決mybatis使用foreach批量insert異常的問題
異常
org.springframework.jdbc.BadSqlGrammarException: ### Error updating database. Cause: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'INSERT INTO t_user_role(userid,roleid)VALUES(1,3) ; INSERT INTO t_user_ro' at line 3 ### The error may involve defaultParameterMap ### The error occurred while setting parameters ### SQL: INSERT INTO t_user_role(userid,roleid)VALUES(?,?) ; INSERT INTO t_user_role(userid,roleid)VALUES(?,?) ; INSERT INTO t_user_role(userid,roleid)VALUES(?,?) ### Cause: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'INSERT INTO t_user_role(userid,roleid)VALUES(1,3) ; INSERT INTO t_user_ro' at line 3 ; bad SQL grammar []; nested exception is com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'INSERT INTO t_user_role(userid,roleid)VALUES(1,3) ; INSERT INTO t_user_ro' at line 3 at org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator.doTranslate(SQLErrorCodeSQLExceptionTranslator.java:231) at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:73) at org.mybatis.spring.MyBatisExceptionTranslator.translateExceptionIfPossible(MyBatisExceptionTranslator.java:73) at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:371) at com.sun.proxy.$Proxy13.insert(Unknown Source) at org.mybatis.spring.SqlSessionTemplate.insert(SqlSessionTemplate.java:240) at org.apache.ibatis.binding.MapperMethod.execute(MapperMethod.java:51) at org.apache.ibatis.binding.MapperProxy.invoke(MapperProxy.java:52) at com.sun.proxy.$Proxy14.addRoles(Unknown Source) at com.atguigu.atcrowdfunding.manager.service.impl.UserServiceImpl.addRoles(UserServiceImpl.java:139) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) at java.lang.reflect.Method.invoke(Unknown Source) at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:317) at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:190) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157) at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:98) at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:262) at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:95) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:207) at com.sun.proxy.$Proxy17.addRoles(Unknown Source) at com.atguigu.atcrowdfunding.manager.controller.UserController.doAddRoles(UserController.java:271) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) at java.lang.reflect.Method.invoke(Unknown Source) at org.springframework.web.bind.annotation.support.HandlerMethodInvoker.invokeHandlerMethod(HandlerMethodInvoker.java:175) at org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter.invokeHandlerMethod(AnnotationMethodHandlerAdapter.java:446) at org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter.handle(AnnotationMethodHandlerAdapter.java:434) at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:945) at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:876) at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:931) at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:833) at javax.servlet.http.HttpServlet.service(HttpServlet.java:648) at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:807) at javax.servlet.http.HttpServlet.service(HttpServlet.java:729) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:291) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206) at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:77) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:108) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206) at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206) at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:88) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:108) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206) at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:219) at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:106) at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:502) at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:142) at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79) at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:616) at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:88) at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:518) at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1091) at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:673) at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1526) at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1482) at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source) at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source) at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) at java.lang.Thread.run(Unknown Source) Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'INSERT INTO t_user_role(userid,roleid)VALUES(1,3) ; INSERT INTO t_user_ro' at line 3 at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) at sun.reflect.NativeConstructorAccessorImpl.newInstance(Unknown Source) at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(Unknown Source) at java.lang.reflect.Constructor.newInstance(Unknown Source) at com.mysql.jdbc.Util.handleNewInstance(Util.java:404) at com.mysql.jdbc.Util.getInstance(Util.java:387) at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:941) at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3870) at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3806) at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:2470) at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2617) at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2550) at com.mysql.jdbc.PreparedStatement.executeInternal(PreparedStatement.java:1861) at com.mysql.jdbc.PreparedStatement.execute(PreparedStatement.java:1192) at com.mchange.v2.c3p0.impl.NewProxyPreparedStatement.execute(NewProxyPreparedStatement.java:823) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) at java.lang.reflect.Method.invoke(Unknown Source) at org.apache.ibatis.logging.jdbc.PreparedStatementLogger.invoke(PreparedStatementLogger.java:62) at com.sun.proxy.$Proxy24.execute(Unknown Source) at org.apache.ibatis.executor.statement.PreparedStatementHandler.update(PreparedStatementHandler.java:44) at org.apache.ibatis.executor.statement.RoutingStatementHandler.update(RoutingStatementHandler.java:69) at org.apache.ibatis.executor.SimpleExecutor.doUpdate(SimpleExecutor.java:48) at org.apache.ibatis.executor.BaseExecutor.update(BaseExecutor.java:105) at org.apache.ibatis.executor.CachingExecutor.update(CachingExecutor.java:71) at org.apache.ibatis.session.defaults.DefaultSqlSession.update(DefaultSqlSession.java:152) at org.apache.ibatis.session.defaults.DefaultSqlSession.insert(DefaultSqlSession.java:141) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) at java.lang.reflect.Method.invoke(Unknown Source) at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:358) ... 65 more
異常分析
mapper.xml導(dǎo)致的錯誤地方
<insert id="addRoles"> <foreach collection="data.ids" item="id" separator=";"> INSERT INTO t_user_role(userid,roleid)VALUES(#{userId},#{id}) </foreach> </insert>
異常中說
Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'INSERT INTO t_user_role(userid,roleid)VALUES(1,3)
首先覺得是 sql語句的問題,但是用sqlyog測試發(fā)現(xiàn)沒有問題
后來發(fā)現(xiàn)是數(shù)據(jù)庫對多個語句拼在一起的操作不支持問題
解決辦法
在jdbcUrl中加入allowMultiQueries=true即可解決
jdbc.url=jdbc:mysql://localhost:3306/atcrowdfunding?allowMultiQueries=true&rewriteBatchedStatements=true&useUnicode=true&characterEncoding=utf8
補充:Mybatis foreach嵌套 批量insert map list數(shù)據(jù)
方式一:
以Teacher和Students為例 ,兩者為一對多關(guān)系:**
Teacher 實體類
private String tid; private String tName; private List<Student> studentList; public String getTid() { return tid; } public void setTid(String tid) { this.tid = tid; } public String gettName() { return tName; } public void settName(String tName) { this.tName = tName; } public List<Student> getStudentList() { return studentList; } public void setStudentList(List<Student> studentList) { this.studentList = studentList; }
Student實體類
private String sid; private String sName; public String getSid() { return sid; } public void setSid(String sid) { this.sid = sid; } public String getsName() { return sName; } public void setsName(String sName) { this.sName = sName; }
應(yīng)用場景 :在老師的service層批量插入老師數(shù)據(jù)
int nums=1000; //定義teacher list List<Teacher> teaList=new ArrayList<Teacher>(); //數(shù)據(jù)是通過excel讀取的 此處模擬循環(huán)讀取excel row數(shù)據(jù) for(int rowi=0;rowi<nums;rowi++){ Teacher teacher=new Teacher(); teacher.settName("xxx"); List<Student> stuList=new ArrayList<Student>(); Student student=new Student(); //此處獲取Students集合 for(xxx){ stuList.add(student); } //把student集合和teacher綁定 teacher.setStudentList(stuList); //把teacher放入list teaList.add(teacher); } // 批量插入老師數(shù)據(jù) teacherMapper.batchInsertTeacher(teaList); //此時每個teacher對象都有返回的主鍵id值 //老師主鍵 對應(yīng)一個list(學生信息) map Map<String,List<Student>> stuMap=new HashMap<>(); //填入數(shù)據(jù) 老師主鍵一對多學生信息 for(Teacher tea:teaList){ //非空判斷 避免老師沒有對應(yīng)學生的情況(此處只進行邏輯處理 不考慮現(xiàn)實中老師沒有學生) if(tea.getStudentList()!=null&&tea.getStudentList().size()>0){ stuMap.put(tea.getTid(),tea.getStudentList()); } } //調(diào)用學生service的批量保存學生方法 studentService.batchInsertStudent(stuMap);
student Mapper接口文件
int batchInsertStudent(@Param("stuMap") Map<String,List<Student>> stuMap);
student mapper.xml文件(寫法一:)
<insert id="batchInsertStudent" parameterType="java.util.Map"> INSERT INTO bs_student (sid,sName,tid) values <foreach collection="stuMap.keys" index="key" item="itemKey" separator=","> <foreach collection="stuMap[itemKey]" index="index_list" item="list" separator="," > ( (select REPLACE(UUID(),'-','') AS sid) ,#{list.sName},#{itemKey} ) </foreach> </foreach> </insert>
student mapper.xml文件(寫法二:)
<insert id="batchInsertStudent" > INSERT INTO bs_student (sid,sName,tid) <foreach collection="stuMap.keys" index="index" item="itemKey" separator="UNION ALL"> <foreach collection="stuMap[itemKey]" index="index_list" item="list" separator="UNION ALL"> ?。? SELECT (select REPLACE(UUID(),'-','') AS sid) ,#{list.sName} ,#{itemKey} FROM DUAL ?。? </foreach> </foreach> </insert>
stuMap:就是在接口中使用@Param(“stuMap”)標注了變量;
使用stuMap.keys可以取到所有的key,遍歷。
內(nèi)層循環(huán)中使用stuMap[itemKey]類似stuMap.get(“key”)取當前key對應(yīng)的value值。由于value是list所以還需要遍歷。
結(jié)果保存成功。再次膜拜強大的Mybatis。
至此 成功插入student數(shù)據(jù)。
方式二(推薦):
自定義類
class EnclosingType{ private String uuid; private List<ElementType> elements; }
class ElementType{ String a; String b; (...) }
Mapper.xml(方法一)
<mapper namespace="my.example.ElementType"> <insert id="insertElements" parameterType="EnclosingType"> INSERT INTO table1(enclosingTypeId,column_a,column_b) VALUES <foreach collection="elements" index="index" item="list" separator=","> ( #{uuid,jdbcType=VARCHAR} ,#{list.a,jdbcType=VARCHAR} ,#{list.b,jdbcType=VARCHAR} ) </foreach> </insert> </mapper>
Mapper.xml(方法二)
<mapper namespace="my.example.ElementType"> <insert id="insertElements" parameterType="EnclosingType"> INSERT INTO table1(enclosingTypeId,column_a,column_b) <foreach collection="elements" index="index" item="list" separator="union all"> ( select #{uuid,jdbcType=VARCHAR} ,#{list.a,jdbcType=VARCHAR} ,#{list.b,jdbcType=VARCHAR} from dual ) </foreach> </insert> </mapper>
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。如有錯誤或未考慮完全的地方,望不吝賜教。
相關(guān)文章
java中extends與implements的區(qū)別淺談
java中extends與implements的區(qū)別淺談,需要的朋友可以參考一下2013-03-03springboot中使用Feign整合nacos,gateway進行微服務(wù)之間的調(diào)用方法
這篇文章主要介紹了springboot中使用Feign整合nacos,gateway進行微服務(wù)之間的調(diào)用方法,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-03-03Mybatis mapper標簽中配置子標簽package的坑及解決
這篇文章主要介紹了Mybatis mapper標簽中配置子標簽package的坑及解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-09-09解決springboot URL帶有斜杠的轉(zhuǎn)義字符百分之2F導(dǎo)致的400錯誤
這篇文章主要介紹了解決springboot URL帶有斜杠的轉(zhuǎn)義字符百分之2F導(dǎo)致的400錯誤問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-08-08Filter、Servlet、Listener的學習_動力節(jié)點Java學院整理
這篇文章主要為大家詳細介紹了Filter、Servlet、Listener的學習資料,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-07-07Echarts+SpringMvc顯示后臺實時數(shù)據(jù)
這篇文章主要為大家詳細介紹了Echarts+SpringMvc顯示后臺實時數(shù)據(jù),文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2019-12-12