博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
“Transaction rolled back because it has been marked as rollback-only”
阅读量:5126 次
发布时间:2019-06-13

本文共 6614 字,大约阅读时间需要 22 分钟。

spring的声明事务提供了强大功能,让我们把业务关注和非业务关注的东西又分离开了。好东西的使用,总是需要有代价的。使用声明事务的时候,一 个不小心经常会碰到“Transaction rolled back because it has been marked as rollback-only”这个异常。有时候又常常会纳闷,"我已经try-catch了,为什么还这样呢?"

 

Xml代码  
  1. <!-- 0 placeHolder -->  
  2. <bean  class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">  
  3.     <property name="locations">  
  4.         <list>  
  5.             <value>files/pro.properties</value>  
  6.         </list>  
  7.     </property>  
  8. </bean>  
  9.   
  10. <!-- 1 dataSource -->  
  11. <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">  
  12.     <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>  
  13.     <property name="url" value="${jdbc.mysql.url}"></property>  
  14.     <property name="username" value="${jdbc.username}"></property>  
  15.     <property name="password" value="${jdbc.userpassword}"></property>  
  16. </bean>  
  17.   
  18. <!-- 2 jdbcTemplate -->  
  19. <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">  
  20.     <property name="dataSource" ref="dataSource"></property>  
  21. </bean>  
  22.   
  23. <!-- 3 BaseDao -->  
  24. <bean id="baseDao" class="transaction.dao.BaseDao" abstract="true">  
  25.     <property name="jdbcTemplate" ref="jdbcTemplate" />  
  26. </bean>  
  27.   
  28. <bean id="aDao" class="transaction.dao.Adao" parent="baseDao">  
  29. </bean>  
  30.   
  31. <bean id="bDao" class="transaction.dao.Bdao" parent="baseDao">   
  32. </bean>  
  33.   
  34. <!-- 4 service -->  
  35. <bean id="aBo" class="transaction.bo.AboImpl">  
  36.     <property name="aDao" ref="aDao" />  
  37.     <property name="bBo" ref="bBo" />  
  38. </bean>  
  39.   
  40. <bean id="bBo" class="transaction.bo.BboImpl">  
  41.     <property name="bDao" ref="bDao" />  
  42. </bean>  
  43.   
  44. <!-- 5 transaction -->  
  45. <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">  
  46.     <property name="dataSource" ref="dataSource" />  
  47. </bean>  
  48.   
  49. <bean id="transactionInterceptor1" class="org.springframework.transaction.interceptor.TransactionInterceptor" >  
  50.     <property name="transactionManager" ref="transactionManager"></property>  
  51.     <property name="transactionAttributes">  
  52.         <props>  
  53.             <prop key="*">PROPAGATION_REQUIRED</prop>  
  54.         </props>    
  55.     </property>  
  56. </bean>  
  57.   
  58. <bean id="autoProxy1"  class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">  
  59.     <property name="beanNames">  
  60.         <list>  
  61.             <value>*Bo</value>  
  62.         </list>  
  63.     </property>  
  64.     <property name="interceptorNames">  
  65.         <list>  
  66.         <!--   
  67.             <value>transactionInterceptor2</value> 
  68.             -->  
  69.             <value>transactionInterceptor1</value>    
  70.         </list>  
  71.     </property>  
  72. </bean>  

 这里的声明事务是作用于所有以Bo为后缀的bean的所有方法上,使用REQUIRED传播方式。

Java代码  
  1. public int insertA(A a)   
  2. {  
  3.     aDao.insertA(a);  
  4.       
  5.     B b = new B();  
  6.     b.setName("bbb");  
  7.     try  
  8.     {  
  9.         bBo.insertB(b);  
  10.     }  
  11.     catch(Exception e)  
  12.     {  
  13.         System.out.println("aaa");  
  14.     }  
  15.     return 0;  
  16. }  

 这里,insertA 开始一个事务,调用aDao.insertA(a)[一个简单的数据库操作],然后调用 bBo.insertB(b)[bo调dao,dao直接抛异常]。bBo的insertB方法,也要开始一个事务,但是这里的传播机制是 REQUIRED。OK,和insertA 的事务合二为一吧。因为bBo.insertB(b)会抛异常出来,这里try-catch下,希望aDao.insertA(a)的操作能够成功。

