欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

關(guān)于Spring?@Transactional事務(wù)傳播機(jī)制詳解

 更新時(shí)間:2023年08月04日 11:40:09   作者:Endwas  
我們?nèi)粘9ぷ髦袠O少使用事務(wù)傳播級(jí)別,單純只是使用事務(wù)和rollbackfor拋出異常來(lái)解決事務(wù)問(wèn)題,但其實(shí)我們很多時(shí)候使用的是不正確的,或者說(shuō)會(huì)造成事務(wù)粒度過(guò)大,本文詳解一下事務(wù)傳播級(jí)別,也讓自己更好地處理事務(wù)問(wèn)題,需要的朋友可以參考下

Spring事務(wù)傳播機(jī)制

1.什么是事務(wù)傳播機(jī)制?

舉個(gè)栗子,方法A是一個(gè)事務(wù)的方法,方法A執(zhí)行過(guò)程中調(diào)用了方法B,那么方法B有無(wú)事務(wù)以及方法B對(duì)事務(wù)的要求不同都會(huì)對(duì)方法A的事務(wù)具體執(zhí)行造成影響,同時(shí)方法A的事務(wù)對(duì)方法B的事務(wù)執(zhí)行也有影響,這種影響具體是什么就由兩個(gè)方法所定義的事務(wù)傳播類(lèi)型所決定。

簡(jiǎn)單說(shuō)就是,我們方法調(diào)用通常是,一個(gè)方法調(diào)用另外一個(gè),而不同方法可以有不同的事務(wù),所以傳播機(jī)制就是指在多個(gè)方法,事務(wù)要如何傳播。

2.Spring事務(wù)傳播類(lèi)型Propagation介紹

一共有七種傳播類(lèi)型

  • Propagation.REQUIRED
  • Propagation.SUPPORTS
  • Propagation.MANDATORY
  • Propagation.REQUIRED_NEW
  • Propagation.NOT_SUPPORTED
  • Propagation.NESTED
  • Propagation.NEVER

本文從案例結(jié)合解釋一下不同傳播類(lèi)型下多個(gè)@Transactional方法會(huì)發(fā)生什么?在遇到異常情況下,不同傳播機(jī)制會(huì)產(chǎn)生什么影響。

1. Propagation.REQUIRED

這是默認(rèn)的傳播機(jī)制,我們最常用的一種,也是@Transactional默認(rèn)的一種

如果當(dāng)前沒(méi)有事務(wù),則自己新建一個(gè)事務(wù),如果當(dāng)前存在事務(wù),則加入這個(gè)事務(wù)

