Spring事務(wù)管理機(jī)制(轉(zhuǎn)載)

Spring事務(wù)機(jī)制主要包括聲明式事務(wù)和編程式事務(wù),此處側(cè)重講解聲明式事務(wù),編程式事務(wù)在實(shí)際開發(fā)中得不到廣泛使用,僅供學(xué)習(xí)參考。

Spring聲明式事務(wù)讓我們從復(fù)雜的事務(wù)處理中得到解脫。使得我們?cè)僖矡o需要去處理獲得連接、關(guān)閉連接、事務(wù)提交和回滾等這些操作。再也無需要我們?cè)谂c事務(wù)相關(guān)的方法中處理大量的try…catch…finally代碼。我們?cè)谑褂肧pring聲明式事務(wù)時(shí),有一個(gè)非常重要的概念就是事務(wù)屬性。事務(wù)屬性通常由事務(wù)的傳播行為,事務(wù)的隔離級(jí)別,事務(wù)的超時(shí)值和事務(wù)只讀標(biāo)志組成。我們?cè)谶M(jìn)行事務(wù)劃分時(shí),需要進(jìn)行事務(wù)定義,也就是配置事務(wù)的屬性。

下面分別詳細(xì)講解,事務(wù)的四種屬性,僅供諸位學(xué)習(xí)參考:

Spring在TransactionDefinition接口中定義這些屬性,以供PlatfromTransactionManager使用, PlatfromTransactionManager是spring事務(wù)管理的核心接口。

public interface TransactionDefinition {
int getPropagationBehavior();//返回事務(wù)的傳播行為。
int getIsolationLevel();//返回事務(wù)的隔離級(jí)別,事務(wù)管理器根據(jù)它來控制另外一個(gè)事務(wù)可以看到本事務(wù)內(nèi)的哪些數(shù)據(jù)。
int getTimeout();//返回事務(wù)必須在多少秒內(nèi)完成。
boolean isReadOnly();//事務(wù)是否只讀,事務(wù)管理器能夠根據(jù)這個(gè)返回值進(jìn)行優(yōu)化,確保事務(wù)是只讀的。
}
  • TransactionDefinition接口中定義五個(gè)隔離級(jí)別:
    ISOLATION_DEFAULT 這是一個(gè)PlatfromTransactionManager默認(rèn)的隔離級(jí)別,使用數(shù)據(jù)庫(kù)默認(rèn)的事務(wù)隔離級(jí)別.另外四個(gè)與JDBC的隔離級(jí)別相對(duì)應(yīng);

ISOLATION_READ_UNCOMMITTED 這是事務(wù)最低的隔離級(jí)別,它充許別外一個(gè)事務(wù)可以看到這個(gè)事務(wù)未提交的數(shù)據(jù)。這種隔離級(jí)別會(huì)產(chǎn)生臟讀,不可重復(fù)讀和幻像讀。

ISOLATION_READ_COMMITTED 保證一個(gè)事務(wù)修改的數(shù)據(jù)提交后才能被另外一個(gè)事務(wù)讀取。另外一個(gè)事務(wù)不能讀取該事務(wù)未提交的數(shù)據(jù)。這種事務(wù)隔離級(jí)別可以避免臟讀出現(xiàn),但是可能會(huì)出現(xiàn)不可重復(fù)讀和幻像讀。

ISOLATION_REPEATABLE_READ 這種事務(wù)隔離級(jí)別可以防止臟讀,不可重復(fù)讀。但是可能出現(xiàn)幻像讀。它除了保證一個(gè)事務(wù)不能讀取另一個(gè)事務(wù)未提交的數(shù)據(jù)外,還保證了避免下面的情況產(chǎn)生(不可重復(fù)讀)。

ISOLATION_SERIALIZABLE 這是花費(fèi)最高代價(jià)但是最可靠的事務(wù)隔離級(jí)別。事務(wù)被處理為順序執(zhí)行。除了防止臟讀,不可重復(fù)讀外,還避免了幻像讀。

