平時我們使用spring框架,不論是springmvcv還是springboot,springCloud,絕大多數情況我們都是在方法,或者直接在類上面加一個@Transactional,將事務交給spring替我們去管理,然後並沒有具體分析一些情況,今天結合幾個例子,結合源代碼,使用僞代碼解釋一波。
1.情況一
service(){
//方法A
methodA(){
insertA();
}
//方法B
@Transactional
methodB(){
insertB();
throw new RunTimeException("強制拋一個異常");
}
public void static main(String[] args){
methodA();
methodB();
}
}
情況一就是這樣,main方法裏面順序調用AB兩個方法,A方法不加事務註解,B方法加了事務註解。如果不瞭解@Transactional 事務的傳播性,可能會回答:A成功插入,B插入失敗,但是實際情況卻是A,B均插入成功了。到底是什麼原因呢?這裏先簡單介紹一下事務的6個傳播屬性:
PROPAGATION_REQUIRED : 支持當前事務,如果當前沒有事務,就新建一個事務,這也是最常見的
PROPAGATION_SUPPORTS : 支持當前事務,如果當前沒有事務,就以非事務的方式執行
PROPAGATION_MANDATORY: 支持當前事務,如果當前沒有事務,就拋異常
PROPAGATION_REQUIRES_NEW:新建事務,如果當前事務存在,就把當前事務掛起
PROPAGATION_NOT_SUPPORTED:以非事務的方式執行,如果存在當前事務,就把當前事務掛起
PROPAGATION_NEVER: 以非事務的方式執行,如果當前存在事務,就拋異常
PROPAGATION_NESTED:如果存在當前事務,則在嵌套事務內執行,如果當前沒有事務,則新建一個事務
前六個策略類似於EJB CMT,第七個(PROPAGATION_NESTED)是Spring所提供的一個特殊變量。
研究源碼,調試程序可以看到:
A沒有事務管理,則線程內的connection 有個autoCommit = true
B得到事務的時候,由於事務的傳播性依然生效, 得到的還是A方法的commit,其autoCommit = true,故而逐條sql進行提交,即A,B都會插入
下面我們來分析情況二:
serviceA(){
methodA(){
insertA();
}
}
serviceB(){
@Transactional
methodB(){
insertB();
throw new RuntimeExcption("強制拋出的異常");
}
}
serviceC(){
@Autowired
private ServiceA serviceA;
@Autowired
private ServiceB serviceB;
public void staic main(String[] args){
serviceA.methodA();
serviceB.methodB();
}
}
情況二的主要代碼和情況一一樣,都是要調用methodA和methodB,但是結果卻不同,情況二的正確結果是指揮插入A,而B會回滾,這是爲什麼呢?同樣是在B方法上面加了事務註解....
其實大家都知道,spring的事務是交由cglib動態代理的,而動態代理對象產生的時機就非常重要了。再回到本例子:
A:在同一個service內部,事務之間嵌套調用,普通方法和事務方法之間的嵌套調用,都不會開啓新的事務(因爲shpring使用的是動態代理的方式來控制的事務,而動態代理最終都是要調用原始對象的,而原始對象在調用方法時,已存在代理對象,是不會再觸發代理了!)
B:兩個方法在不同的service裏(即不同的對象,即代理對象也不是同一個),在ServiceC中,使用注入的方式將serviceA和serviceB注入,這樣即使A沒有使用事務,B也有自己的代理,會根據PROPAGATION_REQUIRED 而生成新的事務.