UnexpectedRollbackException: Transaction rolled back 關於失誤自動回滾的問題
完整的異常資訊:
org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only
字面意思:
出現了不可預知的回滾異常,因為事務已經被標誌位只能回滾,所以事務回滾了。
解釋:
以下邊程式碼舉例
class ServiceA{ //傳播屬性設定為PROPAGATION_REQUIRED void methodA(){ try{ serviceB.methodB(); }catch (Exception e){ } } } class ServiceB{ //傳播屬性設定為PROPAGATION_REQUIRED void methodB(){ }
因為methodB的傳播屬性設定為PROPAGATION_REQUIRED,PROPAGATION_REQUIRED的意思是,當前有事務,則使用當前事務,當前無事務則建立事務。由於methodA的傳播屬性也為PROPAGATION_REQUIRED,所以methodA會建立一個事務,然後methodB與methodA使用同一個事務,methodB出現異常後,將當前事務標誌位回滾,由於在methodA中做了trycatch處理,程式沒有終止而是繼續往下走,當事務commit時,check狀態,發現,需要事務回滾,所以才會出現不可預知的事務異常:因為事務被標誌位回滾,所以事務回滾。
一言以概之,methodA與methodB共用一個事務,methodB將事務標誌為回滾,methodA中commit這個事務,然後,出現事務已經被標誌回滾(methodB標誌的)的異常資訊。
修改:
情況1:methodA與methodB在邏輯上不應該屬於同一個事務,那麼將methodB的事務傳播屬性修改為PROPAGATION_REQUIRES_NEW,這樣,執行methodB時,會建立一個新的事務,不影響methodA中的事務。
情況2:業務A與業務B在業務邏輯上就應該屬於同一個事務,那麼將methodA中的try catch去掉
情況3:業務A與業務B在業務邏輯上就應該屬於同一個事務,但是methodB的失敗與否不能影響methodA的事務提交,那麼仍然在methodA中try catch methodB,並將methodB設定為PROPAGATION_NESTED,它的意思是,methodB是一個子事務,有一個savepoint,失敗時會回滾到savepoint,不影響methodA,如果成功則A、B一起提交,A與B都是一個事務,只是B是一個子事務
總結:
1、深入理解事務傳播的各個狀態含義,比如PROPAGATION_REQUIRED、PROPAGATION_SUPPORTS、PROPAGATION_MANDATORY、PROPAGATION_REQUIRES_NEW等
2、最初我以為是由於spring配置檔案中,methodA的事務設定為ready-only=true(只讀事務)的原因,經過查詢資料得知,只讀事務與事務傳播不衝突,是兩個機制。只讀事務,是一個特殊的事務,該事務內,只能是查詢語句,不能含有修改、更新語句,資料庫可能對只讀事務做優化;傳播屬性是母方法與子方法之間的關係表達。
3、注意,同一個類中,事務巢狀以最外層的方法為準,巢狀的事務失效;不同類中巢狀的事務才會生效;