對於追求數據強一致性的系統,事務扮演者十分重要的角色.最近在項目中遇到一個事務失效的問題,在此分享給你們。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