Spring MVC @Transactional注解方式事务失效的解决办法 ╰半橙微兮° 2022-07-24 12:13 145阅读 0赞 首先展示问题: Spring applicationContext.xml配置: **\[html\]** [view plain][] [copy][view plain] [![在CODE上查看代码片][CODE]][CODE_CODE] [![派生到我的代码片][ico_fork.svg]][ico_fork.svg 1] 1. 2. **<****bean** id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean"**>** 3. **<****property** name="jndiName"**>** 4. **<****value****>**java:comp/env/jdbc/will**</****value****>** 5. **</****property****>** 6. **</****bean****>** 7. 8. **<****bean** id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"**>** 9. **<****property** name="dataSource" ref="dataSource" **/>** 10. **</****bean****>** 11. 12. **<****bean** id="txManager" 13. class="org.springframework.jdbc.datasource.DataSourceTransactionManager"**>** 14. **<****property** name="dataSource" ref="dataSource" **/>** 15. **</****bean****>** 16. 17. <!-- 事务控制 --> 18. **<****tx:annotation-driven** transaction-manager="txManager" **/>** Spring mvc.dispatcher.xml配置: **\[html\]** [view plain][] [copy][view plain] [![在CODE上查看代码片][CODE]][CODE_CODE] [![派生到我的代码片][ico_fork.svg]][ico_fork.svg 1] 1. <!-- 自动扫描的包名 --> 2. **<****context:component-scan** base-package="com.will" **>** 3. **</****context:component-scan****>** 4. 5. <!-- 默认的注解映射的支持 --> 6. **<****mvc:annotation-driven** **/>** 7. 8. <!-- 对静态资源文件的访问 --> 9. **<****mvc:default-servlet-handler****/>** 10. 11. 12. <!-- 拦截器 13. **<****mvc:interceptors****>** 14. **<****bean** class="com.will.mvc.MyInteceptor" **/>** 15. **</****mvc:interceptors****>** 16. \--**>** 17. 18. <!-- 视图解释类 --> 19. **<****bean** id="viewResolver" 20. class="org.springframework.web.servlet.view.UrlBasedViewResolver"**>** 21. **<****property** name="viewClass" value="org.springframework.web.servlet.view.JstlView" **/>** 22. **<****property** name="prefix" value="/WEB-INF/pages/" **/>** 23. **<****property** name="suffix" value=".jsp" **/>** 24. **</****bean****>** 然后在Service层模拟了一个事务回滚的method case: **\[java\]** [view plain][] [copy][view plain] [![在CODE上查看代码片][CODE]][CODE_CODE] [![派生到我的代码片][ico_fork.svg]][ico_fork.svg 1] 1. @Transactional 2. **public** **boolean** save(Person person) 3. \{ 4. **for**(**int** id: **new** **int**\[\]\{ 2,3\}) 5. \{ 6. personDao.del(id); 7. **int** j = 1/0; 8. \} 9. 10. **return** **false**; 11. \} ## 本以为大功告成,在运行save方法时,由于1/0 抛出 java.lang.ArithmeticException: / by zero `RuntimeException,导致事务回归。However,no way! So crazy~` ## `查了下,发现Spring MVC对于事务配置比较讲究,需要额外的配置。解决办法如下:` 需要在 applicationContext.xml增加: **\[html\]** [view plain][] [copy][view plain] [![在CODE上查看代码片][CODE]][CODE_CODE] [![派生到我的代码片][ico_fork.svg]][ico_fork.svg 1] 1. **<****context:component-scan** base-package="com.will"**>** 2. **<****context:exclude-filter** type="annotation" expression="org.springframework.stereotype.Controller" **/>** 3. **</****context:component-scan****>** 在 Spring mvc.dispatcher.xml增加: **\[html\]** [view plain][] [copy][view plain] [![在CODE上查看代码片][CODE]][CODE_CODE] [![派生到我的代码片][ico_fork.svg]][ico_fork.svg 1] 1. **<****context:component-scan** base-package="com.will" **>** 2. **<****context:include-filter** type="annotation" expression="org.springframework.stereotype.Controller" **/>** 3. **<****context:exclude-filter** type="annotation" expression="org.springframework.stereotype.Service" **/>** 4. **</****context:component-scan****>** 由于web.xml中配置: **\[html\]** [view plain][] [copy][view plain] [![在CODE上查看代码片][CODE]][CODE_CODE] [![派生到我的代码片][ico_fork.svg]][ico_fork.svg 1] 1. **<****context-param****>** 2. **<****param-name****>**contextConfigLocation**</****param-name****>** 3. **<****param-value****>** 4. classpath:applicationContext.xml 5. **</****param-value****>** 6. **</****context-param****>** 7. **<****listener****>** 8. **<****listener-class****>**org.springframework.web.context.ContextLoaderListener**</****listener-class****>** 9. **</****listener****>** 10. **<****servlet****>** 11. **<****servlet-name****>**dispatcher**</****servlet-name****>** 12. **<****servlet-class****>**org.springframework.web.servlet.DispatcherServlet**</****servlet-class****>** 13. **<****init-param****>** 14. **<****param-name****>**contextConfigLocation**</****param-name****>** 15. **<****param-value****>**classpath\*:/mvc\_dispatcher\_servlet.xml**</****param-value****>** 16. **</****init-param****>** 17. **<****load-on-startup****>**1**</****load-on-startup****>** 18. **</****servlet****>** 19. **<****servlet-mapping****>** 20. **<****servlet-name****>**dispatcher**</****servlet-name****>** 21. **<****url-pattern****>**\*.do**</****url-pattern****>** 22. **</****servlet-mapping****>** Spring容器优先加载由ServletContextListener(对应applicationContext.xml)产生的父容器,而SpringMVC(对应mvc\_dispatcher\_servlet.xml)产生的是子容器。子容器Controller进行扫描装配时装配的@Service注解的实例是没有经过事务加强处理,即没有事务处理能力的Service,而父容器进行初始化的Service是保证事务的增强处理能力的。如果不在子容器中将Service exclude掉,此时得到的将是原样的无事务处理能力的Service。 ( update 2014.05.27 今天看见一种说法:key word =**双亲上下文。**不使用ContextLoaderListener监听器来加载spring的配置,改用DispatcherServlet来加载spring的配置,不要双亲上下文,只使用一个DispatcherServlet就不会出现上述问题。笔者这里未测过这个办法,因为我自己的业务需要一个extends ContextLoaderListener的selfListener,有兴趣的朋友可以自己测试下这个说法,并欢迎把测试的结果与我交流 :) ) 经过以上分析,故可以**优化上述配置**: 在 applicationContext.xml增加: **\[html\]** [view plain][] [copy][view plain] [![在CODE上查看代码片][CODE]][CODE_CODE] [![派生到我的代码片][ico_fork.svg]][ico_fork.svg 1] 1. **<****context:component-scan** base-package="com.will"**>** 2. **</****context:component-scan****>** 在 Spring mvc.dispatcher.xml增加: **\[html\]** [view plain][] [copy][view plain] [![在CODE上查看代码片][CODE]][CODE_CODE] [![派生到我的代码片][ico_fork.svg]][ico_fork.svg 1] 1. **<****context:component-scan** base-package="com.will" **>** 2. **<****context:exclude-filter** type="annotation" expression="org.springframework.stereotype.Service" **/>** 3. **</****context:component-scan****>** 经过如上配置,可以发现事务控制部分的日志如下: **\[html\]** [view plain][] [copy][view plain] [![在CODE上查看代码片][CODE]][CODE_CODE] [![派生到我的代码片][ico_fork.svg]][ico_fork.svg 1] 1. 2013-09-25 09:53:13,031 \[http-8080-2\] DEBUG \[org.springframework.transaction.annotation.AnnotationTransactionAttributeSource\] - Adding transactional method 'save' with attribute: PROPAGATION\_REQUIRED,ISOLATION\_DEFAULT; '' 2. 2013-09-25 09:53:13,037 \[http-8080-2\] DEBUG \[org.springframework.beans.factory.support.DefaultListableBeanFactory\] - Returning cached instance of singleton bean 'txManager' 3. 2013-09-25 09:53:13,050 \[http-8080-2\] DEBUG \[org.springframework.jdbc.datasource.DataSourceTransactionManager\] - Creating new transaction with name \[com.will.service.impl.PersonServiceImpl.save\]: PROPAGATION\_REQUIRED,ISOLATION\_DEFAULT; '' 4. 2013-09-25 09:53:13,313 \[http-8080-2\] DEBUG \[org.springframework.jdbc.datasource.DataSourceTransactionManager\] - Acquired Connection \[jdbc:mysql://localhost:3306/mvc?useUnicode=true&characterEncoding=UTF\-8, UserName=root@localhost, MySQL-AB JDBC Driver\] for JDBC transaction 5. 2013-09-25 09:53:13,323 \[http-8080-2\] DEBUG \[org.springframework.jdbc.datasource.DataSourceTransactionManager\] - Switching JDBC Connection \[jdbc:mysql://localhost:3306/mvc?useUnicode=true&characterEncoding=UTF\-8, UserName=root@localhost, MySQL-AB JDBC Driver\] to manual commit 6. 2013-09-25 09:53:13,327 \[http-8080-2\] DEBUG \[org.springframework.jdbc.core.JdbcTemplate\] - Executing prepared SQL update 7. 2013-09-25 09:53:13,328 \[http-8080-2\] DEBUG \[org.springframework.jdbc.core.JdbcTemplate\] - Executing prepared SQL statement \[delete from person where id=?\] 8. 2013-09-25 09:53:13,348 \[http-8080-2\] DEBUG \[org.springframework.jdbc.core.JdbcTemplate\] - SQL update affected 1 rows 9. 2013-09-25 09:53:13,363 \[http-8080-2\] DEBUG \[org.springframework.jdbc.datasource.DataSourceTransactionManager\] - Initiating transaction rollback 10. 2013-09-25 09:53:13,364 \[http-8080-2\] DEBUG \[org.springframework.jdbc.datasource.DataSourceTransactionManager\] - Rolling back JDBC transaction on Connection \[jdbc:mysql://localhost:3306/mvc?useUnicode=true&characterEncoding=UTF\-8, UserName=root@localhost, MySQL-AB JDBC Driver\] 11. 2013-09-25 09:53:13,377 \[http-8080-2\] DEBUG \[org.springframework.jdbc.datasource.DataSourceTransactionManager\] - Releasing JDBC Connection \[jdbc:mysql://localhost:3306/mvc?useUnicode=true&characterEncoding=UTF\-8, UserName=root@localhost, MySQL-AB JDBC Driver\] after transaction 12. 2013-09-25 09:53:13,378 \[http-8080-2\] DEBUG \[org.springframework.jdbc.datasource.DataSourceUtils\] - Returning JDBC Connection to DataSource 在2013-09-25 09:53:13,363处进行了rollback。 转载的转载,不知原处。感谢原作者分享! [view plain]: http://blog.csdn.net/will_awoke/article/details/12002705# [CODE]: https://code.csdn.net/assets/CODE_ico.png [CODE_CODE]: https://code.csdn.net/snippets/365899 [ico_fork.svg]: https://code.csdn.net/assets/ico_fork.svg [ico_fork.svg 1]: https://code.csdn.net/snippets/365899/fork
还没有评论,来说两句吧...