但是现实总是残酷的,这里会有一个大大的 “Transaction rolled back because it has been marked as rollback-only” ,结果你会发现aDao.insertA(a)的操作也没有成功。

 

try-catch不起作用的原因简单的说就是,try-catch的不是地方,你认为你的try-catch是最接近异常抛出点了,是第一个处理 的handler了。实际上,spring在更早一步就try-catch 住了,同时还设置了一些标志位,再把catch住的异常往外抛。这个时候才是我们的try-catch。而"Transaction rolled back because it has been marked as rollback-only"就是因为事务在提交的时候,发现标志位已经被设置了,不应该去提交了,然后吭哧吭哧的回滚调,再提示你已经被设置成 rollback-only了。

 

原因是既然如此,那么在不改变代码的情况下,依靠配置能否解决这个问题呢?使用PROPAGATION_REQUIRES_NEW吧。对于 bBo.insertB(b)开个新的事务,如果失败了就回滚调,不影响外面的insertA不就OK了。最简单的情况就是在 transactionInterceptor1前面,再加个拦截器transactionInterceptor2,该拦截器只针对insertB的事 务属性进行修改。

Xml代码  
  1. <bean id="autoProxy1"  class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">  
  2.     <property name="beanNames">  
  3.         <list>  
  4.             <value>*Bo</value>  
  5.         </list>  
  6.     </property>  
  7.     <property name="interceptorNames">  
  8.         <list>  
  9.            
  10.             <value>transactionInterceptor2</value>  
  11.               
  12.             <value>transactionInterceptor1</value>    
  13.         </list>  
  14.     </property>  
  15. </bean>  
  16.   
  17.   
  18. <bean id="transactionInterceptor2" class="org.springframework.transaction.interceptor.TransactionInterceptor" >  
  19.     <property name="transactionManager" ref="transactionManager"></property>  
  20.     <property name="transactionAttributes">  
  21.         <props>  
  22.             <prop key="insertB">PROPAGATION_REQUIRES_NEW</prop>  
  23.         </props>    
  24.     </property>  
  25. </bean>  

注意interceptorNames里面元素的位置。先要使用transactionInterceptor2,再使用 transactionInterceptor1.因为调用insertB的时候,transactionInterceptor2先开了一个新事务,而 后transactionInterceptor1融合进这个事务。如果这2个拦截器的顺序颠倒的话,那么还是会出现“Transaction rolled back because it has been marked as rollback-only”。因为,transactionInterceptor2生成事务回滚以后,还是会把ex抛给 transactionInterceptor1。这个时候,transactionInterceptor1的事务和insertA的事务是同一个。 transactionInterceptor1,把标志设置好,等到insertA真的结束的时候,因为异常被我们的try-catch捕获 了,spring就会发现需要提交的事务具有一个已经被标记号的rollback。所以就又抛出来了。

 

但是如果系统有很多遗留的因素导致你不敢盲目的修改配置文件的话(比如事务的poincut),那么我们就再加一个事务proxy就OK了。

Xml代码  
  1. <bean id="autoProxy2" class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">  
  2.         <property name="beanNames">  
  3.             <list>  
  4.                 <value>*Bo</value>  
  5.             </list>  
  6.         </property>  
  7.         <property name="interceptorNames">  
  8.             <list>  
  9.                 <value>transactionInterceptor2</value>  
  10.                 <!--   
  11.                 <value>transactionInterceptor1</value>   
  12.                 -->  
  13.             </list>  
  14.         </property>  
  15.     </bean>  
  16.       
  17.     <bean id="autoProxy1"  class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">  
  18.         <property name="beanNames">  
  19.             <list>  
  20.                 <value>*Bo</value>  
  21.             </list>  
  22.         </property>  
  23.         <property name="interceptorNames">  
  24.             <list>  
  25.             <value>transactionInterceptor1</value>    
  26.             <!--   
  27.             <value>transactionInterceptor2</value>       
  28.             -->        
  29.             </list>  
  30.         </property>  
  31.     </bean>  

