太難了!面試官讓我結合案例講講自己對Spring事務傳播行爲的理解

前言

Spring 在 TransactionDefinition 接口中規定了 7 種類型的事務傳播行爲。事務傳播行爲是 Spring 框架獨有的事務增強特性,他不屬於的事務實際提供方數據庫行爲。這是 Spring 爲我們提供的強大的工具箱,使用事務傳播行可以爲我們的開發工作提供許多便利。但是人們對他的誤解也頗多,你一定也聽過“service 方法事務最好不要嵌套”的傳言。要想正確的使用工具首先需要了解工具。本文對七種事務傳播行爲做詳細介紹,內容主要代碼示例的方式呈現。

基礎概念

1. 什麼是事務傳播行爲?

事務傳播行爲用來描述由某一個事務傳播行爲修飾的方法被嵌套進另一個方法的時事務如何傳播。

用僞代碼說明:

太難了!面試官讓我結合案例講講自己對Spring事務傳播行爲的理解


代碼中methodA()方法嵌套調用了methodB()方法,methodB()的事務傳播行爲由@Transaction(Propagation=XXX)設置決定。這裏需要注意的是methodA()並沒有開啓事務,某一個事務傳播行爲修飾的方法並不是必須要在開啓事務的外圍方法中調用。

2. Spring 中七種事務傳播行爲

太難了!面試官讓我結合案例講講自己對Spring事務傳播行爲的理解


定義非常簡單,也很好理解,下面我們就進入代碼測試部分,驗證我們的理解是否正確。

代碼驗證

文中代碼以傳統三層結構中兩層呈現,即 Service 和 Dao 層,由 Spring 負責依賴注入和註解式事務管理,DAO 層由 Mybatis 實現,你也可以使用任何喜歡的方式,例如,Hibernate,JPA,JDBCTemplate 等。數據庫使用的是 MySQL 數據庫,你也可以使用任何支持事務的數據庫,並不會影響驗證結果。

首先我們在數據庫中創建兩張表:

user1

太難了!面試官讓我結合案例講講自己對Spring事務傳播行爲的理解


user2

太難了!面試官讓我結合案例講講自己對Spring事務傳播行爲的理解


然後編寫相應的 Bean 和 DAO 層代碼:

User1

太難了!面試官讓我結合案例講講自己對Spring事務傳播行爲的理解


User2

太難了!面試官讓我結合案例講講自己對Spring事務傳播行爲的理解


User1Mapper

太難了!面試官讓我結合案例講講自己對Spring事務傳播行爲的理解


User2Mapper

太難了!面試官讓我結合案例講講自己對Spring事務傳播行爲的理解


最後也是具體驗證的代碼由 service 層實現,下面我們分情況列舉。

1.PROPAGATION_REQUIRED

我們爲 User1Service 和 User2Service 相應方法加上Propagation.REQUIRED屬性。

User1Service 方法:

太難了!面試官讓我結合案例講講自己對Spring事務傳播行爲的理解


User2Service 方法:

太難了!面試官讓我結合案例講講自己對Spring事務傳播行爲的理解


1.1 場景一

此場景外圍方法沒有開啓事務。

驗證方法 1:

太難了!面試官讓我結合案例講講自己對Spring事務傳播行爲的理解


驗證方法 2:

太難了!面試官讓我結合案例講講自己對Spring事務傳播行爲的理解


分別執行驗證方法,結果:

太難了!面試官讓我結合案例講講自己對Spring事務傳播行爲的理解


結論:通過這兩個方法我們證明了在外圍方法未開啓事務的情況下Propagation.REQUIRED修飾的內部方法會新開啓自己的事務,且開啓的事務相互獨立,互不干擾。

1.2 場景二

外圍方法開啓事務,這個是使用率比較高的場景。

驗證方法 1:

太難了!面試官讓我結合案例講講自己對Spring事務傳播行爲的理解


驗證方法 2:

太難了!面試官讓我結合案例講講自己對Spring事務傳播行爲的理解


驗證方法 3:

太難了!面試官讓我結合案例講講自己對Spring事務傳播行爲的理解


分別執行驗證方法,結果:

太難了!面試官讓我結合案例講講自己對Spring事務傳播行爲的理解


結論:以上試驗結果我們證明在外圍方法開啓事務的情況下Propagation.REQUIRED修飾的內部方法會加入到外圍方法的事務中,所有Propagation.REQUIRED修飾的內部方法和外圍方法均屬於同一事務,只要一個方法回滾,整個事務均回滾。

2.PROPAGATION_REQUIRES_NEW

我們爲 User1Service 和 User2Service 相應方法加上Propagation.REQUIRES_NEW屬性。User1Service 方法:

太難了!面試官讓我結合案例講講自己對Spring事務傳播行爲的理解


User2Service 方法:

太難了!面試官讓我結合案例講講自己對Spring事務傳播行爲的理解


2.1 場景一

外圍方法沒有開啓事務。

驗證方法 1:

太難了!面試官讓我結合案例講講自己對Spring事務傳播行爲的理解


驗證方法 2:

太難了!面試官讓我結合案例講講自己對Spring事務傳播行爲的理解


分別執行驗證方法,結果:

太難了!面試官讓我結合案例講講自己對Spring事務傳播行爲的理解


結論:通過這兩個方法我們證明了在外圍方法未開啓事務的情況下Propagation.REQUIRES_NEW修飾的內部方法會新開啓自己的事務,且開啓的事務相互獨立,互不干擾。

