Spring 事務(wù)傳播特性和隔離性

在所有使用 spring 的應(yīng)用中, 聲明式事務(wù)管理可能是使用率最高的功能了, 但是, 從我觀察到的情況看, 絕大多數(shù)人并不能深刻理解事務(wù)聲明中不同事務(wù)傳播屬性配置的的含義, 讓我們來看一下 TransactionDefinition 接口中的定義


int PROPAGATION_REQUIRED =0;

int PROPAGATION_SUPPORTS =1;

int PROPAGATION_MANDATORY =2;

int PROPAGATION_REQUIRES_NEW =3;

int PROPAGATION_NOT_SUPPORTED =4;

int PROPAGATION_NEVER =5;

int PROPAGATION_NESTED =6;


從接口中我們可以看出,Spring一共定義了7種事務(wù)傳播特性,其中每一種的特性簡要說明如下:

PROPAGATION_REQUIRED -- 支持當(dāng)前事務(wù),如果當(dāng)前沒有事務(wù),就新建一個事務(wù)。這是最常見的選擇。

PROPAGATION_SUPPORTS -- 支持當(dāng)前事務(wù),如果當(dāng)前沒有事務(wù),就以非事務(wù)方式執(zhí)行。

PROPAGATION_MANDATORY -- 支持當(dāng)前事務(wù),如果當(dāng)前沒有事務(wù),就拋出異常。

PROPAGATION_REQUIRES_NEW -- 新建事務(wù),如果當(dāng)前存在事務(wù),把當(dāng)前事務(wù)掛起。

PROPAGATION_NOT_SUPPORTED -- 以非事務(wù)方式執(zhí)行操作,如果當(dāng)前存在事務(wù),就把當(dāng)前事務(wù)掛起。

PROPAGATION_NEVER -- 以非事務(wù)方式執(zhí)行,如果當(dāng)前存在事務(wù),則拋出異常。

PROPAGATION_NESTED -- 如果當(dāng)前存在事務(wù),則在嵌套事務(wù)內(nèi)執(zhí)行。如果當(dāng)前沒有事務(wù),則進(jìn)行與PROPAGATION_REQUIRED類似的操作。

前六個策略類似于EJB CMT,第七個(PROPAGATION_NESTED)是Spring所提供的一個特殊變量。

它要求事務(wù)管理器或者使用JDBC 3.0 Savepoint API提供嵌套事務(wù)行為(如Spring的DataSourceTransactionManager)



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

假如有兩個業(yè)務(wù)接口 ServiceA 和 ServiceB, 其中 ServiceA 中有一個方法實(shí)現(xiàn)如下?

/**?

* 事務(wù)屬性配置為 PROPAGATION_REQUIRED?

*/?

void methodA() {?

// 調(diào)用 ServiceB 的方法?

ServiceB.methodB();?

}?

那么如果 ServiceB 的 methodB? 如果配置事務(wù), 就必須配置為 PROPAGATION_NESTED?


這種想法可能害了不少人, 認(rèn)為 Service 之間應(yīng)該避免互相調(diào)用, 其實(shí)根本不用擔(dān)心這點(diǎn),PROPAGATION_REQUIRED 已經(jīng)說得很明白,?

如果當(dāng)前線程中已經(jīng)存在事務(wù), 方法調(diào)用會加入此事務(wù), 果當(dāng)前沒有事務(wù),就新建一個事務(wù), 所以 ServiceB#methodB() 的事務(wù)只要遵循最普通的規(guī)則配置為 PROPAGATION_REQUIRED 即可, 如果 ServiceB#methodB (我們稱之為內(nèi)部事務(wù), 為下文打下基礎(chǔ)) 拋了異常, 那么 ServiceA#methodA(我們稱之為外部事務(wù)) 如果沒有特殊配置此異常時事務(wù)提交 (即 +MyCheckedException的用法), 那么整個事務(wù)是一定要 rollback 的, 什么 Service 只能調(diào) Dao 之類的言論純屬無稽之談, spring 只負(fù)責(zé)配置了事務(wù)屬性方法的攔截, 它怎么知道你這個方法是在 Service 還是 Dao 里 ??

也就是說, 最容易弄混淆的其實(shí)是 PROPAGATION_REQUIRES_NEW 和 PROPAGATION_NESTED, 那么這兩種方式又有何區(qū)別呢? 我簡單的翻譯一下 Juergen Hoeller 的話 :?

?PROPAGATION_REQUIRES_NEW 啟動一個新的, 不依賴于環(huán)境的 "內(nèi)部" 事務(wù). 這個事務(wù)將被完全 commited 或 rolled back 而不依賴于外部事務(wù), 它擁有自己的隔離范圍, 自己的鎖, 等等. 當(dāng)內(nèi)部事務(wù)開始執(zhí)行時, 外部事務(wù)將被掛起, 內(nèi)務(wù)事務(wù)結(jié)束時, 外部事務(wù)將繼續(xù)執(zhí)行.?

