Spring事務(wù)機(jī)制主要包括聲明式事務(wù)和編程式事務(wù)麦萤,此處側(cè)重講解聲明式事務(wù),編程式事務(wù)在實(shí)際開發(fā)中得不到廣泛使用扁眯,僅供學(xué)習(xí)參考壮莹。
Spring聲明式事務(wù)讓我們從復(fù)雜的事務(wù)處理中得到解脫。使得我們再也無需要去處理獲得連接姻檀、關(guān)閉連接命满、事務(wù)提交和回滾等這些操作。再也無需要我們在與事務(wù)相關(guān)的方法中處理大量的try…catch…finally代碼施敢。我們在使用Spring聲明式事務(wù)時(shí)周荐,有一個(gè)非常重要的概念就是事務(wù)屬性。事務(wù)屬性通常由事務(wù)的傳播行為僵娃,事務(wù)的隔離級(jí)別概作,事務(wù)的超時(shí)值和事務(wù)只讀標(biāo)志組成。我們在進(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ù)庫默認(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ù)需求。