1: Dirty reads(臟讀)。也就是說,比如事務(wù)A的未提交(還依然緩存)的數(shù)據(jù)被事務(wù)B讀走,如果事務(wù)A失敗回滾,會(huì)導(dǎo)致事務(wù)B所讀取的的數(shù)據(jù)是錯(cuò)誤的。
2: non-repeatable reads(不可重復(fù)讀)。比如事務(wù)A中兩處讀取數(shù)據(jù)-total-的值。在第一讀的時(shí)候,total是100,然后事務(wù)B就把total的數(shù)據(jù)改成200,事務(wù)A再讀一次,結(jié)果就發(fā)現(xiàn),total竟然就變成200了,造成事務(wù)A數(shù)據(jù)混亂。
3: phantom reads(幻讀),這個(gè)和non-repeatable reads相似,也是同一個(gè)事務(wù)中多次讀不一致的問題。但是non-repeatable reads的不一致是因?yàn)樗〉臄?shù)據(jù)集被改變了(比如total的數(shù)據(jù)),但是phantom reads所要讀的數(shù)據(jù)的不一致卻不是他所要讀的數(shù)據(jù)集改變,而是他的條件數(shù)據(jù)集改變。比如Select account.id where account.name=”ppgogo*”,第一次讀去了6個(gè)符合條件的id,第二次讀取的時(shí)候,由于事務(wù)b把一個(gè)帳號(hào)的名字由”dd”改成”ppgogo1″,結(jié)果取出來了7個(gè)數(shù)據(jù)。

  • 在TransactionDefinition接口中定義了七個(gè)事務(wù)傳播行為:
    (1)PROPAGATION_REQUIRED 如果存在一個(gè)事務(wù),則支持當(dāng)前事務(wù)。如果沒有事務(wù)則開啟一個(gè)新的事務(wù)。
//事務(wù)屬性 PROPAGATION_REQUIRED 
methodA{ 
…… 
methodB(); 
…… 
}
 
//事務(wù)屬性 PROPAGATION_REQUIRED 
methodB{ 
   …… 
}

使用spring聲明式事務(wù),spring使用AOP來支持聲明式事務(wù),會(huì)根據(jù)事務(wù)屬性,自動(dòng)在方法調(diào)用之前決定是否開啟一個(gè)事務(wù),并在方法執(zhí)行之后決定事務(wù)提交或回滾事務(wù)。

單獨(dú)調(diào)用methodB方法:

main{ 
 
metodB(); 
 
}

相當(dāng)于

Main{ 
 
Connection con=null; 
 
try{ 
 
con = getConnection(); 
 
con.setAutoCommit(false); 
 
//方法調(diào)用
 
methodB(); 
 
//提交事務(wù)
 
con.commit(); 
 
} 
 
Catch(RuntimeException ex){ 
 
            //回滾事務(wù)
 
con.rollback();   
 
} 
 
finally{ 
 
//釋放資源
 
closeCon(); 
 
} 
 
}

Spring保證在methodB方法中所有的調(diào)用都獲得到一個(gè)相同的連接。在調(diào)用methodB時(shí),沒有一個(gè)存在的事務(wù),所以獲得一個(gè)新的連接,開啟了一個(gè)新的事務(wù)。

單獨(dú)調(diào)用MethodA時(shí),在MethodA內(nèi)又會(huì)調(diào)用MethodB.

執(zhí)行效果相當(dāng)于:

         main{ 
 
Connection con = null; 
 
try{ 
 
con = getConnection(); 
 
    methodA(); 
 
con.commit(); 
 
} 
 
catch(RuntimeException ex){ 
 
con.rollback(); 
 
} 
 
finally{ 
 
   closeCon(); 
 
}  
 
}

調(diào)用MethodA時(shí),環(huán)境中沒有事務(wù),所以開啟一個(gè)新的事務(wù).當(dāng)在MethodA中調(diào)用MethodB時(shí),環(huán)境中已經(jīng)有了一個(gè)事務(wù),所以methodB就加入當(dāng)前事務(wù)。

(2)PROPAGATION_SUPPORTS 如果存在一個(gè)事務(wù),支持當(dāng)前事務(wù)。如果沒有事務(wù),則非事務(wù)的執(zhí)行。但是對(duì)于事務(wù)同步的事務(wù)管理器,PROPAGATION_SUPPORTS與不使用事務(wù)有少許不同。

//事務(wù)屬性 PROPAGATION_REQUIRED 
methodA(){ 
  methodB(); 
}
 
