









在我所見過的誤解中, 最常見的是下面這種:









這種想法可能害了不少人, 認(rèn)為 Service 之間應(yīng)該避免互相調(diào)用, 其實(shí)根本不用擔(dān)心這點(diǎn),PROPAGATION_REQUIRED 已經(jīng)說得很明白,
如果當(dāng)前線程中已經(jīng)存在事務(wù), 方法調(diào)用會(huì)加入此事務(wù), 果當(dāng)前沒有事務(wù),就新建一個(gè)事務(wù), 所以 ServiceB#methodB() 的事務(wù)只要遵循最普通的規(guī)則配置為 PROPAGATION_REQUIRED 即可, 如果 ServiceB#methodB (我們稱之為內(nèi)部事務(wù), 為下文打下基礎(chǔ)) 拋了異常, 那么 ServiceA#methodA(我們稱之為外部事務(wù)) 如果沒有特殊配置此異常時(shí)事務(wù)提交 (即 +MyCheckedException的用法), 那么整個(gè)事務(wù)是一定要 rollback 的, 什么 Service 只能調(diào) Dao 之類的言論純屬無稽之談, spring 只負(fù)責(zé)配置了事務(wù)屬性方法的攔截, 它怎么知道你這個(gè)方法是在 Service 還是 Dao 里 ?
最容易弄混淆的其實(shí)是 PROPAGATION_REQUIRES_NEW 和 PROPAGATION_NESTED, 那么這兩種方式又有何區(qū)別呢? 我簡單的翻譯一下 Juergen Hoeller 的話 :
PROPAGATION_REQUIRES_NEW 啟動(dòng)一個(gè)新的, 不依賴于環(huán)境的 "內(nèi)部" 事務(wù). 這個(gè)事務(wù)將被完全 commited 或 rolled back 而不依賴于外部事務(wù), 它擁有自己的隔離范圍, 自己的鎖, 等等. 當(dāng)內(nèi)部事務(wù)開始執(zhí)行時(shí), 外部事務(wù)將被掛起, 內(nèi)務(wù)事務(wù)結(jié)束時(shí), 外部事務(wù)將繼續(xù)執(zhí)行.
另一方面, PROPAGATION_NESTED 開始一個(gè) "嵌套的" 事務(wù), 它是已經(jīng)存在事務(wù)的一個(gè)真正的子事務(wù). 潛套事務(wù)開始執(zhí)行時(shí), 它將取得一個(gè) savepoint. 如果這個(gè)嵌套事務(wù)失敗, 我們將回滾到此 savepoint. 潛套事務(wù)是外部事務(wù)的一部分, 只有外部事務(wù)結(jié)束后它才會(huì)被提交.
由此可見, PROPAGATION_REQUIRES_NEW 和 PROPAGATION_NESTED 的最大區(qū)別在于, PROPAGATION_REQUIRES_NEW 完全是一個(gè)新的事務(wù), 而 PROPAGATION_NESTED 則是外部事務(wù)的子事務(wù), 如果外部事務(wù) commit, 潛套事務(wù)也會(huì)被 commit, 這個(gè)規(guī)則同樣適用于 roll back.
那么外部事務(wù)如何利用嵌套事務(wù)的 savepoint 特性呢, 我們用代碼來說話














這種情況下, 因?yàn)?ServiceB#methodB 的事務(wù)屬性為 PROPAGATION_REQUIRES_NEW, 所以兩者不會(huì)發(fā)生任何關(guān)系, ServiceA#methodA 和 ServiceB#methodB 不會(huì)因?yàn)閷?duì)方的執(zhí)行情況而影響事務(wù)的結(jié)果, 因?yàn)樗鼈兏揪褪莾蓚€(gè)事務(wù), 在 ServiceB#methodB 執(zhí)行時(shí) ServiceA#methodA 的事務(wù)已經(jīng)掛起了 (關(guān)于事務(wù)掛起的內(nèi)容已經(jīng)超出了本文的討論范圍, 有時(shí)間我會(huì)再寫一些掛起的文章) .















ServiceB#methodB 如果 rollback, 那么內(nèi)部事務(wù)(即 ServiceB#methodB) 將回滾到它執(zhí)行前的 SavePoint(注意, 這是本文中第一次提到它, 潛套事務(wù)中最核心的概念), 而外部事務(wù)(即 ServiceA#methodA) 可以有以下兩種處理方式:
1. 改寫 ServiceA 如下












這種方式也是潛套事務(wù)最有價(jià)值的地方, 它起到了分支執(zhí)行的效果, 如果 ServiceB.methodB 失敗, 那么執(zhí)行 ServiceC.methodC(), 而 ServiceB.methodB 已經(jīng)回滾到它執(zhí)行之前的 SavePoint, 所以不會(huì)產(chǎn)生臟數(shù)據(jù)(相當(dāng)于此方法從未執(zhí)行過), 這種特性可以用在某些特殊的業(yè)務(wù)中, 而 PROPAGATION_REQUIRED 和 PROPAGATION_REQUIRES_NEW 都沒有辦法做到這一點(diǎn). (題外話 : 看到這種代碼, 似乎似曾相識(shí), 想起了 prototype.js 中的 Try 函數(shù) )
2. 代碼不做任何修改, 那么如果內(nèi)部事務(wù)(即 ServiceB#methodB) rollback, 那么首先 ServiceB.methodB 回滾到它執(zhí)行之前的 SavePoint(在任何情況下都會(huì)如此), 外部事務(wù)(即 ServiceA#methodA) 將根據(jù)具體的配置決定自己是 commit 還是 rollback (+MyCheckedException).
上面大致講述了潛套事務(wù)的使用場景, 下面我們來看如何在 spring 中使用 PROPAGATION_NESTED, 首先來看 AbstractPlatformTransactionManager
JdbcTransactionObjectSupport 告訴我們必須要滿足兩個(gè)條件才能 createSavepoint :
2. java.sql.Savepoint 必須存在, 即 jdk 版本要 1.4+
3. Connection.getMetaData().supportsSavepoints() 必須為 true, 即 jdbc drive 必須支持 JDBC 3.0
確保以上條件都滿足后, 你就可以嘗試使用 PROPAGATION_NESTED 了.