Spring 踩坑之@Transactional 神奇失效

发布时间:2019-11-20 发布网站:脚本宝典
脚本宝典收集整理的这篇文章主要介绍了Spring 踩坑之@Transactional 神奇失效脚本宝典觉得挺不错的,现在分享给大家,也给大家做个参考。

引言

对于追求数据强一致性的系统,事务扮演者十分重要的角色.最近在项目中遇到一个事务失效的问题,在此分享给大家。

情景回放

### Cause: com.MySQL.jdbc.exceptions.jdbc4.MySQLTransactionRollbackException: Lock wait timeout exceeded; try restarting transaction ; SQL []; Lock waIT timeout exceeded; try restarting transaction; nested exception is com.mysql.jdbc.exceptions.jdbc4.MySQLTransactionRollbackException: Lock wait timeout exceeded; try restarting transaction     at org.sPRingframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator.doTranslate(SQLErrorCodeSQLExceptionTranslator.java:259)     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:446)     at com.sun.Proxy.$Proxy121.update(Unknown Source)     at org.mybatis.spring.SqlSessionTemplate.update(SqlSessionTemplate.java:294)     at org.apache.ibatis.binding.MapPErMethod.execute(MapperMethod.java:62)     at org.apache.ibatis.binding.MapperProxy.invoke(MapperProxy.java:59)

问题分析

初步分析这是事务获取锁超时导致的错误,奇怪的是抛出异常但是事务没有回滚。或许你们说MySQLTransactionRollbackException是检查性异常(@Transactional默认只捕获非检查性异常),但是项目添加了注解: @Transactional(rollbackFor = Exception.class)。唯一的解释是——事务失效了。

 ProductService.java /**********************************************************************/ public interface ProductService{     Integer getPrice(ProductInfo p);     Integer compute(ProductInfo p); } /**********************************************************************/   ProductServiceimpl.java /**********************************************************************/ @Service public class ProductServiceImpl implements ProductService{      public Integer getPrice(ProductInfo p){         ...         compute(p);         ...     }      @Transactional(rollbackFor = Exception.class)     public Integer compute(ProductInfo p){ //testService的普通方法         try{             ...         }catch(Exception e){              e.printStackTrace();              return -1;         }     } } /**********************************************************************/ 

初看这段代码,没啥毛病啊。噢,不对,compute 方法内部catch了异常,spring aop无法捕获异常。如果需要捕获异常,需要手动回滚,于是compute方法修改如下:

    @Transactional(rollbackFor = Exception.class)         public Integer compute(ProductInfo p){ //TestService的普通方法             try{                 ...             }catch(Exception e){                  e.printStackTrace();                  TransactionAspectSupport.currentTransactionstatus().setRollbackOnly();//手动回滚事务                  return 0;             }         }

继续运行,结果发现事务还是未生效。通过查询资料发现,service方法直接调用了本类的一个方法(没有通过接口调用),该方法上的事务将不会生效。

解决方案

想启用本类的普通方法的事务,通过接口来调用该方法即可生效。如果先在方法内部catch异常,需要添加TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();否则可以在外面捕获这个异常。下面是在方法内部捕获异常的示例:

 ProductService.java /**********************************************************************/ public interface ProductService{     Integer getPrice(ProductInfo p);     Integer compute(ProductInfo p); } /**********************************************************************/   ProductServiceImpl.java /**********************************************************************/ @Service public class ProductServiceImpl implements ProductService{     @Autowired     private ProductService productService;          public Integer getPrice(ProductInfo p){         productService.COMpute(p);     }      @Transactional(rollbackFor = Exception.class)     public Integer compute(ProductInfo p){          try{             ...         }catch(Exception e){              e.printStackTrace();              TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();              return 0;         }     } } /**********************************************************************/

总结

Spring Transactional一直是RD的事务神器,但是如果用不好,反会伤了自己。下面总结@Transactional经常遇到的几个场景:

@Transactional 加于private方法, 无效 @Transactional 加于未加入接口的public方法, 再通过普通接口方法调用, 无效 @Transactional 加于接口方法, 无论下面调用的是privatepublic方法, 都有效 @Transactional 加于接口方法后, 被本类普通接口方法直接调用, 无效 @Transactional 加于接口方法后, 被本类普通接口方法通过接口调用, 有效 @Transactional 加于接口方法后, 被它类的接口方法调用, 有效 @Transactional 加于接口方法后, 被它类的私有方法调用后, 有效

Transactional是否生效, 仅取决于是否加载于接口方法, 并且是否通过接口方法调用(而不是本类调用)。

如果大家有更好的方法,欢迎加入讨论!

参考博客:https://www.cnblogs.com/milto...

脚本宝典总结

以上是脚本宝典为你收集整理的Spring 踩坑之@Transactional 神奇失效全部内容,希望文章能够帮你解决Spring 踩坑之@Transactional 神奇失效所遇到的问题。

如果觉得脚本宝典网站内容还不错,欢迎将脚本宝典推荐好友。

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。