//事務(wù)屬性 PROPAGATION_SUPPORTS 
methodB(){ 
  …… 
}

單純的調(diào)用methodB時(shí),methodB方法是非事務(wù)的執(zhí)行的。當(dāng)調(diào)用methdA時(shí),methodB則加入了methodA的事務(wù)中,事務(wù)地執(zhí)行。

(3)PROPAGATION_MANDATORY 如果已經(jīng)存在一個(gè)事務(wù),支持當(dāng)前事務(wù)。如果沒有一個(gè)活動(dòng)的事務(wù),則拋出異常。

//事務(wù)屬性 PROPAGATION_REQUIRED 
    methodA(){ 
  methodB(); 
                }
 
    //事務(wù)屬性 PROPAGATION_MANDATORY 
    methodB(){ 
    …… 
    }

當(dāng)單獨(dú)調(diào)用methodB時(shí),因?yàn)楫?dāng)前沒有一個(gè)活動(dòng)的事務(wù),則會(huì)拋出異常throw new IllegalTransactionStateException(“Transaction propagation ‘mandatory’ but no existing transaction found”);當(dāng)調(diào)用methodA時(shí),methodB則加入到methodA的事務(wù)中,事務(wù)地執(zhí)行。

(4)PROPAGATION_REQUIRES_NEW 總是開啟一個(gè)新的事務(wù)。如果一個(gè)事務(wù)已經(jīng)存在,則將這個(gè)存在的事務(wù)掛起。

//事務(wù)屬性 PROPAGATION_REQUIRED 
methodA(){ 
   doSomeThingA(); 
methodB(); 
doSomeThingB(); 
}
 
//事務(wù)屬性 PROPAGATION_REQUIRES_NEW 
methodB(){ 
   …… 
}
main(){ 
  methodA(); 
}

相當(dāng)于

main(){ 
  TransactionManager tm = null; 
try{ 
  //獲得一個(gè)JTA事務(wù)管理器 
    tm = getTransactionManager(); 
    tm.begin();//開啟一個(gè)新的事務(wù) 
    Transaction ts1 = tm.getTransaction(); 
    doSomeThing(); 
    tm.suspend();//掛起當(dāng)前事務(wù) 
    try{ 
      tm.begin();//重新開啟第二個(gè)事務(wù) 
      Transaction ts2 = tm.getTransaction(); 
      methodB(); 
      ts2.commit();//提交第二個(gè)事務(wù) 
   } 
  Catch(RunTimeException ex){ 
      ts2.rollback();//回滾第二個(gè)事務(wù) 
  } 
  finally{ 
     //釋放資源 
   } 
    //methodB執(zhí)行完后,復(fù)恢第一個(gè)事務(wù) 
    tm.resume(ts1); 
doSomeThingB(); 
    ts1.commit();//提交第一個(gè)事務(wù) 
} 
catch(RunTimeException ex){ 
   ts1.rollback();//回滾第一個(gè)事務(wù) 
} 
finally{ 
   //釋放資源 
} 
}

在這里,我把ts1稱為外層事務(wù),ts2稱為內(nèi)層事務(wù)。從上面的代碼可以看出,ts2與ts1是兩個(gè)獨(dú)立的事務(wù),互不相干。Ts2是否成功并不依賴于ts1。如果methodA方法在調(diào)用methodB方法后的doSomeThingB方法失敗了,而methodB方法所做的結(jié)果依然被提交。而除了methodB之外的其它代碼導(dǎo)致的結(jié)果卻被回滾了。使用PROPAGATION_REQUIRES_NEW,需要使用JtaTransactionManager作為事務(wù)管理器。
(5)PROPAGATION_NOT_SUPPORTED 總是非事務(wù)地執(zhí)行,并掛起任何存在的事務(wù)。使用PROPAGATION_NOT_SUPPORTED,也需要使用JtaTransactionManager作為事務(wù)管理器。(代碼示例同上,可同理推出)

(6)PROPAGATION_NEVER 總是非事務(wù)地執(zhí)行,如果存在一個(gè)活動(dòng)事務(wù),則拋出異常;