?另一方面, PROPAGATION_NESTED 開始一個 "嵌套的" 事務(wù),? 它是已經(jīng)存在事務(wù)的一個真正的子事務(wù). 潛套事務(wù)開始執(zhí)行時,? 它將取得一個 savepoint. 如果這個嵌套事務(wù)失敗, 我們將回滾到此 savepoint. 潛套事務(wù)是外部事務(wù)的一部分, 只有外部事務(wù)結(jié)束后它才會被提交.?

?由此可見, PROPAGATION_REQUIRES_NEW 和 PROPAGATION_NESTED 的最大區(qū)別在于, PROPAGATION_REQUIRES_NEW 完全是一個新的事務(wù), 而 PROPAGATION_NESTED 則是外部事務(wù)的子事務(wù), 如果外部事務(wù) commit, 潛套事務(wù)也會被 commit, 這個規(guī)則同樣適用于 roll back.?

那么外部事務(wù)如何利用嵌套事務(wù)的 savepoint 特性呢, 我們用代碼來說話?

Java代碼:

ServiceA?{???????????

?/**??????*?事務(wù)屬性配置為?PROPAGATION_REQUIRED??????*/?????

?void?methodA()?{???

???????ServiceB.methodB();???

???}???

?}??


??ServiceB?{????????????

/**??????*?事務(wù)屬性配置為?PROPAGATION_REQUIRES_NEW??????*/??

?????void?methodB()?{???

???}????

????}?????


這種情況下, 因為 ServiceB#methodB 的事務(wù)屬性為 PROPAGATION_REQUIRES_NEW, 所以兩者不會發(fā)生任何關(guān)系, ServiceA#methodA 和 ServiceB#methodB 不會因為對方的執(zhí)行情況而影響事務(wù)的結(jié)果, 因為它們根本就是兩個事務(wù), 在 ServiceB#methodB 執(zhí)行時 ServiceA#methodA 的事務(wù)已經(jīng)掛起了 (關(guān)于事務(wù)掛起的內(nèi)容已經(jīng)超出了本文的討論范圍, 有時間我會再寫一些掛起的文章) .?


那么 PROPAGATION_NESTED 又是怎么回事呢? 繼續(xù)看代碼?

ServiceA?{????????

????/**??????*?事務(wù)屬性配置為?PROPAGATION_REQUIRED??????*/??????

void?methodA()?{??????????ServiceB.methodB();????

??}???

?}???

?ServiceB?{??????????

??/**??????*?事務(wù)屬性配置為?PROPAGATION_NESTED??????*/?????

??void?methodB()?{??????}?????

???}?????



現(xiàn)在的情況就變得比較復(fù)雜了, ServiceB#methodB 的事務(wù)屬性被配置為 PROPAGATION_NESTED, 此時兩者之間又將如何協(xié)作呢? 從 Juergen Hoeller 的原話中我們可以找到答案, ServiceB#methodB 如果 rollback, 那么內(nèi)部事務(wù)(即 ServiceB#methodB) 將回滾到它執(zhí)行前的 SavePoint(注意, 這是本文中第一次提到它, 潛套事務(wù)中最核心的概念), 而外部事務(wù)(即 ServiceA#methodA) 可以有以下兩種處理方式:?

1. 改寫 ServiceA 如下?

ServiceA?{??????????

??/**??????*?事務(wù)屬性配置為?PROPAGATION_REQUIRED??????*/???

???void?methodA()?{????????

??try?{?????????????

? ? ? ServiceB.methodB();???????

???}?catch?(SomeException)?{?????????????

? ? ? ?//?執(zhí)行其他業(yè)務(wù),?如?ServiceC.methodC();?????

?????}????

??}???

?}??


這種方式也是潛套事務(wù)最有價值的地方, 它起到了分支執(zhí)行的效果, 如果 ServiceB.methodB 失敗, 那么執(zhí)行 ServiceC.methodC(), 而 ServiceB.methodB 已經(jīng)回滾到它執(zhí)行之前的 SavePoint, 所以不會產(chǎn)生臟數(shù)據(jù)(相當(dāng)于此方法從未執(zhí)行過), 這種特性可以用在某些特殊的業(yè)務(wù)中, 而 PROPAGATION_REQUIRED 和 PROPAGATION_REQUIRES_NEW 都沒有辦法做到這一點(diǎn). (題外話 : 看到這種代碼, 似乎似曾相識, 想起了 prototype.js 中的 Try 函數(shù) )?

2. 代碼不做任何修改, 那么如果內(nèi)部事務(wù)(即 ServiceB#methodB) rollback, 那么首先 ServiceB.methodB 回滾到它執(zhí)行之前的 SavePoint(在任何情況下都會如此),?

外部事務(wù)(即 ServiceA#methodA) 將根據(jù)具體的配置決定自己是 commit 還是 rollback (+MyCheckedException).?

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,501評論 6 544
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,673評論 3 429
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,610評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,939評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 72,668評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 56,004評論 1 329
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,001評論 3 449
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 43,173評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,705評論 1 336
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 41,426評論 3 359
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,656評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,139評論 5 364
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,833評論 3 350
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,247評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,580評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,371評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,621評論 2 380

推薦閱讀更多精彩內(nèi)容