事務傳播行為
事務傳播行為指的是當一個事務方法被另一個事務方法調用時,這個方法是怎么運行的。
舉例說明:有兩個事務方法,一個是方法A,一個是方法B,方法A中調用了方法B,那方法B是開啟一個事務運行還是在方法A中的事務中運行,是由方法B的事務傳播行為控制的。
在Spring中定義了7中事務傳播行為:
傳播行為 | 含義 |
---|---|
PROPAGATION_REQUIRED | 表示當前方法必須運行在事務中,如果當前事務存在,方法將會在該事務中運行,否則,會啟動一個新的事務 |
PROPAGATION_SUPPORTS | 表示該方法不需要事務上下文,但是如果存在當前的事務的話,那么該方法會在這個事務匯總運行 |
PROPAGATION_MANDATORY | 表示該方法必須在事務中運行,如果當前事務不存在,則會拋出一個異常 |
PROPAGATION_REQUIRED_NEW | 表示當前方法必須運行在它自己的事務中。一個新的事務將被啟動,如果存在當前事務,在該方法執行期間,當期事務會被掛起。如果使用JTATransactionManager的話,則需要訪問TransactionManager |
PROPAGATION_NOT_SUPPORTED | 表示當前方法不應該運行在事務中,如果存在當前事務,在該方法運行期間,當前事務將被掛起,如果使用JTATransactionManager的話,則需要訪問TransactionManager |
PROPAGATION_NEVER | 表示當前方法不應該運行在事務上下文中,如果當前正有一個事務在運行,則拋出異常 |
PROPAGATION_NESTED | 表示如果當前已經存在一個事務,那么該方法將會在嵌套事務中運行。嵌套的事務可以獨立於當前事務進行單獨的提交或回滾。如果當前事務不存在,那么其行為和 PROPAGATION_REQUIRED一樣。注意各廠商對這種傳播行為的支持是有所差異的,可以參考資源管理器的文檔來確認它們是夠支持嵌套事務 |
1、PROPAGATION_REQUIRED
存在事務就支持該事務,不存在則開啟一個事務
演示代碼:
@Component
public class Transaction {
@Autowired
JdbcTemplate jdbcTemplate;
@Transactional(propagation = Propagation.REQUIRED)
public void methodA() {
jdbcTemplate.update("insert into users value (12,'wangwu','000000')");
methodB();
}
@Transactional(propagation = Propagation.REQUIRED)
public void methodB() {
jdbcTemplate.update("insert into actor value (20,'范閑',now())");
// 此處拋出異常,方法A和方法B的操作都將回滾
int i = 100 / 0;
}
}
單獨調用方法B,因為上下文不存在事務,所以會開啟一個事務
當調用方法A時,因為上下文不存在事務,所以會開啟一個事務,當執行到方法B時,方法B發現存在一個事務,方法B就不會開啟新的事務,而是在方法A中的事務中執行。
2、PROPAGATION_SUPPORTS
如果存在事務則支持當前事務,如果不存在事務則無事務運行
演示代碼:
@Component
public class Transaction {
@Autowired
JdbcTemplate jdbcTemplate;
@Transactional(propagation = Propagation.REQUIRED)
public void methodA() {
jdbcTemplate.update("insert into users value (12,'wangwu','000000')");
methodB();
}
@Transactional(propagation = Propagation.SUPPORTS)
public void methodB() {
jdbcTemplate.update("insert into actor value (20,'范閑',now())");
// 存在事務回滾操作,不存在事務則不回滾
int i = 100 / 0;
}
}
單獨調用方法B,無事務運行
調用方法A,方法B加入方法A的事務,事務的運行
3、PROPAGATION_MANDATORY
如果存在一個事務,則事務的運行,沒有事務則拋出異常
@Component
public class Transaction {
@Autowired
JdbcTemplate jdbcTemplate;
@Transactional(propagation = Propagation.REQUIRED)
public void methodA() {
jdbcTemplate.update("insert into users value (12,'wangwu','000000')");
methodB();
}
@Transactional(propagation = Propagation.MANDATORY)
public void methodB() {
jdbcTemplate.update("insert into actor value (20,'范閑',now())");
}
}
直接調用方法B,因為沒有事務所有會拋出異常
No existing transaction found for transaction marked with propagation 'mandatory'
調用方法A,方法B會加入到方法A的事務中去
4、PROPAGATION_REQUIRED_NEW
使用PROPAGATION_REQUIRES_NEW,需要使用 JtaTransactionManager作為事務管理器。
會開啟一個事務,如果存在一個事務,則會把存在的事務掛起
@Component
public class Transaction {
@Autowired
JdbcTemplate jdbcTemplate;
@Transactional(propagation = Propagation.REQUIRED)
public void methodA() {
doSomeTingA();
methodB();
doSomeTingB();
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void methodB() {
jdbcTemplate.update("insert into actor value (20,'范閑',now())");
}
public void doSomeTingA() {
jdbcTemplate.update("insert into users value (12,'wangwu','000000')");
}
public void doSomeTingB() {
jdbcTemplate.update("insert into actor value (21,'范思轍',now())");
}
}
當調用
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之外的其它代碼導致的結果卻被回滾了
5、PROPAGATION_NOT_SUPPORTED
PROPAGATION_NOT_SUPPORTED總是非事務的運行,並且掛起任何存在的事務,使用PROPAGATION_NOT_SUPPORTED,也需要使用JtaTransactionManager作為事務管理器。
6、PROPAGATION_NEVER
總是非事務的運行,如果存在一個活動的事務,則拋出異常
7、PROPAGATION_NESTED
如果一個活動的事務存在,則運行在一個嵌套事務中,如果沒有活動事務,則按照PROPAGATION_REQUIRED屬性執行
這是一個嵌套事務,使用JDBC 3.0驅動時,僅僅支持DataSourceTransactionManager作為事務管理器。
需要JDBC 驅動的java.sql.Savepoint類。使用PROPAGATION_NESTED,還需要把PlatformTransactionManager的nestedTransactionAllowed屬性設為true(屬性值默認為false)。
@Component
public class Transaction {
@Autowired
JdbcTemplate jdbcTemplate;
@Transactional(propagation = Propagation.REQUIRED)
public void methodA() {
doSomeTingA();
methodB();
doSomeTingB();
}
@Transactional(propagation = Propagation.NESTED)
public void methodB() {
jdbcTemplate.update("insert into actor value (20,'范閑',now())");
}
public void doSomeTingA() {
jdbcTemplate.update("insert into users value (12,'wangwu','000000')");
}
public void doSomeTingB() {
jdbcTemplate.update("insert into actor value (21,'范思轍',now())");
}
}
單獨執行方法B,按照PROPAGATION_REQUIRED屬性執行
如果調用方法A,相當於如下效果:
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版本支持。其它的JTATrasactionManager實現可能有不同的支持方式。
PROPAGATION_REQUIRES_NEW 啟動一個新的, 不依賴於環境的 “內部” 事務. 這個事務將被完全 commited 或 rolled back 而不依賴於外部事務, 它擁有自己的隔離范圍, 自己的鎖, 等等. 當內部事務開始執行時, 外部事務將被掛起, 內務事務結束時, 外部事務將繼續執行。
另一方面, PROPAGATION_NESTED 開始一個 “嵌套的” 事務, 它是已經存在事務的一個真正的子事務. 潛套事務開始執行時, 它將取得一個 savepoint. 如果這個嵌套事務失敗, 我們將回滾到此 savepoint. 潛套事務是外部事務的一部分, 只有外部事務結束后它才會被提交。
由此可見, PROPAGATION_REQUIRES_NEW 和 PROPAGATION_NESTED 的最大區別在於, PROPAGATION_REQUIRES_NEW 完全是一個新的事務, 而 PROPAGATION_NESTED 則是外部事務的子事務, 如果外部事務 commit, 嵌套事務也會被 commit, 這個規則同樣適用於 roll back.