// 示例1:
@Transactional(propagation = Propagation.REQUIRED)
public void main(){
    insertA();  // 插入A 
    service.sub();   // 調(diào)用其他方法
}
// 兩個(gè)Service中調(diào)用,如果同一個(gè)要注意不能用this調(diào)用,事務(wù)不會(huì)起作用
@Transactional(propagation = Propagation.REQUIRED)
public void sub(){
    insertB();  //插入B
    throw RuntimeException;     //發(fā)生異常拋出
    insertC();  //調(diào)用C

簡(jiǎn)單來(lái)說(shuō)就是,開(kāi)啟一個(gè)事務(wù),上面的案例就是當(dāng)main方法如果沒(méi)開(kāi)啟事務(wù),那么sub方法就會(huì)開(kāi)啟,如果main方法已經(jīng)@Transactional開(kāi)啟了事務(wù),sub方法就會(huì)加入外層方法的事務(wù),所以上面方法執(zhí)行在遇到異常時(shí)候會(huì)全部回滾

結(jié)果:

A、B、C全部無(wú)法插入。

// 示例2:
public void main(){
    insertA();  // 插入A 
    service.sub();   // 調(diào)用其他方法
}
// 兩個(gè)Service中調(diào)用,如果同一個(gè)要注意不能用this調(diào)用,事務(wù)不會(huì)起作用
@Transactional(propagation = Propagation.REQUIRED)
public void sub(){
    insertB();  //插入B
    throw RuntimeException;     //發(fā)生異常拋出
    insertC();  //調(diào)用C

結(jié)果:

A插入成功,BC開(kāi)啟新的事務(wù),遇到異?;貪L,B、C無(wú)法插入

2. Propagation.SUPPORTS

當(dāng)前存在事務(wù),則加入當(dāng)前事務(wù),如果當(dāng)前沒(méi)有事務(wù),就以非事務(wù)方法執(zhí)行

// 示例3:
public void main(){
    insertA();  // 插入A 
    service.sub();   // 調(diào)用其他方法
}
// 兩個(gè)Service中調(diào)用,如果同一個(gè)要注意不能用this調(diào)用,事務(wù)不會(huì)起作用
@Transactional(propagation = Propagation.SUPPORTS)
public void sub(){
    insertB();  //插入B
    throw RuntimeException;     //發(fā)生異常拋出
    insertC();  //調(diào)用C

這個(gè)和REQUIRED很像,但是里層的sub方法事務(wù)取決于main方法,如果main方法有開(kāi)啟那么里面的就和外層事務(wù)一起,如果發(fā)生異常全部回滾。

結(jié)果:

A、B插入成功,C無(wú)法插入因?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)用其他方法
}
// 兩個(gè)Service中調(diào)用,如果同一個(gè)要注意不能用this調(diào)用,事務(wù)不會(huì)起作用
@Transactional(propagation = Propagation.MANDATORY)
public void sub(){
    insertB();  //插入B
    throw RuntimeException;     //發(fā)生異常拋出
    insertC();  //調(diào)用C

這種情形的執(zhí)行結(jié)果就是insertA存儲(chǔ)成功,而insertB和insertC沒(méi)有存儲(chǔ)。b和c沒(méi)有存儲(chǔ),并不是事務(wù)回滾的原因,而是因?yàn)閙ain方法沒(méi)有聲明事務(wù),在去執(zhí)行sub方法時(shí)就直接拋出事務(wù)要求的異常(如果當(dāng)前事務(wù)不存在,則拋出異常),所以sub方法里的內(nèi)容就完全沒(méi)有執(zhí)行。

結(jié)果:

A插入成功,B、C無(wú)法插入,方法拋出異常

那么當(dāng)main方法有事務(wù)的情況下

// 示例5:
@Transactional(propagation = Propagation.REQUIRED)
public void main(){
    insertA();  // 插入A 
    service.sub();   // 調(diào)用其他方法
}
// 兩個(gè)Service中調(diào)用,如果同一個(gè)要注意不能用this調(diào)用,事務(wù)不會(huì)起作用
@Transactional(propagation = Propagation.MANDATORY)
public void sub(){
    insertB();  //插入B
    throw RuntimeException;     //發(fā)生異常拋出
    insertC();  //調(diào)用C

結(jié)果:

A、B、C全部無(wú)法插入,A、B回滾

4. Propagation.REQUIRED_NEW

創(chuàng)建一個(gè)新事務(wù),如果存在當(dāng)前事務(wù),則掛起該事務(wù)。

// 示例5:
@Transactional(propagation = Propagation.REQUIRED)
public void main(){
    insertA();  // 插入A 
    service.sub();   // 調(diào)用其他方法
    throw RuntimeException;     //發(fā)生異常拋出
}
// 兩個(gè)Service中調(diào)用,如果同一個(gè)要注意不能用this調(diào)用,事務(wù)不會(huì)起作用
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void sub(){
    insertB();  //插入B
    insertC();  //調(diào)用C

因?yàn)閟ub方法會(huì)開(kāi)啟一個(gè)新的事務(wù),所以main方法拋出的異常并不會(huì)影響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)用其他方法
}
// 兩個(gè)Service中調(diào)用,如果同一個(gè)要注意不能用this調(diào)用,事務(wù)不會(huì)起作用
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void sub(){
    insertB();  //插入B
    throw RuntimeException;     //發(fā)生異常拋出
    insertC();  //調(diào)用C

示例6因?yàn)楫?dāng)main方法有事務(wù)的時(shí)候,就會(huì)掛起當(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ù)不會(huì)執(zhí)行,所以插入失敗。

