照抄自:https://www.jianshu.com/p/c6d4095f5833
spring 事務傳播行為之嵌套事務NESTED細節
經過我之前的實踐,可以看出 NESTED事務申明在調用者上會新建一個獨立事務。申明在被調用者上,若調用者存在事務則加入調用者事務。調用者不存在事務則新建一個獨立事務。
這個功能好像和spring默認的事務傳播行為REQUIRED一樣的?
不,它的功能可是比REQUIRED要強大!
我來通過實驗證明NESTED和REQUIRED的區別
這個例子是基於 https://www.jianshu.com/p/bc3cbacf9e70 這個文章的代碼
首先,InsertUsers和InsertCuser方法上都申明了REQUIRED,讓他們屬於同一個事務。將引發異常的語句
int i = 1/0;
放到 InsertCuser方法里
注意
為什么要加try/catch包裹cuserService.InsertCuser(cuser);語句?
為了杜絕InsertCuser中拋出的異常影響InsertUsers方法的實驗結果
try { cuserService.InsertCuser(cuser); } catch (Exception e) { e.printStackTrace(); }
程序運行,結果是InsertCuser中出現異常,導致事務回滾、users表和cuser表均無數據插入。由於兩個方法被納入同一個事務,因此兩者都會回滾。即使在cuserService.InsertCuser(cuser);上使用try/catch捕獲並不拋出異常也沒用(此方法能保證調用者方法中的獨立事務不受被調用者拋出的異常影響而回滾)
我們再來看,將上面環境的InsertCuser方法傳播行為改成NESTED
再次運行,可以看到日志的打印情況。兩方法的事務的id一致,說明的確是相同事務

users表中插入了數據說明InsertUsers方法提交成功,cuser表中沒有數據說明InsertCuser方法回滾

那么現在就有一個問題了,既然兩個方法使用同一個事務,為什么沒有一起回滾?
這就是NESTED嵌套事務的奧秘之處-----它能讓事務部分回滾
我在網上找到了一句話:
NESTED申明在被調用方法上,若調用者方法有開啟事務。此時NESTED會開始一個 "嵌套的" 事務, 它是已經存在事務的一個真正的子事務。 潛套事務開始執行時, 它將取得一個
savepoint
。 如果這個嵌套事務失敗, 我們將回滾到此savepoint
。 潛套事務是外部事務的一部分, 只有外部事務結束后它才會被提交。
這段話中提到的 savepoint
其實是mysql的innodb引擎的特性,為了去了解它我在mysql客戶端對它進行了簡單使用,可以看看這篇文章https://www.jianshu.com/p/c93c1730e5dc 。 總之它就是一個保存點,生成一個保存點就是生成一個數據鏡像。然后無論經過了什么sql操作,只要使用回滾至此保存點的命令即可恢復至創建保存點的數據狀態。
那么上面代碼的演示結果也就說的通了。即使InsertUsers和InsertCuser方法屬於同一個事務,被NESTED嵌套事務申明的InsertCuser方法出現異常也沒導致REQUIRED申明的InsertUsers的全部回滾
,只是部分回滾
到了調用InsertCuser方法之前。因為在調用InsertCuser方法時會自動生成一個savepoint
InsertUsers方法里出現異常會導致InsertCuser方法嵌套事務回滾嗎?
將出現異常的代碼行放到這里,結果都回滾了,畢竟是同一個事務

總結下NESTED的回滾特性
- 主事務和嵌套事務屬於同一個事務
- 嵌套事務出錯回滾不會影響到主事務
- 主事務回滾會將嵌套事務一起回滾了
進一步證明NESTED嵌套事務的savepoint機制
可以通過閱讀spring源碼的方式來驗證NESTED是不是使用savepoint機制來實現的,我現在的水平還不足以去閱讀源碼。但是我會很快就有這個能力的,我相信! 不過有簡友已經分析過了,可以去看看。寫的很好~
https://www.jianshu.com/p/2f79ee33c8ad