Spring MVC @Transactional注解方式事务无法回滚的解决办法

前文提到,最新换了框架,新项目用SpringMVC + spring JdbcTemplate。搭框架时,发现了一个事务无法正常回滚的问题,记录如下:

首先展示问题:

Spring applicationContext.xml配置:

[html]  view plain copy 在CODE上查看代码片 派生到我的代码片
  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 在CODE上查看代码片 派生到我的代码片
  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 在CODE上查看代码片 派生到我的代码片
  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 在CODE上查看代码片 派生到我的代码片
  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 在CODE上查看代码片 派生到我的代码片
  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 在CODE上查看代码片 派生到我的代码片
  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 在CODE上查看代码片 派生到我的代码片
  1. <context:component-scan base-package=“com.will”>   
  2. </context:component-scan>  

在 Spring mvc.dispatcher.xml增加:

[html]  view plain copy 在CODE上查看代码片 派生到我的代码片
  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 在CODE上查看代码片 派生到我的代码片
  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。


PS:习惯了Structs,对事务处理有点思维定式,这次花费不少时间来解决这个问题。颇为尴尬!

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Spring MVC 是一种基于Spring框架的Web开发框架,它是基于模型-视图-控制器(MVC)设计模式来组织和管理Web应用程序的开发的。数据库事务是保证数据操作的一致性和可靠性的重要机制。 在Spring MVC中,数据库事务的实现主要依赖于Spring框架内置的事务管理器。Spring框架为我们提供了多种事务管理器的实现方式,例如基于JDBC的DataSourceTransactionManager和基于JPA的JpaTransactionManager等。这些事务管理器实现了Spring的PlatformTransactionManager接口,它负责管理和控制事务的生命周期。 Spring MVC中使用注解@Transactional来标注需要参与事务管理的方法或类。被@Transactional标注的方法或类,会在运行时被Spring框架内置的AOP机制拦截,以实现事务的控制。当方法被调用时,Spring框架首先会检查当前线程是否已经有一个事务实例存在,如果不存在则创建一个新的事务,如果存在则加入已存在的事务中。 一旦事务创建成功,Spring框架会开始执行方法体中的业务逻辑操作。如果方法执行成功,事务将会继续提交,操作的结果将会永久保存到数据库中。如果方法执行失败或发生异常,Spring框架会回滚事务,将操作的结果恢复到之前的状态。这样可以确保在出现异常情况时,数据库的数据不会被污染或损坏。 事务的提交和回滚是由Spring框架的事务管理器来完成的。事务管理器负责管理事务的开始、提交和回滚等操作,并与底层的数据库连接进行交互。Spring框架还提供了各种配置选项,可以通过配置文件或注解来灵活地控制事务的传播行为、隔离级别、超时设置等。这些配置选项可以根据实际业务需求进行调整,以提供更好的性能和可靠性。 总而言之,Spring MVC中的数据库事务的实现原理是依赖于Spring框架提供的事务管理器和AOP机制。通过使用@Transactional注解,我们可以简单地将需要参与事务管理的方法或类进行标注,使其具备事务性的功能。这样可以实现对数据库操作的一致性和可靠性保证。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值