// 示例7:
public void main(){
    insertA();  // 插入A 
    service.sub();   // 調(diào)用其他方法
}
// 兩個(gè)Service中調(diào)用,如果同一個(gè)要注意不能用this調(diào)用,事務(wù)不會(huì)起作用
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void sub(){
    insertB();  //插入B
    throw RuntimeException;     //發(fā)生異常拋出
    insertC();  //調(diào)用C

示例7這種情況就是所有方法都不會(huì)以事務(wù)運(yùn)行,A、B均能插入成功,C無(wú)法插入

6. Propagation.NEVER

不使用事務(wù),如果當(dāng)前事務(wù)存在,則拋出異常

// 示例7:
@Transactional(propagation = Propagation.REQUIRED)
public void main(){
    insertA();  // 插入A 
    service.sub();   // 調(diào)用其他方法
}
// 兩個(gè)Service中調(diào)用,如果同一個(gè)要注意不能用this調(diào)用,事務(wù)不會(huì)起作用
@Transactional(propagation = Propagation.NEVER)
public void sub(){
    insertB();  //插入B
    insertC();  //調(diào)用C

sub因?yàn)槭荖ever所以是不會(huì)執(zhí)行直接拋出錯(cuò)誤,所以main的事務(wù)遇到異常直接回滾,所以A回滾無(wú)法插入,B、C不會(huì)插入。

7. Propagation.NESTED

如果當(dāng)前事務(wù)存在,則在嵌套(父子)事務(wù)中執(zhí)行,否則REQUIRED的操作一樣(開(kāi)啟一個(gè)事務(wù))

// 示例7:
@Transactional(propagation = Propagation.REQUIRED)
public void main(){
    insertA();  // 插入A 
    service.sub();   // 調(diào)用其他方法
    throw RuntimeException;     //發(fā)生異常拋出
}
// 兩個(gè)Service中調(diào)用,如果同一個(gè)要注意不能用this調(diào)用,事務(wù)不會(huì)起作用
@Transactional(propagation = Propagation.NESTED)
public void sub(){
    insertB();  //插入B
    insertC();  //調(diào)用C

這個(gè)是最需要理解的一種傳播機(jī)制,要理清楚嵌套(父子)事務(wù),main的是父事務(wù),sub是子事務(wù),main發(fā)生異常全部都會(huì)回滾。

結(jié)果:

A、B、C全部回滾

// 示例8:
@Transactional(propagation = Propagation.REQUIRED)
public void main(){
    insertA();  // 插入A 
    try {
   		 service.sub();   // 調(diào)用其他方法
	} catch (Exception e) {
	}
	insertD();
}
// 兩個(gè)Service中調(diào)用,如果同一個(gè)要注意不能用this調(diào)用,事務(wù)不會(huì)起作用
@Transactional(propagation = Propagation.NESTED)
public void sub(){
    insertB();  //插入B
    throw RuntimeException;     //發(fā)生異常拋出
    insertC();  //調(diào)用C

示例8,子事務(wù)發(fā)生異常拋出,但父事務(wù)catch掉了,那么這個(gè)時(shí)候main方法就相當(dāng)于正常執(zhí)行沒(méi)有發(fā)生異常,那么就只有子事務(wù)回滾。

結(jié)果:

A、D插入成功,B、C插入失敗

  • REQUIRED
    • 內(nèi)外同一個(gè)事務(wù),任何一個(gè)地方拋出異常全部一起回滾。
  • REQUIRED_NEW
    • 內(nèi)部開(kāi)啟一個(gè)新的事務(wù),外部事務(wù)回滾并不會(huì)影響內(nèi)部的事務(wù),而如果內(nèi)部事務(wù)拋出被catch也不會(huì)影響外部事務(wù)。

怎么樣快速記憶,七個(gè)分四組,221這樣記,兩個(gè)一對(duì)互相類(lèi)似

傳播類(lèi)型含義
group1Propagation.REQUIRED如果當(dāng)前已有事務(wù)則加入當(dāng)前事務(wù),否則開(kāi)啟新的事務(wù)
group1Propagation.REQUIRED_NEW無(wú)論當(dāng)前是否有事務(wù)都開(kāi)啟新的事務(wù)
group2Propagation.SUPPORTED如果當(dāng)前事務(wù)存在就加入事務(wù),否則以非事務(wù)運(yùn)行
group2Propagation.NOT_SUPPORTED始終以非事務(wù)方式執(zhí)行,如果當(dāng)前存在事務(wù),則掛起當(dāng)前事務(wù)
group3Propagation.NEVER不使用事務(wù),如果當(dāng)前事務(wù)存在,則拋出異常
group3Propagation.MANDATORY當(dāng)前存在事務(wù),則加入當(dāng)前事務(wù),如果當(dāng)前事務(wù)不存在,則拋出異常。
group4Propagation.NESTED父子(嵌套)事務(wù),父回滾全回滾,子回滾不影響父事務(wù)

3.具體案例

單純講案例比較枯燥,會(huì)覺(jué)得工作中什么情況會(huì)使用到呢,這邊就舉一個(gè)例子來(lái)講解一下。

在下單時(shí)候,我們最主要是寫(xiě)入訂單、然后添加積分,最后記錄日志

 @Service
   public class OrderServiceImpl implements OrderService{
        @Transactional
        public void placeOrder(OrderDTO orderDTO){
            try {
                pointService.addPoint(Point point);
            } catch (Exception e) {
               // 記錄錯(cuò)誤信息
            }
            //省略...
        }
        //省略...
   }
   @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){
            //省略...
        }
        //省略...
   }

下單的操作不會(huì)影響添加積分的操作,所以我們使用NESTED,下單只要成功,添加積分可以成功或失敗,失敗的話就錯(cuò)誤信息后續(xù)補(bǔ)償。而記錄日志我們可以有也可以沒(méi)有,就可以設(shè)置為NOT_SUPPORTED不開(kāi)啟事務(wù),使得事務(wù)的方法能盡可能的精簡(jiǎn),避免一個(gè)很大的事務(wù)方法。

總結(jié)

本文講解了Spring事務(wù)的七種傳播機(jī)制,我們可以根據(jù)具體的類(lèi)型,具體設(shè)置,避免事務(wù)的方法過(guò)于長(zhǎng),一個(gè)事務(wù)里面調(diào)用的庫(kù)表越多,就越有可能造成死鎖,所以我們要根據(jù)具體的需要拆分使用。

以上就是關(guān)于Spring @Transactional事務(wù)傳播機(jī)制詳解的詳細(xì)內(nèi)容,更多關(guān)于Spring @Transactional事務(wù)的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評(píng)論