对于追求数据强一致性的系统,事务扮演者十分重要的角色.最近在项目中遇到一个事务失效的问题,在此分享给你们。html
### 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)
。惟一的解释是——事务失效了。java
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方法修改以下:mysql
@Transactional(rollbackFor = Exception.class) public Integer compute(ProductInfo p){ //TestService的普通方法 try{ ... }catch(Exception e){ e.printStackTrace(); TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();//手动回滚事务 return 0; } }
继续运行,结果发现事务仍是未生效。经过查询资料发现,service方法直接调用了本类的一个方法(没有经过接口调用),该方法上的事务将不会生效。spring
想启用本类的普通方法的事务,经过接口来调用该方法便可生效。若是先在方法内部catch异常,须要添加TransactionAspectSupport.currentTransactionStatus().setRollbackOnly()
;不然能够在外面捕获这个异常。下面是在方法内部捕获异常的示例:sql
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常常遇到的几个场景:apache
@Transactional 加于private方法, 无效 @Transactional 加于未加入接口的public方法, 再经过普通接口方法调用, 无效 @Transactional 加于接口方法, 不管下面调用的是private或public方法, 都有效 @Transactional 加于接口方法后, 被本类普通接口方法直接调用, 无效 @Transactional 加于接口方法后, 被本类普通接口方法经过接口调用, 有效 @Transactional 加于接口方法后, 被它类的接口方法调用, 有效 @Transactional 加于接口方法后, 被它类的私有方法调用后, 有效
Transactional是否生效, 仅取决因而否加载于接口方法, 而且是否经过接口方法调用(而不是本类调用)。mybatis
若是你们有更好的方法,欢迎加入讨论!app
参考博客:https://www.cnblogs.com/milto...rest