摘要:引言對于追求數據強一致性的系統,事務扮演者十分重要的角色最近在項目中遇到一個事務失效的問題,在此分享給大家。情景回放問題分析初步分析這是事務獲取鎖超時導致的錯誤,奇怪的是拋出異常但是事務沒有回滾。唯一的解釋是事務失效了。
引言
對于追求數據強一致性的系統,事務扮演者十分重要的角色.最近在項目中遇到一個事務失效的問題,在此分享給大家。
情景回放### 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 加于接口方法, 無論下面調用的是private或public方法, 都有效 @Transactional 加于接口方法后, 被本類普通接口方法直接調用, 無效 @Transactional 加于接口方法后, 被本類普通接口方法通過接口調用, 有效 @Transactional 加于接口方法后, 被它類的接口方法調用, 有效 @Transactional 加于接口方法后, 被它類的私有方法調用后, 有效
Transactional是否生效, 僅取決于是否加載于接口方法, 并且是否通過接口方法調用(而不是本類調用)。
如果大家有更好的方法,歡迎加入討論!
參考博客:https://www.cnblogs.com/milto...
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/69240.html
最近開始復習css一直在踩坑,今天分享一個inline-block 關于inline-block可能很多人都不熟悉,布局這方面很多人用的都是flex或者浮動,flex很強大毋庸置疑的可是關于兼容性就不是很讓人滿意,而浮動雖然很兼容可是覺得清除浮動就很麻煩,于此我在一些大型網站,例如我們的segmentfault的首頁導航展示用的布局就是inline-block,覺得inline-block可以擼一...
最近開始復習css一直在踩坑,今天分享一個inline-block 關于inline-block可能很多人都不熟悉,布局這方面很多人用的都是flex或者浮動,flex很強大毋庸置疑的可是關于兼容性就不是很讓人滿意,而浮動雖然很兼容可是覺得清除浮動就很麻煩,于此我在一些大型網站,例如我們的segmentfault的首頁導航展示用的布局就是inline-block,覺得inline-block可以擼一...
摘要:在頁面與傳值中我們經常用到的方式,然后通過取的值今天在獲取值時怎么也取不到,后來發現對象有和屬性,而就在中,所以通過取到了正確的值。另外最好不要用駝峰命名如,這樣有時候也取不到值。 在頁面與js傳值中我們經常用到data-id=1的方式,然后通過e.target.dataset.id取id的值今天在獲取值時怎么也取不到,后來發現e對象有currentTarget和target屬性,而d...
摘要:提示原因是對象中的值應該為字符串正確的賦值打開方式應該是不得不說小程序中的坑真多,賦值類型還得是字符串 console提示:showModal:fail parameter error: parameter.content should be String instead of Undefined;原因是 Page({ data: { userInfo: {}, h...
摘要:在頁面與傳值中我們經常用到的方式,然后通過取的值今天在獲取值時怎么也取不到,后來發現對象有和屬性,而就在中,所以通過取到了正確的值。另外最好不要用駝峰命名如,這樣有時候也取不到值。 在頁面與js傳值中我們經常用到data-id=1的方式,然后通過e.target.dataset.id取id的值今天在獲取值時怎么也取不到,后來發現e對象有currentTarget和target屬性,而d...
閱讀 2413·2021-08-18 10:21
閱讀 2519·2019-08-30 13:45
閱讀 2155·2019-08-30 13:16
閱讀 2100·2019-08-30 12:52
閱讀 1363·2019-08-30 11:20
閱讀 2622·2019-08-29 13:47
閱讀 1622·2019-08-29 11:22
閱讀 2760·2019-08-26 12:11