事務的嵌套概念
所謂事務的嵌套就是兩個事務方法之間相互調用。spring事務開啟 ,或者是基于接口的或者是基于類的代理被創建(注意一定要是代理,不能手動new 一個對象,并且此類(有無接口都行)一定要被代理——spring中的bean只要納入了IOC管理都是被代理的)。所以在同一個類中一個方法調用另一個方法有事務的方法,事務是不會起作用的。
Spring默認情況下會對運行期例外(RunTimeException),即uncheck異常,進行事務回滾。
如果遇到checked異常就不回滾。
如何改變默認規則:
? ? 1 讓checked例外也回滾:
????@Transactional(rollbackFor=Exception.class)
? ? 2?讓unchecked例外不回滾:
????@Transactional(notRollbackFor=RunTimeException.class)
? ? 3?不需要事務管理的(只查詢的)的方法:
????@Transactional(propagation=Propagation.NOT_SUPPORTED)
上面三種方式也可在xml配置
spring事務傳播屬性
?在 spring的?TransactionDefinition接口中一共定義了六種事務傳播屬性:
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)
舉例淺析Spring嵌套事務
ServiceA#methodA(我們稱之為外部事務),ServiceB#methodB(我們稱之為外部事務)
假如當前正要執行的事務不在另外一個事務里,那么就起一個新的事務
比如說,ServiceB.methodB的事務級別定義為PROPAGATION_REQUIRED, 那么由于執行ServiceA.methodA的時候
1、如果ServiceA.methodA已經起了事務,這時調用ServiceB.methodB,ServiceB.methodB看到自己已經運行在ServiceA.methodA的事務內部,就不再起新的事務。這時只有外部事務并且他們是共用的,所以這時ServiceA.methodA或者ServiceB.methodB無論哪個發生異常methodA和methodB作為一個整體都將一起回滾。
2、如果ServiceA.methodA沒有事務,ServiceB.methodB就會為自己分配一個事務。這樣,在ServiceA.methodA中是沒有事務控制的。只是在ServiceB.methodB內的任何地方出現異常,ServiceB.methodB將會被回滾,不會引起ServiceA.methodA的回滾
如果當前在事務中,即以事務的形式運行,如果當前不再一個事務中,那么就以非事務的形式運行
必須在一個事務中運行。也就是說,他只能被一個父事務調用。否則,他就要拋出異常
啟動一個新的, 不依賴于環境的 "內部" 事務. 這個事務將被完全 commited 或 rolled back 而不依賴于外部事務, 它擁有自己的隔離范圍, 自己的鎖, 等等. 當內部事務開始執行時, 外部事務將被掛起, 內務事務結束時, 外部事務將繼續執行.?
?比如我們設計ServiceA.methodA的事務級別為PROPAGATION_REQUIRED,ServiceB.methodB的事務級別為PROPAGATION_REQUIRES_NEW,那么當執行到ServiceB.methodB的時候,ServiceA.methodA所在的事務就會掛起,ServiceB.methodB會起一個新的事務,等待ServiceB.methodB的事務完成以后,他才繼續執行。他與PROPAGATION_REQUIRED 的事務區別在于事務的回滾程度了。因為ServiceB.methodB是新起一個事務,那么就是存在兩個不同的事務。
1、如果ServiceB.methodB已經提交,那么ServiceA.methodA失敗回滾,ServiceB.methodB是不會回滾的。
2、如果ServiceB.methodB失敗回滾,如果他拋出的異常被ServiceA.methodA的try..catch捕獲并處理,ServiceA.methodA事務仍然可能提交;如果他拋出的異常未被ServiceA.methodA捕獲處理,ServiceA.methodA事務將回滾。
使用場景:
不管業務邏輯的service是否有異常,Log Service都應該能夠記錄成功,所以Log Service的傳播屬性可以配為此屬性。最下面將會貼出配置代碼。
當前不支持事務。比如ServiceA.methodA的事務級別是PROPAGATION_REQUIRED ,而ServiceB.methodB的事務級別是PROPAGATION_NOT_SUPPORTED ,那么當執行到ServiceB.methodB時,ServiceA.methodA的事務掛起,而他以非事務的狀態運行完,再繼續ServiceA.methodA的事務。
不能在事務中運行。假設ServiceA.methodA的事務級別是PROPAGATION_REQUIRED, 而ServiceB.methodB的事務級別是PROPAGATION_NEVER ,那么ServiceB.methodB就要拋出異常了。
開始一個 "嵌套的" 事務, ?它是已經存在事務的一個真正的子事務. 潛套事務開始執行時, ?它將取得一個 savepoint. 如果這個嵌套事務失敗, 我們將回滾到此 savepoint. 潛套事務是外部事務的一部分, 只有外部事務結束后它才會被提交.?
比如我們設計ServiceA.methodA的事務級別為PROPAGATION_REQUIRED,ServiceB.methodB的事務級別為PROPAGATION_NESTED,那么當執行到ServiceB.methodB的時候,ServiceA.methodA所在的事務就會掛起,ServiceB.methodB會起一個新的子事務并設置savepoint,等待ServiceB.methodB的事務完成以后,他才繼續執行。。因為ServiceB.methodB是外部事務的子事務,那么
1、如果ServiceB.methodB已經提交,那么ServiceA.methodA失敗回滾,ServiceB.methodB也將回滾。
2、如果ServiceB.methodB失敗回滾,如果他拋出的異常被ServiceA.methodA的try..catch捕獲并處理,ServiceA.methodA事務仍然可能提交;如果他拋出的異常未被ServiceA.methodA捕獲處理,ServiceA.methodA事務將回滾。
理解Nested的關鍵是savepoint。他與PROPAGATION_REQUIRES_NEW的區別是:
PROPAGATION_REQUIRES_NEW 完全是一個新的事務,它與外部事務相互獨立; 而 PROPAGATION_NESTED 則是外部事務的子事務, 如果外部事務 commit, 嵌套事務也會被 commit, 這個規則同樣適用于 roll back.
在 spring 中使用 PROPAGATION_NESTED的前提:
1. 我們要設置 transactionManager 的 nestedTransactionAllowed 屬性為 true, 注意, 此屬性默認為 false!!!?
2. java.sql.Savepoint 必須存在, 即 jdk 版本要 1.4+?
3. Connection.getMetaData().supportsSavepoints() 必須為 true, 即 jdbc drive 必須支持 JDBC 3.0?
確保以上條件都滿足后, 你就可以嘗試使用 PROPAGATION_NESTED 了.