鑒於上一篇博文一次修改mysql字段類型引發的技術探究提到的,要對foreach里面的collection相關的內容做一些介紹,今天就圍繞foreach,做一些數據插入和查詢相關的研究。
首先介紹一下我的環境:
1. linux redhat7
2. mysql 5.6
3. java7
4. mybatis 3.2.7 (后來遇到問題,更新到3.3.1)
第一步,在數據庫中創建測試用的表 foreach_test。如下:
mysql> desc foreach_test; +-------+-------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +-------+-------------+------+-----+---------+----------------+ | id | int(11) | NO | PRI | NULL | auto_increment | | name | varchar(32) | YES | | NULL | | | age | int(8) | YES | | NULL | | | idx | int(4) | YES | | NULL | | +-------+-------------+------+-----+---------+----------------+ 4 rows in set (0.00 sec)
第二步,進行mybatis相關的mapper以及dao進行配置。今天研究和介紹的關於foreach的相關內容,將從insert以及select兩個大類進行案例介紹,其中,insert是重點,因為批量數據插入,相對比較復雜點,涉及到鍵值的更新過程。對於mysql數據庫而言,對於第一步中創建的數據表foreach_test,有一個主鍵id,是數字自增型的。這里,結合mybatis的官方文檔介紹,將會有兩種更新主鍵的方法:selectKey,以及useGeneratedKeys=“true” 下面將結合這兩種方法,以及foreach的collection能夠支持的三種集合類型list,array以及map進行案例分析。
1. selectKey方案的插入,數據采用list傳入
mapper的sql語句:
<insert id="foreachSelectKeyInsert" parameterType="java.util.List"> <selectKey resultType ="java.lang.Integer" keyProperty= "iid" order= "AFTER"> SELECT LAST_INSERT_ID() </selectKey > insert into foreach_test (name, age, idx) values <foreach item="st" collection="list" index="idx" open="" separator="," close=""> (#{st.name, jdbcType=VARCHAR}, #{st.age, jdbcType=INTEGER}, #{idx}) </foreach> </insert>
dao層的接口:
int foreachSelectKeyInsert(List<Du> dud);
java業務邏輯:
@GET @Path("/foreach/selectkey/insert") public String foreachSelectKeyInsert(@Context HttpServletRequest req){ List<Du> dud = new ArrayList<Du>(); for(int i=1; i < 5; i++){ Du du1 = new Du(); du1.setName("SelectKey" + i); du1.setAge(30+i); dud.add(du1); } pes.foreachSelectKeyInsert(dud); return "SelectKey Insert OK"; }
在地址欄輸入:
http://10.90.9.20:8080/ecs/demo/foreach/selectkey/insert
數據庫中得到:
mysql> select * from foreach_test; +----+------------+------+------+ | id | name | age | idx | +----+------------+------+------+ | 1 | SelectKey1 | 31 | 0 | | 2 | SelectKey2 | 32 | 1 | | 3 | SelectKey3 | 33 | 2 | | 4 | SelectKey4 | 34 | 3 | +----+------------+------+------+ 4 rows in set (0.00 sec)
2. useGeneratedKeys=”true“方案的插入,數據采用list傳入
mapper的sql語句:
<insert id="foreachUseGeneratedKeysInsert1" keyProperty="id" useGeneratedKeys="true"> insert into foreach_test (name, age, idx) values <foreach item="st" collection="list" index="idx" open="" separator="," close=""> (#{st.name, jdbcType=VARCHAR}, #{st.age, jdbcType=INTEGER}, #{idx}) </foreach> </insert>
dao接口:
int foreachUseGeneratedKeysInsert1(List<Du> dud);
java業務邏輯:
@GET @Path("/foreach/usegeneratedkeys/insert1") public String foreachUseGeneratedKeysInsert1(@Context HttpServletRequest req){ List<Du> dud = new ArrayList<Du>(); for(int i=6; i < 10; i++){ Du du1 = new Du(); du1.setName("UseGeneratedKeys1" + i); du1.setAge(30+i); dud.add(du1); } pes.foreachUseGeneratedKeysInsert1(dud); return "UseGeneratedKeys1 Insert OK"; }
在地址欄輸入:
http://10.90.9.20:8080/ecs/demo/foreach/usegeneratedkeys/insert1
結果爆出錯誤:

org.apache.ibatis.binding.BindingException: Parameter 'id' not found. Available parameters are [list] at org.apache.ibatis.session.defaults.DefaultSqlSession$StrictMap.get(DefaultSqlSession.java:261) at org.apache.ibatis.reflection.wrapper.MapWrapper.getSetterType(MapWrapper.java:79) at org.apache.ibatis.reflection.MetaObject.getSetterType(MetaObject.java:91) at org.apache.ibatis.executor.keygen.Jdbc3KeyGenerator.getTypeHandlers(Jdbc3KeyGenerator.java:82) at org.apache.ibatis.executor.keygen.Jdbc3KeyGenerator.processBatch(Jdbc3KeyGenerator.java:61) at org.apache.ibatis.executor.keygen.Jdbc3KeyGenerator.processAfter(Jdbc3KeyGenerator.java:45) at org.apache.ibatis.executor.statement.PreparedStatementHandler.update(PreparedStatementHandler.java:48) 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(NativeMethodAccessorImpl.java:57) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:606) at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:358) at com.sun.proxy.$Proxy11.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.$Proxy32.foreachUseGeneratedKeysInsert1(Unknown Source) at com.tg.ecs.ucc.service.impl.PurchaseElementService.foreachUseGeneratedKeysInsert1(PurchaseElementService.java:116) at com.tg.ecs.ucc.service.impl.PurchaseElementService$$FastClassBySpringCGLIB$$4e94c14f.invoke(<generated>) at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204) at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:700) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:150) at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:96) at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:260) at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:94) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172) at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:91) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172) at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:633) at com.tg.ecs.ucc.service.impl.PurchaseElementService$$EnhancerBySpringCGLIB$$d26e54fd.foreachUseGeneratedKeysInsert1(<generated>) at com.tg.ecs.ucc.service.impl.PurchaseElementService$$FastClassBySpringCGLIB$$4e94c14f.invoke(<generated>) at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204) at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:700) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:150) at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:96) at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:260) at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:94) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172) at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:633) at com.tg.ecs.ucc.service.impl.PurchaseElementService$$EnhancerBySpringCGLIB$$5a877984.foreachUseGeneratedKeysInsert1(<generated>) at com.tg.ecs.test.DemoController.foreachUseGeneratedKeysInsert1(DemoController.java:90) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:606) at org.glassfish.jersey.server.model.internal.ResourceMethodInvocationHandlerFactory$1.invoke(ResourceMethodInvocationHandlerFactory.java:81) at org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher$1.run(AbstractJavaResourceMethodDispatcher.java:144) at org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher.invoke(AbstractJavaResourceMethodDispatcher.java:161) at org.glassfish.jersey.server.model.internal.JavaResourceMethodDispatcherProvider$TypeOutInvoker.doDispatch(JavaResourceMethodDispatcherProvider.java:205) at org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher.dispatch(AbstractJavaResourceMethodDispatcher.java:99) at org.glassfish.jersey.server.model.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:389) at org.glassfish.jersey.server.model.ResourceMethodInvoker.apply(ResourceMethodInvoker.java:347) at org.glassfish.jersey.server.model.ResourceMethodInvoker.apply(ResourceMethodInvoker.java:102) at org.glassfish.jersey.server.ServerRuntime$2.run(ServerRuntime.java:326) at org.glassfish.jersey.internal.Errors$1.call(Errors.java:271) at org.glassfish.jersey.internal.Errors$1.call(Errors.java:267) at org.glassfish.jersey.internal.Errors.process(Errors.java:315) at org.glassfish.jersey.internal.Errors.process(Errors.java:297) at org.glassfish.jersey.internal.Errors.process(Errors.java:267) at org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:317) at org.glassfish.jersey.server.ServerRuntime.process(ServerRuntime.java:305) at org.glassfish.jersey.server.ApplicationHandler.handle(ApplicationHandler.java:1154) at org.glassfish.jersey.servlet.WebComponent.serviceImpl(WebComponent.java:473) at org.glassfish.jersey.servlet.ServletContainer.serviceImpl(ServletContainer.java:408) at org.glassfish.jersey.servlet.ServletContainer.doFilter(ServletContainer.java:583) at org.glassfish.jersey.servlet.ServletContainer.doFilter(ServletContainer.java:524) at org.glassfish.jersey.servlet.ServletContainer.doFilter(ServletContainer.java:461) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:240) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207) at com.tg.ecs.core.xss.XssFilter.doFilter(XssFilter.java:43) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:240) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207) at org.apache.shiro.web.servlet.AbstractShiroFilter.executeChain(AbstractShiroFilter.java:449) at org.apache.shiro.web.servlet.AbstractShiroFilter$1.call(AbstractShiroFilter.java:365) at org.apache.shiro.subject.support.SubjectCallable.doCall(SubjectCallable.java:90) at org.apache.shiro.subject.support.SubjectCallable.call(SubjectCallable.java:83) at org.apache.shiro.subject.support.DelegatingSubject.execute(DelegatingSubject.java:383) at org.apache.shiro.web.servlet.AbstractShiroFilter.doFilterInternal(AbstractShiroFilter.java:362) at org.apache.shiro.web.servlet.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:125) at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:343) at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:260) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:240) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207) at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:88) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:106) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:240) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207) at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:212) 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:141) 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:522) at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1095) at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:672) at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1502) at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1458) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615) at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) at java.lang.Thread.run(Thread.java:744)
這個錯誤,折騰了我好久,在stackoverflow網站找到了一個老外的類似問題,得到線索,說是mybatis的版本問題,3.3.1之后(我當前的研究環境是3.2.7),這個問題才得以解決。在沒有驗證是否正確之前,繼續當前環境下的研究。前端報錯了,依舊查看數據庫,看是否有數據寫入:
mysql> select * from foreach_test; +----+--------------------+------+------+ | id | name | age | idx | +----+--------------------+------+------+ | 1 | UseGeneratedKeys16 | 36 | 0 | | 2 | UseGeneratedKeys17 | 37 | 1 | | 3 | UseGeneratedKeys18 | 38 | 2 | | 4 | UseGeneratedKeys19 | 39 | 3 | +----+--------------------+------+------+ 4 rows in set (0.00 sec)
說明,數據是寫入了庫,但是前端這個錯誤很不友好。
雖然報錯,但是從插入數據庫的結果得出結論:傳入給mybatis的list(單獨參數),collection的值,默認是list,此時,item的內容為list的元素,index的值為遍歷list的序號,從0開始遞增。
3. useGeneratedKeys=”true“方案的插入,數據采用map傳入
mapper的sql語句:
<insert id="foreachUseGeneratedKeysInsert2" keyProperty="id" useGeneratedKeys="true"> insert into foreach_test (name, age, idx) values <foreach item="st" collection="dudkey" index="idx" open="(" separator="),(" close=")"> #{st.name, jdbcType=VARCHAR}, #{st.age, jdbcType=INTEGER}, #{idx} </foreach> </insert>
dao接口:
int foreachUseGeneratedKeysInsert2(HashMap<String, List<Du>> dumap);
java業務邏輯:
@GET @Path("/foreach/usegeneratedkeys/insert2") public String foreachUseGeneratedKeysInsert2(@Context HttpServletRequest req){ List<Du> dud = new ArrayList<Du>(); for(int i=6; i < 10; i++){ Du du1 = new Du(); du1.setName("UseGeneratedKeys2" + i); du1.setAge(30+i); dud.add(du1); } HashMap<String, List<Du>> dumap = new HashMap<String, List<Du>>(); dumap.put("dudkey", dud); pes.foreachUseGeneratedKeysInsert2(dumap); return "UseGeneratedKeys2 Insert OK"; }
在地址欄輸入:
http://10.90.9.20:8080/ecs/demo/foreach/usegeneratedkeys/insert2
查看數據庫:
mysql> select * from foreach_test; +----+--------------------+------+------+ | id | name | age | idx | +----+--------------------+------+------+ | 1 | UseGeneratedKeys26 | 36 | 0 | | 2 | UseGeneratedKeys27 | 37 | 1 | | 3 | UseGeneratedKeys28 | 38 | 2 | | 4 | UseGeneratedKeys29 | 39 | 3 | +----+--------------------+------+------+ 4 rows in set (0.00 sec)
奇怪吧,3.2.7的環境(mybatis-spring版本是1.2.4)下,傳入map,批量插入不報前面出現的id找不到的錯誤。
從這個案例,可以看出:傳入mybatis的map,若給collection的值是入參map中的key的話,那么item的值將是map的value,此時的index值,就是map中value遍歷的序號,從0開始遞增。
到此,將mybatis的版本進行升級,升級到3.3.1,去maven倉庫下載該版本:http://mvnrepository.com/artifact/org.mybatis/mybatis/3.3.1,更新到項目中,然后啟動項目進行驗證,哎,不幸運啊,又有新錯誤:
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'shiroRealm': Injection of resource dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'sysMenuService': Invocation of init method failed; nested exception is java.lang.AbstractMethodError: org.mybatis.spring.transaction.SpringManagedTransaction.getTimeout()Ljava/lang/Integer; at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.postProcessPropertyValues(CommonAnnotationBeanPostProcessor.java:307) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1148) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:519) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:458) at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:293) at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:223) at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:290) at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:191) at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:328) ... 65 more Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'sysMenuService': Invocation of init method failed; nested exception is java.lang.AbstractMethodError: org.mybatis.spring.transaction.SpringManagedTransaction.getTimeout()Ljava/lang/Integer; at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor.postProcessBeforeInitialization(InitDestroyAnnotationBeanPostProcessor.java:133) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsBeforeInitialization(AbstractAutowireCapableBeanFactory.java:396) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1507) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:521) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:458) at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:293) at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:223) at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:290) at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:195) at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.autowireResource(CommonAnnotationBeanPostProcessor.java:449) at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.getResource(CommonAnnotationBeanPostProcessor.java:423) at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor$ResourceElement.getResourceToInject(CommonAnnotationBeanPostProcessor.java:551) at org.springframework.beans.factory.annotation.InjectionMetadata$InjectedElement.inject(InjectionMetadata.java:169) at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:88) at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.postProcessPropertyValues(CommonAnnotationBeanPostProcessor.java:304) ... 73 more Caused by: java.lang.AbstractMethodError: org.mybatis.spring.transaction.SpringManagedTransaction.getTimeout()Ljava/lang/Integer; at org.apache.ibatis.executor.SimpleExecutor.prepareStatement(SimpleExecutor.java:85) at org.apache.ibatis.executor.SimpleExecutor.doQuery(SimpleExecutor.java:62) at org.apache.ibatis.executor.BaseExecutor.queryFromDatabase(BaseExecutor.java:324) at org.apache.ibatis.executor.BaseExecutor.query(BaseExecutor.java:156) at org.apache.ibatis.executor.CachingExecutor.query(CachingExecutor.java:109) at org.apache.ibatis.executor.CachingExecutor.query(CachingExecutor.java:83) at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:148) at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:141) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:606) at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:358) at com.sun.proxy.$Proxy11.selectList(Unknown Source) at org.mybatis.spring.SqlSessionTemplate.selectList(SqlSessionTemplate.java:198) at org.apache.ibatis.binding.MapperMethod.executeForMany(MapperMethod.java:137) at org.apache.ibatis.binding.MapperMethod.execute(MapperMethod.java:75) at org.apache.ibatis.binding.MapperProxy.invoke(MapperProxy.java:59) at com.sun.proxy.$Proxy17.getListWithUrlNotNull(Unknown Source) at com.tg.ecs.system.service.impl.SysMenuService.getListWithUrlNotNull(SysMenuService.java:101) at com.tg.ecs.system.service.impl.SysMenuService.initFilterChain(SysMenuService.java:106) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:606) at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor$LifecycleElement.invoke(InitDestroyAnnotationBeanPostProcessor.java:344) at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor$LifecycleMetadata.invokeInitMethods(InitDestroyAnnotationBeanPostProcessor.java:295) at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor.postProcessBeforeInitialization(InitDestroyAnnotationBeanPostProcessor.java:130) ... 87 more
看這個錯誤,應該是mybatis-spring的版本與mybatis不匹配的原因造成的。於是,我去maven上查看mybatis-spring支持的mybatis的版本,我選擇mybatis-spring 1.2.5的版本:再次啟動軟件,上述mybatis-spring的錯誤解除。
到目前為止,我的研究環境,變成了mybatis 3.3.1,mybatis-spring 1.2.5. 繼續后續研究!
在這個新的環境下,繼續上述案例2的測試,在地址欄輸入 http://10.90.9.20:8080/ecs/demo/foreach/usegeneratedkeys/insert1, 這次不再報錯。所以,上面案例2的錯誤,還真是stackoverflow上人家描述的問題導致的。
4. useGeneratedKeys=”true“方案的插入,數據采用map傳入,注意與案例3的不同,在於傳入的數據,此處是純map,collection就是一個注解指定名稱了的map,而案例3中,foreach collection其實是map的key
mapper層的sql:
<insert id="foreachUseGeneratedKeysInsert3" useGeneratedKeys="true"> <!-- <selectKey resultType ="java.lang.Integer" keyProperty= "iid" order= "AFTER"> SELECT LAST_INSERT_ID() </selectKey > --> insert into foreach_test (name, age, idx) values <foreach item="dt" collection="map" index="key" open="(" separator="),(" close=")"> #{dt.name, jdbcType=VARCHAR}, #{dt.age, jdbcType=INTEGER}, #{key} </foreach> </insert>
dao層的接口:
int foreachUseGeneratedKeysInsert3(@Param("map") HashMap<Integer, Du> dumap);
java層的業務邏輯:
@GET @Path("/foreach/usegeneratedkeys/insert3") public String foreachUseGeneratedKeysInsert3(@Context HttpServletRequest req){ List<Du> dud = new ArrayList<Du>(); HashMap<Integer, Du> dumap = new HashMap<Integer, Du>(); for(int i=30; i < 33; i++){ Du du = new Du(); du.setName("UseGeneratedKeys3" + i); du.setAge(30+i); dud.add(du); dumap.put(i, du); } int cnt = pes.foreachUseGeneratedKeysInsert3(dumap); System.out.println(cnt); return "UseGeneratedKeys3 Insert OK"; }
在地址欄輸入:
mysql> select * from foreach_test; +----+---------------------+------+------+ | id | name | age | idx | +----+---------------------+------+------+ | 1 | UseGeneratedKeys332 | 62 | 32 | | 2 | UseGeneratedKeys331 | 61 | 31 | | 3 | UseGeneratedKeys330 | 60 | 30 | +----+---------------------+------+------+ 3 rows in set (0.00 sec)
這個案例:可以看出,傳入map給mybatis時,foreach的collection,采用dao層接口注解指定的變量名,然后,index指的是map的key值,而item的內容,就是map中index所對應的key值指向的value,即du對象。
5. useGeneratedKeys=”true“方案的插入,數據采用array傳入
mapper的sql:
<insert id="foreachUseGeneratedKeysInsert4" useGeneratedKeys="true"> insert into foreach_test (name, age, idx) values <foreach item="dt" collection="array" index="seq" open="(" separator="),(" close=")"> #{dt.name, jdbcType=VARCHAR}, #{dt.age, jdbcType=INTEGER}, #{seq} </foreach> </insert>
dao接口:
int foreachUseGeneratedKeysInsert4(Du[] duarray);
java應用層的邏輯:
@GET @Path("/foreach/usegeneratedkeys/insert4") public String foreachUseGeneratedKeysInsert4(@Context HttpServletRequest req){ Du [] dud = new Du[4]; for(int i=0; i < dud.length ; i++){ Du du = new Du(); du.setName("UseGeneratedKeys4" + i); du.setAge(30+i); dud[i] = du; } int cnt = pes.foreachUseGeneratedKeysInsert4(dud); System.out.println(cnt); return "UseGeneratedKeys4 Insert OK"; }
在地址欄輸入:
http://10.90.9.20:8080/ecs/demo/foreach/usegeneratedkeys/insert4
查看數據庫得到下面的結果:
mysql> select * from foreach_test; +----+--------------------+------+------+ | id | name | age | idx | +----+--------------------+------+------+ | 1 | UseGeneratedKeys40 | 30 | 0 | | 2 | UseGeneratedKeys41 | 31 | 1 | | 3 | UseGeneratedKeys42 | 32 | 2 | | 4 | UseGeneratedKeys43 | 33 | 3 | +----+--------------------+------+------+ 4 rows in set (0.00 sec)
上述運行一切正常,從結果來看:當傳入mybatis的數據為Array時,collection的值默認就是array,除非在dao層通過注解@Param("xxx")指定其他名稱。item的值為數組的元素,index的值為遍歷數組的序號,從0開始,依次遞增。
6.foreach用於批量的查詢
mapper的sql:
<select id="foreachSelect" resultType="com.tg.ecs.ucc.model.Du"> select name, age from foreach_test where idx in <foreach item="st" collection="map" index="seq" open="(" separator="," close=")"> #{st, jdbcType=INTEGER} </foreach> </select>
dao接口:
List<Du> foreachSelect(@Param("map") HashMap<String, Integer> idxMap);
java的業務邏輯實現:
@GET @Path("/foreach/select") public String foreachSelect(@Context HttpServletRequest req){ HashMap<String, Integer> idxMap = new HashMap<String, Integer>(); for(int i=0; i<5; i++){ idxMap.put("key" + i, i); } List<Du> res = pes.foreachSelect(idxMap); for(Du du: res){ System.out.println(du.getName() + " -- " + du.getAge()); } return "Select OK"; }
運行web應用前,查看數據庫內容:
mysql> select * from foreach_test; +----+---------------------+------+------+ | id | name | age | idx | +----+---------------------+------+------+ | 1 | UseGeneratedKeys40 | 30 | 0 | | 2 | UseGeneratedKeys41 | 31 | 1 | | 3 | UseGeneratedKeys42 | 32 | 2 | | 4 | UseGeneratedKeys43 | 33 | 3 | | 5 | UseGeneratedKeys332 | 62 | 32 | | 6 | UseGeneratedKeys331 | 61 | 31 | | 7 | UseGeneratedKeys330 | 60 | 30 | | 8 | UseGeneratedKeys16 | 36 | 0 | | 9 | UseGeneratedKeys17 | 37 | 1 | | 10 | UseGeneratedKeys18 | 38 | 2 | | 11 | UseGeneratedKeys19 | 39 | 3 | +----+---------------------+------+------+ 11 rows in set (0.00 sec)
運行web應用,在地址欄輸入:
http://10.90.9.20:8080/ecs/demo/foreach/select
eclipse控制台打印的結果:
UseGeneratedKeys40 -- 30 UseGeneratedKeys41 -- 31 UseGeneratedKeys42 -- 32 UseGeneratedKeys43 -- 33 UseGeneratedKeys16 -- 36 UseGeneratedKeys17 -- 37 UseGeneratedKeys18 -- 38 UseGeneratedKeys19 -- 39
結合上面的java代碼和sql,輸出結果是正確的。
最后,總結一下:
1. mybatis的insert操作,這里主要指批量插入操作,要注意鍵值的生成問題,對於mysql,有selectKey和useGeneratedKeys=“true”兩種方案,例如本案例中,主鍵是auto_increment的,所以,用useGeneratedKeys=“true”相對簡單(前提是數據庫支持自動生成鍵值)。
2. foreach的使用,主要是搞清楚item,collection,index,open,separator,close幾個屬性的使用。其中,最最重要的是collection和index的含義。本博文中,針對collection支持的三種類型:list,array以及map都做了介紹,而map相對復雜點,原則上map支持任何參數的傳入。要結合map的構造方式,以及dao層給map參數指定的參數名。對於map,collection的值若是map的key,則index是個序號,遍歷map的次序編號;若collection的值給的是map,那么,index的值就是map的key,item的值就是key對於的value。
3. foreach中的open,separator以及close,配合使用,用於構造SQL語句。例如in指令的語句中,in后面是一個序列,所以習慣用open=“(”,separator=“,”,close=“)”。當然也不一定,這個的值,還和foreach標簽體內的SQL的寫法有關系。例如本博文案例2和案例3的這open,separator和close的值,就可以看出門道。
好了,本博文就到這里吧,若有什么需要探討的,可以給我留言或者加好友討論!