數據庫的事務在我們的項目中應用廣泛,最近在一個springboot項目中遇到了使用@Transactional註解後事務不回滾的問題,代碼如下:
後來查資料發現是因爲數據庫(MySQL)用的引擎是MyISAM,而MySQL的MyISAM引擎不支持回滾事務,如果需要自動回滾事務,需要將MySQL的引擎設置成InnoDB,並不是因爲代碼的問題造成的事務不回滾
在此總結造成事務不回滾可能的原因:
1、首先要看數據庫本身對應的庫、表所設置的引擎是什麼;MyIsam不支持事務,如果需要,則必須改爲InnnoDB
2、@Transactional所註解的方法是否爲public;如果應用在protected、private的方法上,也不會報錯,不過事務設置不會起作用
3、@Transactional所註解的方法所在的類,是否已經被註解@Service或@Component等標記
4、需要調用該方法且需要支持事務特性的調用方是在有@Transactional註解所在的類的外面;注意:類內部的其他方法調用這個註解了@Transactional的方法,事務是不會起作用的
注:service類標籤(一般不建議在接口上)上添加@Transactional,可以將整個類納入spring事務管理,在每個業務方法執行時都會開啓一個事務,不過這些事務採用相同的管理方式
5、@Transactional註解的位置:@Transactional註解必須和拋出異常的位置在一起;在Service中加入的事務註解,手動拋出異常時要在Service中拋出,才能看到效果;如果Controller中,調用兩個不同Service的方法並開啓了事務回滾,要想事務生效,則需要在Controller也加入@Transactional註解
6、在業務中拋出異常時,本應該被事務管理器捕獲的異常被手動catch處理了,或者事務結果未滿足具體業務需求的,如果需要手動catch異常做業務處理,需要在catch裏手動回滾事務:
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
或者在catch中主動拋出異常:
throw new RuntimeException();
7、註解爲事務範圍的方法中,事務的回滾僅僅對於unchecked的異常有效,對於checked異常無效,也就是說事務回滾僅僅發生在出現RuntimeException或Error的時候
注:什麼是checked異常,什麼是unchecked異常?
java裏面將派生於Error或者RuntimeException(比如空指針、4/0)的異常稱爲unchecked異常,其他繼承自java.lang.Exception得異常統稱爲Checked Exception,如IOException、TimeoutException等
通俗點就是代碼中出現的空指針等異常會被回滾,而文件讀寫、網絡出問題,spring就沒法回滾了
如果checked異常也需要進行事務回滾的話,可以在@Transactional註解中加上rollbackFor=Exception.class屬性,如下:
@Transactional(rollbackFor=Exception.class)