關(guān)于Spring?@Transactional事務(wù)傳播機(jī)制詳解
Spring事務(wù)傳播機(jī)制
1.什么是事務(wù)傳播機(jī)制?
舉個栗子,方法A是一個事務(wù)的方法,方法A執(zhí)行過程中調(diào)用了方法B,那么方法B有無事務(wù)以及方法B對事務(wù)的要求不同都會對方法A的事務(wù)具體執(zhí)行造成影響,同時方法A的事務(wù)對方法B的事務(wù)執(zhí)行也有影響,這種影響具體是什么就由兩個方法所定義的事務(wù)傳播類型所決定。
簡單說就是,我們方法調(diào)用通常是,一個方法調(diào)用另外一個,而不同方法可以有不同的事務(wù),所以傳播機(jī)制就是指在多個方法,事務(wù)要如何傳播。
2.Spring事務(wù)傳播類型Propagation介紹
一共有七種傳播類型
- Propagation.REQUIRED
- Propagation.SUPPORTS
- Propagation.MANDATORY
- Propagation.REQUIRED_NEW
- Propagation.NOT_SUPPORTED
- Propagation.NESTED
- Propagation.NEVER
本文從案例結(jié)合解釋一下不同傳播類型下多個@Transactional方法會發(fā)生什么?在遇到異常情況下,不同傳播機(jī)制會產(chǎn)生什么影響。
1. Propagation.REQUIRED
這是默認(rèn)的傳播機(jī)制,我們最常用的一種,也是@Transactional默認(rèn)的一種
如果當(dāng)前沒有事務(wù),則自己新建一個事務(wù),如果當(dāng)前存在事務(wù),則加入這個事務(wù)
// 示例1:
@Transactional(propagation = Propagation.REQUIRED)
public void main(){
insertA(); // 插入A
service.sub(); // 調(diào)用其他方法
}
// 兩個Service中調(diào)用,如果同一個要注意不能用this調(diào)用,事務(wù)不會起作用
@Transactional(propagation = Propagation.REQUIRED)
public void sub(){
insertB(); //插入B
throw RuntimeException; //發(fā)生異常拋出
insertC(); //調(diào)用C簡單來說就是,開啟一個事務(wù),上面的案例就是當(dāng)main方法如果沒開啟事務(wù),那么sub方法就會開啟,如果main方法已經(jīng)@Transactional開啟了事務(wù),sub方法就會加入外層方法的事務(wù),所以上面方法執(zhí)行在遇到異常時候會全部回滾
結(jié)果:
A、B、C全部無法插入。
// 示例2:
public void main(){
insertA(); // 插入A
service.sub(); // 調(diào)用其他方法
}
// 兩個Service中調(diào)用,如果同一個要注意不能用this調(diào)用,事務(wù)不會起作用
@Transactional(propagation = Propagation.REQUIRED)
public void sub(){
insertB(); //插入B
throw RuntimeException; //發(fā)生異常拋出
insertC(); //調(diào)用C結(jié)果:
A插入成功,BC開啟新的事務(wù),遇到異?;貪L,B、C無法插入
2. Propagation.SUPPORTS
當(dāng)前存在事務(wù),則加入當(dāng)前事務(wù),如果當(dāng)前沒有事務(wù),就以非事務(wù)方法執(zhí)行
// 示例3:
public void main(){
insertA(); // 插入A
service.sub(); // 調(diào)用其他方法
}
// 兩個Service中調(diào)用,如果同一個要注意不能用this調(diào)用,事務(wù)不會起作用
@Transactional(propagation = Propagation.SUPPORTS)
public void sub(){
insertB(); //插入B
throw RuntimeException; //發(fā)生異常拋出
insertC(); //調(diào)用C這個和REQUIRED很像,但是里層的sub方法事務(wù)取決于main方法,如果main方法有開啟那么里面的就和外層事務(wù)一起,如果發(fā)生異常全部回滾。
結(jié)果:
A、B插入成功,C無法插入因?yàn)榘l(fā)生異常
3. Propagation.MANDATORY
當(dāng)前存在事務(wù),則加入當(dāng)前事務(wù),如果當(dāng)前事務(wù)不存在,則拋出異常。
// 示例4:
public void main(){
insertA(); // 插入A
service.sub(); // 調(diào)用其他方法
}
// 兩個Service中調(diào)用,如果同一個要注意不能用this調(diào)用,事務(wù)不會起作用
@Transactional(propagation = Propagation.MANDATORY)
public void sub(){
insertB(); //插入B
throw RuntimeException; //發(fā)生異常拋出
insertC(); //調(diào)用C這種情形的執(zhí)行結(jié)果就是insertA存儲成功,而insertB和insertC沒有存儲。b和c沒有存儲,并不是事務(wù)回滾的原因,而是因?yàn)閙ain方法沒有聲明事務(wù),在去執(zhí)行sub方法時就直接拋出事務(wù)要求的異常(如果當(dāng)前事務(wù)不存在,則拋出異常),所以sub方法里的內(nèi)容就完全沒有執(zhí)行。
結(jié)果:
A插入成功,B、C無法插入,方法拋出異常
那么當(dāng)main方法有事務(wù)的情況下
// 示例5:
@Transactional(propagation = Propagation.REQUIRED)
public void main(){
insertA(); // 插入A
service.sub(); // 調(diào)用其他方法
}
// 兩個Service中調(diào)用,如果同一個要注意不能用this調(diào)用,事務(wù)不會起作用
@Transactional(propagation = Propagation.MANDATORY)
public void sub(){
insertB(); //插入B
throw RuntimeException; //發(fā)生異常拋出
insertC(); //調(diào)用C結(jié)果:
A、B、C全部無法插入,A、B回滾
4. Propagation.REQUIRED_NEW
創(chuàng)建一個新事務(wù),如果存在當(dāng)前事務(wù),則掛起該事務(wù)。
// 示例5:
@Transactional(propagation = Propagation.REQUIRED)
public void main(){
insertA(); // 插入A
service.sub(); // 調(diào)用其他方法
throw RuntimeException; //發(fā)生異常拋出
}
// 兩個Service中調(diào)用,如果同一個要注意不能用this調(diào)用,事務(wù)不會起作用
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void sub(){
insertB(); //插入B
insertC(); //調(diào)用C因?yàn)閟ub方法會開啟一個新的事務(wù),所以main方法拋出的異常并不會影響sub方法的提交
結(jié)果:
A插入失敗,B、C能插入成功
5. Propagation.NOT_SUPPORTED
始終以非事務(wù)方式執(zhí)行,如果當(dāng)前存在事務(wù),則掛起當(dāng)前事務(wù)
// 示例6:
@Transactional(propagation = Propagation.REQUIRED)
public void main(){
insertA(); // 插入A
service.sub(); // 調(diào)用其他方法
}
// 兩個Service中調(diào)用,如果同一個要注意不能用this調(diào)用,事務(wù)不會起作用
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void sub(){
insertB(); //插入B
throw RuntimeException; //發(fā)生異常拋出
insertC(); //調(diào)用C示例6因?yàn)楫?dāng)main方法有事務(wù)的時候,就會掛起當(dāng)前事務(wù)即main以事務(wù)運(yùn)行,sub不以事務(wù)運(yùn)行
所以最終結(jié)果:
A因?yàn)閟ub拋出異常事務(wù)回滾,插入失敗,B因?yàn)椴灰允聞?wù)運(yùn)行插入成功,C因?yàn)橛龅疆惓?,后續(xù)不會執(zhí)行,所以插入失敗。
// 示例7:
public void main(){
insertA(); // 插入A
service.sub(); // 調(diào)用其他方法
}
// 兩個Service中調(diào)用,如果同一個要注意不能用this調(diào)用,事務(wù)不會起作用
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void sub(){
insertB(); //插入B
throw RuntimeException; //發(fā)生異常拋出
insertC(); //調(diào)用C示例7這種情況就是所有方法都不會以事務(wù)運(yùn)行,A、B均能插入成功,C無法插入
6. Propagation.NEVER
不使用事務(wù),如果當(dāng)前事務(wù)存在,則拋出異常
// 示例7:
@Transactional(propagation = Propagation.REQUIRED)
public void main(){
insertA(); // 插入A
service.sub(); // 調(diào)用其他方法
}
// 兩個Service中調(diào)用,如果同一個要注意不能用this調(diào)用,事務(wù)不會起作用
@Transactional(propagation = Propagation.NEVER)
public void sub(){
insertB(); //插入B
insertC(); //調(diào)用Csub因?yàn)槭荖ever所以是不會執(zhí)行直接拋出錯誤,所以main的事務(wù)遇到異常直接回滾,所以A回滾無法插入,B、C不會插入。
7. Propagation.NESTED
如果當(dāng)前事務(wù)存在,則在嵌套(父子)事務(wù)中執(zhí)行,否則REQUIRED的操作一樣(開啟一個事務(wù))
// 示例7:
@Transactional(propagation = Propagation.REQUIRED)
public void main(){
insertA(); // 插入A
service.sub(); // 調(diào)用其他方法
throw RuntimeException; //發(fā)生異常拋出
}
// 兩個Service中調(diào)用,如果同一個要注意不能用this調(diào)用,事務(wù)不會起作用
@Transactional(propagation = Propagation.NESTED)
public void sub(){
insertB(); //插入B
insertC(); //調(diào)用C這個是最需要理解的一種傳播機(jī)制,要理清楚嵌套(父子)事務(wù),main的是父事務(wù),sub是子事務(wù),main發(fā)生異常全部都會回滾。
結(jié)果:
A、B、C全部回滾
// 示例8:
@Transactional(propagation = Propagation.REQUIRED)
public void main(){
insertA(); // 插入A
try {
service.sub(); // 調(diào)用其他方法
} catch (Exception e) {
}
insertD();
}
// 兩個Service中調(diào)用,如果同一個要注意不能用this調(diào)用,事務(wù)不會起作用
@Transactional(propagation = Propagation.NESTED)
public void sub(){
insertB(); //插入B
throw RuntimeException; //發(fā)生異常拋出
insertC(); //調(diào)用C示例8,子事務(wù)發(fā)生異常拋出,但父事務(wù)catch掉了,那么這個時候main方法就相當(dāng)于正常執(zhí)行沒有發(fā)生異常,那么就只有子事務(wù)回滾。
結(jié)果:
A、D插入成功,B、C插入失敗
- REQUIRED
- 內(nèi)外同一個事務(wù),任何一個地方拋出異常全部一起回滾。
- REQUIRED_NEW
- 內(nèi)部開啟一個新的事務(wù),外部事務(wù)回滾并不會影響內(nèi)部的事務(wù),而如果內(nèi)部事務(wù)拋出被catch也不會影響外部事務(wù)。
怎么樣快速記憶,七個分四組,221這樣記,兩個一對互相類似
| 組 | 傳播類型 | 含義 |
|---|---|---|
| group1 | Propagation.REQUIRED | 如果當(dāng)前已有事務(wù)則加入當(dāng)前事務(wù),否則開啟新的事務(wù) |
| group1 | Propagation.REQUIRED_NEW | 無論當(dāng)前是否有事務(wù)都開啟新的事務(wù) |
| group2 | Propagation.SUPPORTED | 如果當(dāng)前事務(wù)存在就加入事務(wù),否則以非事務(wù)運(yùn)行 |
| group2 | Propagation.NOT_SUPPORTED | 始終以非事務(wù)方式執(zhí)行,如果當(dāng)前存在事務(wù),則掛起當(dāng)前事務(wù) |
| group3 | Propagation.NEVER | 不使用事務(wù),如果當(dāng)前事務(wù)存在,則拋出異常 |
| group3 | Propagation.MANDATORY | 當(dāng)前存在事務(wù),則加入當(dāng)前事務(wù),如果當(dāng)前事務(wù)不存在,則拋出異常。 |
| group4 | Propagation.NESTED | 父子(嵌套)事務(wù),父回滾全回滾,子回滾不影響父事務(wù) |
3.具體案例
單純講案例比較枯燥,會覺得工作中什么情況會使用到呢,這邊就舉一個例子來講解一下。
在下單時候,我們最主要是寫入訂單、然后添加積分,最后記錄日志
@Service
public class OrderServiceImpl implements OrderService{
@Transactional
public void placeOrder(OrderDTO orderDTO){
try {
pointService.addPoint(Point point);
} catch (Exception e) {
// 記錄錯誤信息
}
//省略...
}
//省略...
} @Service
public class PointServiceImpl implements PointService{
@Transactional(propagation = Propagation.NESTED)
public void addPoint(Point point){
try {
recordService.addRecord(Record record);
} catch (Exception e) {
//省略...
}
//省略...
}
//省略...
} @Service
public class RecordServiceImpl implements RecordService{
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void addRecord(Record record){
//省略...
}
//省略...
}下單的操作不會影響添加積分的操作,所以我們使用NESTED,下單只要成功,添加積分可以成功或失敗,失敗的話就錯誤信息后續(xù)補(bǔ)償。而記錄日志我們可以有也可以沒有,就可以設(shè)置為NOT_SUPPORTED不開啟事務(wù),使得事務(wù)的方法能盡可能的精簡,避免一個很大的事務(wù)方法。
總結(jié)
本文講解了Spring事務(wù)的七種傳播機(jī)制,我們可以根據(jù)具體的類型,具體設(shè)置,避免事務(wù)的方法過于長,一個事務(wù)里面調(diào)用的庫表越多,就越有可能造成死鎖,所以我們要根據(jù)具體的需要拆分使用。
以上就是關(guān)于Spring @Transactional事務(wù)傳播機(jī)制詳解的詳細(xì)內(nèi)容,更多關(guān)于Spring @Transactional事務(wù)的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
使用Gson將字符串轉(zhuǎn)換成JsonObject和JsonArray
這篇文章主要介紹了使用Gson將字符串轉(zhuǎn)換成JsonObject和JsonArray,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-02-02
Java虛擬線程原理與實(shí)踐指南(含實(shí)際案例)
Java虛擬線程是一種輕量級線程,由JVM管理而非操作系統(tǒng),這篇文章主要介紹了Java虛擬線程原理與實(shí)踐指南的相關(guān)資料,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下2026-06-06
Spring Controller接收前端JSON數(shù)據(jù)請求方式
這篇文章主要為大家介紹了Spring Controller接收前端JSON數(shù)據(jù)請求方式詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-07-07
Spring中的BeanFactory對象實(shí)例化工廠詳解
這篇文章主要介紹了Spring中的BeanFactory對象實(shí)例化工廠詳解,BeanFactory及其子類是Spring IOC容器中最重要的一個類,BeanFactory由類名可以看出其是一個Bean工廠類,其實(shí)它確實(shí)是一個Bean工廠類,完成Bean的初始化操作,需要的朋友可以參考下2023-12-12
win11?idea?shift+F6快捷鍵失效問題解決方案
這篇文章主要介紹了win11?idea?shift+F6快捷鍵失效問題,本文給大家分享最新解決方案,需要的朋友可以參考下2023-08-08
Java 8 lambda表達(dá)式引入詳解及實(shí)例
這篇文章主要介紹了Java 8 lambda表達(dá)式引入詳解及實(shí)例的相關(guān)資料,需要的朋友可以參考下2017-05-05
SpringBoot+RustFS 實(shí)現(xiàn)文件切片極速上傳的實(shí)例代碼
本文介紹利用SpringBoot和RustFS構(gòu)建高性能文件切片上傳系統(tǒng),實(shí)現(xiàn)大文件秒傳、斷點(diǎn)續(xù)傳和分片上傳等功能,具有一定的參考價值,感興趣的可以了解一下2025-09-09
詳談Java編程之委托代理回調(diào)、內(nèi)部類以及匿名內(nèi)部類回調(diào)(閉包回調(diào))
下面小編就為大家?guī)硪黄斦凧ava編程之委托代理回調(diào)、內(nèi)部類以及匿名內(nèi)部類回調(diào)(閉包回調(diào))。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-05-05
用C++實(shí)現(xiàn)求N!中末尾0的個數(shù)的方法詳解
這篇文章主要介紹了用C++實(shí)現(xiàn)求N!中末尾0的個數(shù)的方法詳解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-07-07