如上的配置还是会带来悲剧的“Transaction rolled back because it has been marked as rollback-only”。

 

但是如果我们把 autoProxy2 放到 autoProxy1 或者给自动代理加上顺序的话。。。结果就是喜剧了。。

Xml代码  
  1. <bean id="autoProxy1"  class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">  
  2.         <property name="beanNames">  
  3.             <list>  
  4.                 <value>*Bo</value>  
  5.             </list>  
  6.         </property>  
  7.         <property name="interceptorNames">  
  8.             <list>  
  9.             <value>transactionInterceptor1</value>    
  10.             <!--   
  11.             <value>transactionInterceptor2</value>       
  12.             -->        
  13.             </list>  
  14.         </property>  
  15.     </bean>  
  16.       
  17.       
  18.     <bean id="autoProxy2" class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">  
  19.         <property name="beanNames">  
  20.             <list>  
  21.                 <value>*Bo</value>  
  22.             </list>  
  23.         </property>  
  24.         <property name="interceptorNames">  
  25.             <list>  
  26.                 <value>transactionInterceptor2</value>  
  27.                 <!--   
  28.                 <value>transactionInterceptor1</value>   
  29.                 -->  
  30.             </list>  
  31.         </property>  
  32.     </bean>  

 

 

造成这个原因是由使用了2个代理的顺序导致的。

在做自动代理的时候,spring会按照postBeanProcessor bean声明的顺序(如果没有设置顺序的话),来依次处理bean。如果autoProxy2 在 autoProxy1 之前,这样transactionInterceptor2 就会更加贴近insertB的调用,其效果就像

Xml代码  
  1. <bean id="autoProxy1"  class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">  
  2.         <property name="beanNames">  
  3.             <list>  
  4.                 <value>*Bo</value>  
  5.             </list>  
  6.         </property>  
  7.         <property name="interceptorNames">  
  8.             <list>  
  9.             <value>transactionInterceptor1</value>    
  10.            
  11.             <value>transactionInterceptor2</value>        
  12.                   
  13.             </list>  
  14.         </property>  
  15.     </bean>  

 的配置。

 

 

看来~~~ spring 还是要注意bean的顺序啊,哈哈哈。。。

转载于:https://www.cnblogs.com/langtianya/p/4962784.html

你可能感兴趣的文章
STM32F10x_RTC秒中断
查看>>
display:none和visiblity:hidden区别
查看>>
C#double转化成字符串 保留小数位数, 不以科学计数法的形式出现。
查看>>
牛的障碍Cow Steeplechase
查看>>
Zookeeper选举算法原理
查看>>
3月29日AM
查看>>
利用IP地址查询接口来查询IP归属地
查看>>
HTML元素定义 ID,Class,Style的优先级
查看>>
构造者模式
查看>>
http和https的区别
查看>>
Hbuild在线云ios打包失败,提示BuildConfigure Failed 31013 App Store 图标 未找到 解决方法...
查看>>
找到树中指定id的所有父节点
查看>>
今天新开通了博客
查看>>
AS3优化性能笔记二
查看>>
ElasticSearch(站内搜索)
查看>>
4----COM:a Generative Model for group recommendation(组推荐的一种生成模型)
查看>>
UVA 11137 - Ingenuous Cubrency
查看>>
js阻止事件冒泡的两种方法
查看>>
Java异常抛出
查看>>
[SQL Server 系] T-SQL数据库的创建与修改
查看>>