先了解事務的7種傳播屬性:
PROPAGATION_REQUIRED -- 支持當前事務,如果當前沒有事務,就新建一個事務。這是最常見的選擇。 PROPAGATION_SUPPORTS -- 支持當前事務,如果當前沒有事務,就以非事務方式執行。 PROPAGATION_MANDATORY -- 支持當前事務,如果當前沒有事務,就拋出異常。 PROPAGATION_REQUIRES_NEW -- 新建事務,如果當前存在事務,把當前事務掛起。 PROPAGATION_NOT_SUPPORTED -- 以非事務方式執行操作,如果當前存在事務,就把當前事務掛起。 PROPAGATION_NEVER -- 以非事務方式執行,如果當前存在事務,則拋出異常。 PROPAGATION_NESTED -- 如果當前存在事務,則在嵌套事務內執行。如果當前沒有事務,則進行與PROPAGATION_REQUIRED類似的操作。 前六個策略類似於EJB CMT,第七個(PROPAGATION_NESTED)是Spring所提供的一個特殊變量。 它要求事務管理器或者使用JDBC 3.0 Savepoint API提供嵌套事務行為(如Spring的DataSourceTransactionManager)
問題產生場景:
1.在 DemoServiceA.java中有方法 demoMethodA().其中嵌套DemoServiceB.java中demoMethodB()。當demoMethodA事務發生rollback時, demoMethodB 事務也可以rollback或是commit。
如圖:
public class DemoServiceA { public void demoMethodA() { demoServiceB.demoMethodB();//Insert對象B 操作
} }
常見解決方案:
public class DemoServiceA { /** * 新建事務 * 事務屬性配置為 PROPAGATION_REQUIRED */ @Transactional(propagation=Propagation.REQUIRED) public void demoMethodA() { //操作... /** * 1.事務屬性配置為 PROPAGATION_REQUIRES_NEW ; * A. DemoServiceA 事務commit與rollback,與 DemoServiceB無任何關系,DemoServiceB 不屬於事務 DemoServiceA的子事務。 * PROPAGATION_REQUIRES_NEW 啟動一個新的, 不依賴於環境的 "內部" 事務. 這個事務將被完全 commited * 或 rolled back 而不依賴於外部事務, 它擁有自己的隔離范圍, 自己的鎖, 當內部事務開始執行時, * 外部事務將被掛起, 內務事務結束時, 外部事務將繼續執行. * B. 可以起到分支執行的效果。service方法雖然嵌套但是事務之間狀態相互無影響 * * * * 2.事務屬性配置為 PROPAGATION_NESTED; * PROPAGATION_NESTED 開始一個 "嵌套的" 事務, 它是已經存在事務的一個真正的子事務. * 潛套事務開始執行時, 它將取得一個 savepoint. 如果這個嵌套事務失敗, * 我們將回滾到此 savepoint. 潛套事務是外部事務的一部分, 只有外部事務結束后它才會被提交. * */ demoServiceB.demoMethodB();//Insert對象B 操作 //操作... } }
下面詳細介紹7種事務傳播屬性在示例中的作用:
1: REQUIRED 加入當前正要執行的事務不在另外一個事務里,那么就起一個新的事務 比如說,DemoServiceB.demoMethodB的事務級別定義為REQUIRED, 那么由於執行DemoServiceA.demoMethodA的時候, DemoServiceA.demoMethodA已經起了事務,這時調用DemoServiceB.demoMethodB,DemoServiceB.demoMethodB看到自己已經運行在DemoServiceA.demoMethodA 的事務內部,就不再起新的事務。而假如DemoServiceA.demoMethodA運行的時候發現自己沒有在事務中,他就會為自己分配一個事務。 這樣,在DemoServiceA.demoMethodA或者在DemoServiceB.demoMethodB內的任何地方出現異常,事務都會被回滾。即使DemoServiceB.demoMethodB的事務已經被 提交,但是DemoServiceA.demoMethodA在接下來fail要回滾,DemoServiceB.demoMethodB也要回滾 2: SUPPORTS 如果當前在事務中,即以事務的形式運行,如果當前不再一個事務中,那么就以非事務的形式運行 3: MANDATORY 必須在一個事務中運行。也就是說,他只能被一個父事務調用。否則,他就要拋出異常 4: REQUIRES_NEW 這個就比較繞口了。 比如我們設計DemoServiceA.demoMethodA的事務級別為REQUIRED,DemoServiceB.demoMethodB的事務級別為REQUIRES_NEW, 那么當執行到DemoServiceB.demoMethodB的時候,DemoServiceA.demoMethodA所在的事務就會掛起,DemoServiceB.demoMethodB會起一個新的事務,等待DemoServiceB.demoMethodB的事務完成以后, 他才繼續執行。他與REQUIRED 的事務區別在於事務的回滾程度了。因為DemoServiceB.demoMethodB是新起一個事務,那么就是存在 兩個不同的事務。如果DemoServiceB.demoMethodB已經提交,那么DemoServiceA.demoMethodA失敗回滾,DemoServiceB.demoMethodB是不會回滾的。如果DemoServiceB.demoMethodB失敗回滾, 如果他拋出的異常被DemoServiceA.demoMethodA捕獲,DemoServiceA.demoMethodA事務仍然可能提交。 5: NOT_SUPPORTED 當前不支持事務。比如DemoServiceA.demoMethodA的事務級別是REQUIRED ,而DemoServiceB.demoMethodB的事務級別是NOT_SUPPORTED , 那么當執行到DemoServiceB.demoMethodB時,DemoServiceA.demoMethodA的事務掛起,而他以非事務的狀態運行完,再繼續DemoServiceA.demoMethodA的事務。 6: NEVER 不能在事務中運行。假設DemoServiceA.demoMethodA的事務級別是REQUIRED, 而DemoServiceB.demoMethodB的事務級別是NEVER , 那么DemoServiceB.demoMethodB就要拋出異常了。 7: NESTED 理解Nested的關鍵是savepoint。他與REQUIRES_NEW的區別是,REQUIRES_NEW另起一個事務,將會與他的父事務相互獨立, 而Nested的事務和他的父事務是相依的,他的提交是要等和他的父事務一塊提交的。也就是說,如果父事務最后回滾,他也要回滾的。