大廠數據庫事務實踐-事務生效就能保證正確回滾? 反面教材

AOP實現事務:使用try?c/atch包裹@Transactional註解的方法,當方法出現異常並滿足一定條件時,在catch裏可設置事務回滾,沒有異常則直接提交事務。

“一定條件”包括:

  • 只有異常傳播出了標記了@Transactional註解的方法,事務才能回滾。在Spring的TransactionAspectSupport裏有個 invokeWithinTransaction方法,裏面就是處理事務的邏輯。可以看到,只有捕獲到異常才能進行後續事務處理:

  • 默認情況下,出現RuntimeException(非受檢異常)或Error,Spring纔會回滾事務。

打開Spring的DefaultTransactionAttribute

  • 受檢異常一般是業務異常或是類似另一種方法的返回值,出現這樣的異常可能業務還能完成,所以不會主動回滾
  • 而Error或RuntimeException代表非預期結果,應回滾


反面教材

註冊用戶:

  • createUserError1會拋RuntimeException,但方法內的catch所有異常:


  • createUserError2,註冊用戶同時會有一次otherTask文件讀,若讀文件失敗,希望用戶註冊的DB操作回滾。這裏雖無捕獲異常,但因otherTask拋受檢異常,createUserError2傳播出去的也是受檢異常,事務同樣不回滾:
  • readFile


createUserError1、2這倆方法的實現和調用,雖然避開了事務不生效的坑,但因異常處理不當,文件操作出現異常時依舊不回滾事務。

修復bug

以及如何通過日誌來驗證是否修復成功。針對這2種情況,對應的修復方法如下。

1 如果希望自己捕獲異常並處理,可手動設置讓當前事務處回滾態

@Transactional
public void createUserRight1(String name) {
    try {
        userRepository.save(new UserEntity(name));
        throw new RuntimeException("error");
    } catch (Exception ex) {
        log.error("create user failed", ex);
        TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
    }
}

查看日誌,事務確定回滾。

Transactional code has requested rollback:手動請求回滾。

2 在註解中聲明,期望遇到所有的Exception都回滾事務

以突破默認不回滾受檢異常的限制。

查看日誌,提示回滾:
[圖片上傳失敗...(image-cd575d-1605664603758)]

該案例有DB操作、IO操作,在IO操作問題時期望DB事務也回滾,以確保邏輯一致性。

小結

由於異常處理不正確,導致雖然事務生效,但出現異常時沒回滾。
Spring默認只對被@Transactional註解的方法出現RuntimeExceptionError時回滾,所以若方法捕獲了異常,就需要通過手寫代碼處理事務回滾。
若希望Spring針對其他異常也可回滾,可相應配置@Transactional註解的rollbackFornoRollbackFor屬性覆蓋Spring的默認配置。

有些業務可能包含多次DB操作,不一定希望將兩次操作作爲一個事務,這時就需仔細考慮事務傳播的配置。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章