事務的第一個方面是傳播行為(propagation behavior)。當事務方法被另一個事務方法調用時,必須指定事務應該如何傳播。例如:方法可能繼續在現有事務中運作,也可能開啟一個新事務,并在自己的事務中運作。Spring定義了七種傳播行為:![]()
Spring - 事務之傳播行為
(1)PROPAGATION_REQUIRED 如果存在一個事務,則支援目前事務。如果沒有事務則開啟一個新的事務。
//事務屬性 PROPAGATION_REQUIRED
methodA{
……
methodB();
……
}
//事務屬性 PROPAGATION_REQUIRED
methodB{
……
}
使用Spring聲明式事務,Spring使用AOP來支援聲明式事務,會根據事務屬性,自動在方法調用之前決定是否開啟一個事務,并在方法執行之後決定事務送出或復原事務。
單獨調用methodB方法:
main{
methodB();
}
相當于
Main{
Connection con=null;
try{
con = getConnection();
con.setAutoCommit(false);
//方法調用
methodB();
//送出事務
con.commit();
} Catch(RuntimeException ex) {
//復原事務
con.rollback();
} finally {
//釋放資源
closeCon();
}
}
Spring保證在methodB方法中所有的調用都獲得到一個相同的連接配接。在調用methodB時,沒有一個存在的事務,是以獲得一個新的連接配接,開啟了一個新的事務。
單獨調用MethodA時,在MethodA内又會調用MethodB。
執行效果相當于:
main{
Connection con = null;
try{
con = getConnection();
methodA();
con.commit();
} catch(RuntimeException ex) {
con.rollback();
} finally {
closeCon();
}
}
調用MethodA時,環境中沒有事務,是以開啟一個新的事務。當在MethodA中調用MethodB時,環境中已經有了一個事務,是以methodB就加入目前事務。
(2)PROPAGATION_SUPPORTS 如果存在一個事務,支援目前事務。如果沒有事務,則非事務的執行。但是對于事務同步的事務管理器,PROPAGATION_SUPPORTS與不使用事務有少許不同。
//事務屬性 PROPAGATION_REQUIRED
methodA(){
methodB();
}
//事務屬性 PROPAGATION_SUPPORTS
methodB(){
……
}
單純的調用methodB時,methodB方法是非事務的執行的。當調用methdA時,methodB則加入了methodA的事務中,事務地執行。
(3)PROPAGATION_MANDATORY 如果已經存在一個事務,支援目前事務。如果沒有一個活動的事務,則抛出異常。
//事務屬性 PROPAGATION_REQUIRED
methodA(){
methodB();
}
//事務屬性 PROPAGATION_MANDATORY
methodB(){
……
}
當單獨調用methodB時,因為目前沒有一個活動的事務,則會抛出異常throw new IllegalTransactionStateException(“Transaction propagation ‘mandatory’ but no existing transaction found”); 當調用methodA時,methodB則加入到methodA的事務中,事務地執行。
(4)PROPAGATION_REQUIRES_NEW 總是開啟一個新的事務。如果一個事務已經存在,則将這個存在的事務挂起。
//事務屬性 PROPAGATION_REQUIRED
methodA(){
doSomeThingA();
methodB();
doSomeThingB();
}
//事務屬性 PROPAGATION_REQUIRES_NEW
methodB(){
……
}
調用A方法:
main(){
methodA();
}
相當于
main(){
TransactionManager tm = null;
try{
//獲得一個JTA事務管理器
tm = getTransactionManager();
tm.begin();//開啟一個新的事務
Transaction ts1 = tm.getTransaction();
doSomeThing();
tm.suspend();//挂起目前事務
try{
tm.begin();//重新開啟第二個事務
Transaction ts2 = tm.getTransaction();
methodB();
ts2.commit();//送出第二個事務
} Catch(RunTimeException ex) {
ts2.rollback();//復原第二個事務
} finally {
//釋放資源
}
//methodB執行完後,恢複第一個事務
tm.resume(ts1);
doSomeThingB();
ts1.commit();//送出第一個事務
}
catch(RunTimeException ex) {
ts1.rollback();//復原第一個事務
}
finally {
//釋放資源
}
}
在這裡,我把ts1稱為外層事務,ts2稱為内層事務。從上面的代碼可以看出,ts2與ts1是兩個獨立的事務,互不相幹。ts2是否成功并不依賴于 ts1。如果methodA方法在調用methodB方法後的doSomeThingB方法失敗了,而methodB方法所做的結果依然被送出。而除了 methodB 之外的其它代碼導緻的結果卻被復原了。使用PROPAGATION_REQUIRES_NEW,需要使用 JtaTransactionManager作為事務管理器。
(5)PROPAGATION_NOT_SUPPORTED 總是非事務地執行,并挂起任何存在的事務。使用PROPAGATION_NOT_SUPPORTED,也需要使用JtaTransactionManager作為事務管理器。(代碼示例同上,可同理推出)
(6)PROPAGATION_NEVER 總是非事務地執行,如果存在一個活動事務,則抛出異常。
(7)PROPAGATION_NESTED如果一個活動的事務存在,則運作在一個嵌套的事務中。如果沒有活動事務,則按TransactionDefinition.PROPAGATION_REQUIRED 屬性執行。這是一個嵌套事務,使用JDBC 3.0驅動時,僅僅支援DataSourceTransactionManager作為事務管理器。需要JDBC 驅動的java.sql.Savepoint類。有一些JTA的事務管理器實作可能也提供了同樣的功能。使用PROPAGATION_NESTED,還需要把PlatformTransactionManager的nestedTransactionAllowed屬性設為true;而 nestedTransactionAllowed屬性值預設為false。
//事務屬性 PROPAGATION_REQUIRED
methodA(){
doSomeThingA();
methodB();
doSomeThingB();
}
//事務屬性 PROPAGATION_NESTED
methodB(){
……
}
如果單獨調用methodB方法,則按REQUIRED屬性執行。如果調用methodA方法,相當于下面的效果:
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 {
//釋放資源
}
}
當methodB方法調用之前,調用setSavepoint方法,儲存目前的狀态到savepoint。如果methodB方法調用失敗,則恢複到之前儲存的狀态。但是需要注意的是,這時的事務并沒有進行送出,如果後續的代碼(doSomeThingB()方法)調用失敗,則復原包括methodB方法的所有操作。
嵌套事務一個非常重要的概念就是内層事務依賴于外層事務。外層事務失敗時,會復原内層事務所做的動作。而内層事務操作失敗并不會引起外層事務的復原。
PROPAGATION_NESTED 與PROPAGATION_REQUIRES_NEW的差別:它們非常類似,都像一個嵌套事務,如果不存在一個活動的事務,都會開啟一個新的事務。使用 PROPAGATION_REQUIRES_NEW時,内層事務與外層事務就像兩個獨立的事務一樣,一旦内層事務進行了送出後,外層事務不能對其進行復原。兩個事務互不影響。兩個事務不是一個真正的嵌套事務。同時它需要JTA事務管理器的支援。
使用PROPAGATION_NESTED時,外層事務的復原可以引起内層事務的復原。而内層事務的異常并不會導緻外層事務的復原,它是一個真正的嵌套事務。DataSourceTransactionManager使用savepoint支援PROPAGATION_NESTED時,需要JDBC 3.0以上驅動及1.4以上的JDK版本支援。其它的JTA TrasactionManager實作可能有不同的支援方式。
PROPAGATION_REQUIRES_NEW 啟動一個新的, 不依賴于環境的 “内部” 事務. 這個事務将被完全 commited 或 rolled back 而不依賴于外部事務,它擁有自己的隔離範圍,自己的鎖,等等。當内部事務開始執行時,外部事務将被挂起,内務事務結束時,外部事務将繼續執行。
另一方面,PROPAGATION_NESTED 開始一個 “嵌套的” 事務,它是已經存在事務的一個真正的子事務。潛套事務開始執行時,它将取得一個 savepoint 如果這個嵌套事務失敗,我們将復原到此 savepoint 潛套事務是外部事務的一部分,隻有外部事務結束後它才會被送出。