(7)PROPAGATION_NESTED如果一個(gè)活動(dòng)的事務(wù)存在,則運(yùn)行在一個(gè)嵌套的事務(wù)中. 如果沒有活動(dòng)事務(wù), 則按TransactionDefinition.PROPAGATION_REQUIRED 屬性執(zhí)行。這是一個(gè)嵌套事務(wù),使用JDBC 3.0驅(qū)動(dòng)時(shí),僅僅支持DataSourceTransactionManager作為事務(wù)管理器。需要JDBC 驅(qū)動(dòng)的java.sql.Savepoint類。有一些JTA的事務(wù)管理器實(shí)現(xiàn)可能也提供了同樣的功能。使用PROPAGATION_NESTED,還需要把PlatformTransactionManager的nestedTransactionAllowed屬性設(shè)為true;而nestedTransactionAllowed屬性值默認(rèn)為false;

//事務(wù)屬性 PROPAGATION_REQUIRED 
methodA(){ 
   doSomeThingA(); 
   methodB(); 
   doSomeThingB(); 
}
 
//事務(wù)屬性 PROPAGATION_NESTED 
methodB(){ 
  …… 
}

如果單獨(dú)調(diào)用methodB方法,則按REQUIRED屬性執(zhí)行。如果調(diào)用methodA方法,相當(dāng)于下面的效果:

main(){ 
Connection con = null; 
Savepoint savepoint = null; 
try{ 
   con = getConnection(); 
   con.setAutoCommit(false); 
   doSomeThingA(); 
   savepoint = con2.setSavepoint(); 
   try{ 
       methodB(); 
   }catch(RuntimeException ex){ 
      con.rollback(savepoint); 
   } 
   finally{ 
     //釋放資源 
  }
 
   doSomeThingB(); 
   con.commit(); 
} 
catch(RuntimeException ex){ 
  con.rollback(); 
} 
finally{ 
   //釋放資源 
} 
}

當(dāng)methodB方法調(diào)用之前,調(diào)用setSavepoint方法,保存當(dāng)前的狀態(tài)到savepoint。如果methodB方法調(diào)用失敗,則恢復(fù)到之前保存的狀態(tài)。但是需要注意的是,這時(shí)的事務(wù)并沒有進(jìn)行提交,如果后續(xù)的代碼(doSomeThingB()方法)調(diào)用失敗,則回滾包括methodB方法的所有操作。

嵌套事務(wù)一個(gè)非常重要的概念就是內(nèi)層事務(wù)依賴于外層事務(wù)。外層事務(wù)失敗時(shí),會(huì)回滾內(nèi)層事務(wù)所做的動(dòng)作。而內(nèi)層事務(wù)操作失敗并不會(huì)引起外層事務(wù)的回滾。

PROPAGATION_NESTED 與PROPAGATION_REQUIRES_NEW的區(qū)別:它們非常類似,都像一個(gè)嵌套事務(wù),如果不存在一個(gè)活動(dòng)的事務(wù),都會(huì)開啟一個(gè)新的事務(wù)。使用PROPAGATION_REQUIRES_NEW時(shí),內(nèi)層事務(wù)與外層事務(wù)就像兩個(gè)獨(dú)立的事務(wù)一樣,一旦內(nèi)層事務(wù)進(jìn)行了提交后,外層事務(wù)不能對(duì)其進(jìn)行回滾。兩個(gè)事務(wù)互不影響。兩個(gè)事務(wù)不是一個(gè)真正的嵌套事務(wù)。同時(shí)它需要JTA事務(wù)管理器的支持。

使用PROPAGATION_NESTED時(shí),外層事務(wù)的回滾可以引起內(nèi)層事務(wù)的回滾。而內(nèi)層事務(wù)的異常并不會(huì)導(dǎo)致外層事務(wù)的回滾,它是一個(gè)真正的嵌套事務(wù)。DataSourceTransactionManager使用savepoint支持PROPAGATION_NESTED時(shí),需要JDBC 3.0以上驅(qū)動(dòng)及1.4以上的JDK版本支持。其它的JTA TrasactionManager實(shí)現(xiàn)可能有不同的支持方式。

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.
PROPAGATION_REQUIRED應(yīng)該是我們首先的事務(wù)傳播行為。它能夠滿足我們大多數(shù)的事務(wù)需求。

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

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