前兩篇寫了將Spring AOP應用與入參校驗和攔截方法進行日誌打印。本篇繼續應用三AOP應用於事務管理,順便把事務相關技術點統一Review一遍。(同樣基於case)
一、事務管理回顧
基於用戶支付成功後,創建支付訂單和支付記錄(兩個表的insert操作)來回顧事務管理。一共涉及3個方法
1)創建pay支付記錄邏輯 2)創建order支付訂單邏輯 3)組合方法:調用方法1、2,同步創建支付記錄和支付訂單
下表case默認(表1)
1、保證事務的均採用Required傳播屬性。
2、執行方法2先,1後,因爲都在1中寫的業務異常測試
| 組合 | Method1 | Method2 | 執行結果 |
是否有事務 | 無 throw | 有 | 有 | 12均成功,後拋出異常 |
| 無 throw | 有(業務異常) | 有 | 2成功,1失敗 |
| 有 throw | 有(業務異常) | 有 | 12均失敗 |
| 有 throw | 有 | 有 | 12均失敗 |
1)分析:pay方法執行便立即提交commit,order同理。即便最後拋出異常,結果照樣正常入庫。
2)分析:同上,pay方法執行insert後立即執行commit請求,支付記錄入庫。而創建訂單邏輯報錯,創建失敗。
3)分析:儘管pay、order分別存在兩個事務,但當組合方法增加事務管理時,在執行pay事務後,並未執行commit請求,而是等着order執行成功後,一併commit。所以在order邏輯報錯後,事務回滾,表現就是pay和order均未創建。
4)分析:雖然12邏輯正常,組合方法最後設置拋出異常操作,故組合方法commit不執行,直接rollback
結論
1、當組合方法不添加事務管理時,無論單個方法是否包含事務,也無論報錯是原於單個方法中邏輯問題錯誤,還是兩個事務邏輯正常手動拋exeption的異常,背後都是先執行邏輯沒問題即刻提交commit,先執行先成功。組合方法無原子性。
2、當組合方法添加事務管理時,無論何種異常,都校驗到事務中所有操作均成功後,再提價組合事務commit操作,期間有一個失敗,則兩單個事務都入庫失敗。事務性無關單個方法事務,由組合方法事務保證原子性。
注意:
在case3時犯了一個很低級的錯誤,創建order內部異常時,由於代碼主動catch了異常,雖然是三個方法都設置了required事務,但由於catch了第一個方法異常,第二個方法仍會順序執行,最後一起commit組合方法的提交。所以表象看起來好像外層保證了原子性,但結果確實一張表失敗,一張表insert成功。
所以雖然是配置事務傳播屬性,但還跟代碼具體寫法相關。尤其是錯誤處理是否throw出來異常,還是catch住接下來的邏輯正常執行。這個得配合具體業務場景設計。
二、Spring事務傳播屬性
以下所有表格case默認:有事務表示case中均採用要驗證的傳播屬性,例如本case驗證Required,有則表示都設置成的傳播Required。非默認傳播屬性會特別標明。(表2)1、Required
| 組合 | Method1 | Method2 | 執行結果 |
是否有事務 | 無throw | 有 | 有 | 12均成功 |
| 無throw | 有 | 有(異常) | 1成功,2失敗 |
| 有throw | 無 | 無(異常) | 12均失敗 |
| 有throw | 有 | 有(異常) | 12均失敗 |
1)1、2required加入到無事務中,自啓事務保證commit操作
2)由於組合方法無事務,當2事務異常,1仍執行成功,表明required所起的新事務,僅保證自身方法原子性。而不保證整個組合方法原子性(否則因爲2異常失敗,1也該失敗)。注意外部無事務保證。
3)組合方法有事務保證,被調起的12無事務,因2的異常,導致組合方法整個事務回滾,12均失敗。
4)組合有事務保證,1的required會新起事務,保證1執行成功。2由於異常,失敗。但1並未回滾,驗證了2)中的說法。
其實“表1”跟“表2”的case基本重合,都驗證了兩點。
1、當外部操作有事務時,被調起的方法若傳播屬性是Required,則直接加入到外部事務,即便先執行被調起方法也不會commit,而是由外部事務統一控制commit操作。即在執行被調用方法後,若外部事務執行異常,被調起的方法隨外部事務一起回滾。簡言之:原操作有事務,加入到原事務中,統一由原事務管理,失敗一併回滾。
2、當外部方法無事務保證時,被調起required事務傳播屬性的事務方法,若原操作無事務,required事務方法將自啓事務,但注意!!新起的事務僅保證自身即被調起方法的事務執行,成功即提交,原操作執行失敗,被調起事務也不會回滾。即表2中第二行case,1成功2失敗。
2、Surport
被調用爲sruppot傳播屬性時,如果原操作有保證事務,即以事務的形式運行,如果原操作無保證事務,那麼就以非事務的形式運行
| 組合 | Method1 | Method2 | 執行結果 |
是否有事務 | 無throw | 有 | 無(異常) | 1成功,2失敗 |
| 有throw | 有 | 無(異常) | 12均失敗 |
| 有Surport throw | 有 | 無(異常) | 1成功,2失敗 |
1) 組合方法無事務保證,1以sup,則直接按無事務執行,1成功,2有異常失敗
2) 組合方法有事務保證(設置爲Required),1以sup,此時加入到組合方法事務中執行,當2異常時,1也回滾,兩個操作均失敗
3) 組合方法有事務保證(設置爲sup),因爲程序入口爲Test不保證事務,則組合方法便按無事務順序執行,1也是sup,同理。故1成功,2異常失敗。
3、MANDATORY
必須在一個事務中運行。也就是說,他只能被一個父事務調用。否則,他就要拋出異常
. | 組合 | Method1 | Method2 | 執行結果 |
是否有事務 | 無 | 有 | 無 | 12均成功 |
4、REQUIRES_NEW
| 組合 | Method1 | Method2 | 執行結果 |
是否有事務 | 有Re | 有Re_New | 無 | 12均成功 |
| 有Re | 有Re_New | 無(異常) | 1成功,2失敗 |
| 有Re | 有Re_New(異常) | 無 | 1失敗,2成功 |
1)組合方法事務級別爲REQUIRED,到1時,1會新起事務,組合方法事務掛起,等待1事務執行結果後,執行2,12均無異常,均成功。
2)此時若2異常,1在新事務中執行成功,由於2異常組合事務執行失敗回滾。而由於1在全新的事務中已commit,故1不會回滾,成功執行。
3)此時如果1異常,回滾。組合中catch住1的異常後,掛起的事務繼續執行2,2無異常,組合事務正常commit。
5、NESTED
NESTED與REQUIRES_NEW的區別是,REQUIRES_NEW另起一個事務,將會與他的父事務(組合方法中的事務)相互獨立,而Nested的事務和它的父事務是相依的,需要等父事務一塊提交的。如果父事務最後回滾,Nested也將回滾不執行commit操作。
6、NOT_SUPPORTED
被調用的方法採用該傳播屬性時表示我不支持事務。如遇源操作配置需保證事務處理,則原操作執行到調用方法時,事務會被掛起,等待被調用方法順序執行完畢後,再繼續執行原操作的事務。
| 組合 | Method1 | Method2 | 執行結果 |
是否有事務 | 有Re | 有(異常) | 無 | 1異常被catch後,執行2成功 |
| 有Re | 有(異常) | 無(異常) | 1、2均打印異常,2由異常失敗 |
1)組合方法已有事務保證,執行到1時,事務掛起,1中異常(使用trycatch打印日誌),catch完後,2正常執行
2)當2中也發生異常時,2也失敗回滾。
7、NEVER
被調用的方法不能在事務中運行。如遇源操作配置需保證事務處理,則原操作執行到調用方法時,被調用方法直接拋異常。
| 組合 | Method1 | Method2 | 執行結果 |
是否有事務 | 有Re throw | 有 | 無 | 12均成功 |
1) 在required事務裏裏,1也沒有拋異常,不起作用(未完待續)