2.2 場景二

外圍方法開啓事務。

驗證方法 1:

太難了!面試官讓我結合案例講講自己對Spring事務傳播行爲的理解


驗證方法 2:

太難了!面試官讓我結合案例講講自己對Spring事務傳播行爲的理解


驗證方法 3:

太難了!面試官讓我結合案例講講自己對Spring事務傳播行爲的理解


分別執行驗證方法,結果:

太難了!面試官讓我結合案例講講自己對Spring事務傳播行爲的理解


結論:在外圍方法開啓事務的情況下Propagation.REQUIRES_NEW修飾的內部方法依然會單獨開啓獨立事務,且與外部方法事務也獨立,內部方法之間、內部方法和外部方法事務均相互獨立,互不干擾。

3.PROPAGATION_NESTED

我們爲 User1Service 和 User2Service 相應方法加上Propagation.NESTED屬性。User1Service 方法:

太難了!面試官讓我結合案例講講自己對Spring事務傳播行爲的理解


User2Service 方法:

太難了!面試官讓我結合案例講講自己對Spring事務傳播行爲的理解


3.1 場景一

此場景外圍方法沒有開啓事務。

驗證方法 1:

太難了!面試官讓我結合案例講講自己對Spring事務傳播行爲的理解


驗證方法 2:

太難了!面試官讓我結合案例講講自己對Spring事務傳播行爲的理解


分別執行驗證方法,結果:

太難了!面試官讓我結合案例講講自己對Spring事務傳播行爲的理解


結論:通過這兩個方法我們證明了在外圍方法未開啓事務的情況下Propagation.NESTED和Propagation.REQUIRED作用相同,修飾的內部方法都會新開啓自己的事務,且開啓的事務相互獨立,互不干擾。

3.2 場景二

外圍方法開啓事務。

驗證方法 1:

太難了!面試官讓我結合案例講講自己對Spring事務傳播行爲的理解


驗證方法 2:

太難了!面試官讓我結合案例講講自己對Spring事務傳播行爲的理解


驗證方法 3:

太難了!面試官讓我結合案例講講自己對Spring事務傳播行爲的理解


分別執行驗證方法,結果:

太難了!面試官讓我結合案例講講自己對Spring事務傳播行爲的理解


結論:以上試驗結果我們證明在外圍方法開啓事務的情況下Propagation.NESTED修飾的內部方法屬於外部事務的子事務,外圍主事務回滾,子事務一定回滾,而內部子事務可以單獨回滾而不影響外圍主事務和其他子事務

4. REQUIRED,REQUIRES_NEW,NESTED 異同

由“1.2 場景二”和“3.2 場景二”對比,我們可知:NESTED 和 REQUIRED 修飾的內部方法都屬於外圍方法事務,如果外圍方法拋出異常,這兩種方法的事務都會被回滾。但是 REQUIRED 是加入外圍方法事務,所以和外圍事務同屬於一個事務,一旦 REQUIRED 事務拋出異常被回滾,外圍方法事務也將被回滾。而 NESTED 是外圍方法的子事務,有單獨的保存點,所以 NESTED 方法拋出異常被回滾,不會影響到外圍方法的事務。

由“2.2 場景二”和“3.2 場景二”對比,我們可知:NESTED 和 REQUIRES_NEW 都可以做到內部方法事務回滾而不影響外圍方法事務。但是因爲 NESTED 是嵌套事務,所以外圍方法回滾之後,作爲外圍方法事務的子事務也會被回滾。而 REQUIRES_NEW 是通過開啓新的事務實現的,內部事務和外圍事務是兩個事務,外圍事務回滾不會影響內部事務。

5. 其他事務傳播行爲

鑑於文章篇幅問題,其他事務傳播行爲的測試就不在此一一描述了,感興趣的讀者可以去源碼中自己尋找相應測試代碼和結果解釋。

模擬用例

介紹了這麼多事務傳播行爲,我們在實際工作中如何應用呢?下面我來舉一個示例:

假設我們有一個註冊的方法,方法中調用添加積分的方法,如果我們希望添加積分不會影響註冊流程(即添加積分執行失敗回滾不能使註冊方法也回滾),我們會這樣寫:

太難了!面試官讓我結合案例講講自己對Spring事務傳播行爲的理解


我們還規定註冊失敗要影響addPoint()方法(註冊方法回滾添加積分方法也需要回滾),那麼addPoint()方法就需要這樣實現:

太難了!面試官讓我結合案例講講自己對Spring事務傳播行爲的理解


我們注意到了在addPoint()中還調用了addRecord()方法,這個方法用來記錄日誌。他的實現如下:

太難了!面試官讓我結合案例講講自己對Spring事務傳播行爲的理解


我們注意到addRecord()方法中propagation = Propagation.NOT_SUPPORTED,因爲對於日誌無所謂精確,可以多一條也可以少一條,所以addRecord()方法本身和外圍addPoint()方法拋出異常都不會使addRecord()方法回滾,並且addRecord()方法拋出異常也不會影響外圍addPoint()方法的執行。

通過這個例子相信大家對事務傳播行爲的使用有了更加直觀的認識,通過各種屬性的組合確實能讓我們的業務實現更加靈活多樣。

結論

通過上面的介紹,相信大家對 Spring 事務傳播行爲有了更加深入的理解,希望大家日常開發工作有所幫助。


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