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

解決Spring事務(wù)@Transactional多層嵌套失效問題

 更新時間:2024年11月08日 14:32:37   作者:Leo|Java Runner  
在使用Spring進(jìn)行事務(wù)管理時,可能會遇到事務(wù)失效的問題,主要原因包括數(shù)據(jù)庫不支持事務(wù)、方法訪問級別不是public、未被Spring管理的Bean、當(dāng)前類的方法內(nèi)部調(diào)用以及配置的事務(wù)傳播性不當(dāng)?shù)?解決事務(wù)失效的方法有使用聲明式事務(wù)處理采用合適的事務(wù)傳播行為

場景

在 AService 中,我會直接調(diào)用 A 的數(shù)據(jù)操作層去操作 A的數(shù)據(jù) 以及 A關(guān)聯(lián)密切的其它數(shù)據(jù),在操作完之后,會去調(diào)用 BService 和 CService 中更新對應(yīng)的數(shù)據(jù),并在每個方法上使用了事務(wù),但在調(diào)用 BService 或者 CService 時候出現(xiàn)了異常,此時出現(xiàn)異常的BService 或者 CService 中數(shù)據(jù)沒有改變,回滾了。

但在 AService 中調(diào)用的 update 方法和出現(xiàn)異常前已經(jīng)執(zhí)行完的方法執(zhí)行成功并且沒有回滾。

偽代碼如下:

1、AService實(shí)現(xiàn)類

@Service
@Slf4j
public class AServiceImpl implements IAService {
    private final IBService bService;
    private final ICService cService;
    public AServiceImpl(IBService bService, ICService cService) {
        this.bService = bService;
        this.cService = cService;
    }
    
    @Override
    @Transactional(rollbackFor = Exception.class)
    public String modifyA(TestAParam param) throws BaseException {
        // 兜底:入?yún)⒖兆侄涡r?yàn)
        this.judgeNull(param);

        TestModifyParam modifyParam = param.getModifyParam();

        String aCode = update(param, Boolean.TRUE);

        if (null != modifyParam.getStatus() && TestStatusConstant.EDITED.equals(modifyParam.getStatus())){
            // 保存B信息
            bService.saveInfo(param, aCode);
            // 保存C信息
            cService.saveInfo(param, aCode);
        }

        // 更新A數(shù)據(jù)關(guān)聯(lián)其它數(shù)據(jù)
        if (StringUtils.isNotBlank(aCode)){
            setOtherData(param);
        }

        return aCode;
    }

    @Transactional(rollbackFor = Exception.class)
    public void setOtherData(TestAParam param) throws BaseException {
        // 其它關(guān)聯(lián)數(shù)據(jù)處理
    }

    @Transactional(rollbackFor = Exception.class)
    public String update(TestAParam param, Boolean directlyFlag) throws BaseException {
        // 更新處理
       return null;
    }

    @Transactional(rollbackFor = Exception.class)
    public void judgeNull(TestAParam param) throws BaseException {
        // 參數(shù)空校驗(yàn)與提醒處理
    }

}

2、BService 和 CService 實(shí)現(xiàn)類(CService實(shí)現(xiàn)類異常處理差不多相同)

@Service
@Slf4j
public class BServiceImpl implements IBService {
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveInfo(TestAParam param, String aCode) throws BaseException {
        // 數(shù)據(jù)校驗(yàn)

        // 模擬代碼,出現(xiàn)不匹配數(shù)據(jù)錯誤情況會拋出異常
        if (Objects.isNull(param)){
            throw new BaseException(CodeEnum.FAILED.getCode(),"BServiceImpl saveInfo param mistake");
        }

        // 其它操作

    }
}

一、Spring事務(wù)實(shí)現(xiàn)方式及原理

Spring 事務(wù)的本質(zhì)其實(shí)就是數(shù)據(jù)庫對事務(wù)的支持,沒有數(shù)據(jù)庫的事務(wù)支持,spring 是無法提供事務(wù)功能的。真正的數(shù)據(jù)庫層的事務(wù)提交和回滾是通過 binlog 或者 redo log 實(shí)現(xiàn)的。

一般我們在程序里面使用的都是在方法上面加 @Transaction 注解,這種屬于聲明式事物

聲明式事務(wù)本質(zhì)是通過 AOP 功能,對方法前后進(jìn)行攔截,將事務(wù)處理的功能編織到攔截的方法中,也就是在目標(biāo)方法開始之前加入一個事務(wù),在執(zhí)行完目標(biāo)方法之后根據(jù)執(zhí)行情況提交或者回滾事務(wù)。

二、事務(wù)失效原因

2.1 數(shù)據(jù)庫本身不支持事物

這里以 MySQL 為例,其 MyISAM 引擎是不支持事務(wù)操作的,InnoDB 才是支持事務(wù)的引擎,一般要支持事務(wù)都會使用 InnoDB。

2.2 方法不是Public

注解 @Transactional 只能放在 public 修飾的方法上才起作用private 方法是不會被spring代理的)因此是不會有事物產(chǎn)生的,這種做法是無效的。

2.3 未被 Spring 管理的Bean

沒有被spring管理的bean, spring連代理對象都無法生成,事務(wù)自然是無效的。

2.4 當(dāng)前類的調(diào)用

@Service
public class UserServiceImpl implements UserService {

    public void update(User user) {
        updateUser(user);
    }

    @Transactional(rollbackFor = Exception.class)
    public void updateUser(User user) {
        // update user
    }

}

上面的這種情況下是不會有事物管理操作的。

通過看聲明式事物的原理可知,spring使用的是AOP切面的方式,本質(zhì)上使用的是動態(tài)代理來達(dá)到事物管理的目的,當(dāng)前類調(diào)用的方法上面加 @Transactional 這個是沒有任何作用的,因?yàn)檎{(diào)用這個方法的是this。

再看下面的一種例子:

@Service
public class UserServiceImpl implements UserService {

    @Transactional(rollbackFor = Exception.class)
    public void update(User user) {
        updateUser(user);
    }

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void updateUser(User user) {
        // update user
    }

}

這次在 update 方法上加了 @Transactional,updateUser 加了 REQUIRES_NEW 新開啟一個事務(wù),那么新開的事務(wù)管用么?

答案是:不管用!

因?yàn)樗鼈儼l(fā)生了自身調(diào)用,就調(diào)該類自己的方法,而沒有經(jīng)過 Spring 的代理類,默認(rèn)只有在外部調(diào)用事務(wù)才會生效,這也是老生常談的經(jīng)典問題了。

還有就是在沒有指定事務(wù)傳播行為時,從源碼中可以看到默認(rèn)是使用 Propagation.REQUIRED。

2.5 配置的事物傳播性有問題

@Service
public class UserServiceImpl implements UserService {

    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    public void update(User user) {
        // update user
    }    
}

2.6 異常被你 "抓住"了

@Service
public class UserServiceImpl implements UserService {

    @Transactional(rollbackFor = Exception.class)
    public void update(User user) {

      try{
        // update user
      }catch(Execption e){
         log.error("異常",e)
      }
    }    
}

異常被抓了,這樣子代理類就沒辦法知道你到底有沒有錯誤,需不需要回滾,所以這種情況也是沒辦法回滾。

2.7 rollbackFor 異常指定錯誤

@Service
public class UserServiceImpl implements UserService {

    @Transactional
    public void update(User user) {
        // update user
    }    
}

上面這種沒有指定回滾異常,這個時候默認(rèn)的回滾異常是 RuntimeException ,如果出現(xiàn)其他異常那么就不會回滾事物。

三、Spring的事務(wù)傳播行為

Spring 事務(wù)的傳播行為說的是,當(dāng)多個事務(wù)同時存在的時候, Spring 如何處理這些事務(wù)的行為。

類型說明
PROPAGATION_REQUIRED如果當(dāng)前沒有事務(wù),就創(chuàng)建一個新事務(wù),如果當(dāng)前存在事務(wù),就加入該事務(wù),該設(shè)置是最常用的設(shè)置。
PROPAGATION_SUPPORTS支持當(dāng)前事務(wù),如果當(dāng)前存在事務(wù),就加入該事務(wù),如果當(dāng)前不存在事務(wù),就以非事務(wù)執(zhí)行。
PROPAGATION_MANDATORY支持當(dāng)前事務(wù),如果當(dāng)前存在事務(wù),就加入該事務(wù),如果當(dāng)前不存在事務(wù),就拋出異常。
PROPAGATION_REQUIRES_NEW創(chuàng)建新事務(wù),無論當(dāng)前存不存在事務(wù),都創(chuàng)建新事務(wù)。
PROPAGATION_NOT_SUPPORTED以非事務(wù)方式執(zhí)行操作,如果當(dāng)前存在事務(wù),就把當(dāng)前事務(wù)掛起。
PROPAGATION_NEVER以非事務(wù)方式執(zhí)行,如果當(dāng)前存在事務(wù),則拋出異常。
PROPAGATION_NESTED如果當(dāng)前存在事務(wù),則在嵌套事務(wù)內(nèi)執(zhí)行。如果當(dāng)前沒有事務(wù),則按 REQUIRED 屬性執(zhí)行。

當(dāng)傳播行為設(shè)置了PROPAGATION_NOT_SUPPORTED,PROPAGATION_NEVER,PROPAGATION_SUPPORTS這三種時,就有可能存在事物不生效。

四、解決思路

1、聲明式事務(wù)處理,采用合適的事務(wù)傳播行為,將AService中修改A中數(shù)據(jù)方法update和更新A數(shù)據(jù)關(guān)聯(lián)其它數(shù)據(jù)方法setOtherData通過AppContext.getBean()的方式直接獲取AService中的方法,不違背事務(wù)失效原因中的2.4項(xiàng)(當(dāng)前類的調(diào)用),并將保持B和C信息的方法單獨(dú)抽取出來,提供一個saveBandCInfo方法,加上事務(wù)處理和傳播行為,并拋出異常信息。

2、使用編程式事務(wù)管理,手動配置事務(wù)邊界,確保modifyA中所有方法在事務(wù)中執(zhí)行。

五、采取方法

由于AService中的方法 modifyA 調(diào)用鏈路比較長(業(yè)務(wù)急需處理好),如果使用聲明式事務(wù)處理,改動起來是比較大的,中間鏈路可能會存在事務(wù)傳播行為失效的情況,此時使用編程式事務(wù)管理解決就會很明顯輕松解決。(這方法并不建議大家日常使用,建議使用聲明事務(wù)更好點(diǎn))

偽代碼如下:

@Service
@Slf4j
public class AServiceImpl implements IAService {
    private final IBService bService;
    private final ICService cService;
    private final PlatformTransactionManager transactionManager;
    public AServiceImpl(IBService bService, ICService cService, PlatformTransactionManager transactionManager) {
        this.bService = bService;
        this.cService = cService;
        this.transactionManager = transactionManager;
    }
    
    @Override
    @Transactional(rollbackFor = Exception.class)
    public String modifyA(TestAParam param) throws BaseException {
        // 兜底:入?yún)⒖兆侄涡r?yàn)
        this.judgeNull(param);
        // 編程式事務(wù)
        DefaultTransactionDefinition def = new DefaultTransactionDefinition();
        TransactionStatus status = transactionManager.getTransaction(def);
        try {
            TestModifyParam modifyParam = param.getModifyParam();
            String aCode = update(param, Boolean.TRUE);
            if (null != modifyParam.getStatus() && TestStatusConstant.EDITED.equals(modifyParam.getStatus())){
                // 保存B信息
                bService.saveInfo(param, aCode);
                // 保存C信息
                cService.saveInfo(param, aCode);
            }
            // 更新A數(shù)據(jù)關(guān)聯(lián)其它數(shù)據(jù)
            if (StringUtils.isNotBlank(aCode)){
                setOtherData(param);
            }
            // 提交事務(wù)
            transactionManager.commit(status);
            return aCode;
        } catch (Exception e) {
            // 回滾事務(wù)
            transactionManager.rollback(status);
            // 處理異?;蚋鶕?jù)需要重新拋出異常
            throw new BaseException(CodeEnum.FAILED.getCode(),"modifyA error message is {}", e.getMessage());
        }
            
    }

    @Transactional(rollbackFor = Exception.class)
    public void setOtherData(TestAParam productStrategyDO) throws BaseException {
        // 其它關(guān)聯(lián)數(shù)據(jù)處理
    }

    @Transactional(rollbackFor = Exception.class)
    public String update(TestAParam param, Boolean directlyFlag) throws BaseException {
        // 更新處理
        return null;
    }

    @Transactional(rollbackFor = Exception.class)
    public void judgeNull(TestAParam param) throws BaseException {
        // 參數(shù)空校驗(yàn)與提醒處理
    }

上述代碼使用了 PlatformTransactionManager接口的實(shí)現(xiàn)來手動管理事務(wù)。

在代碼中,我們首先獲取 transactionManager的實(shí)例,然后使用該實(shí)例手動創(chuàng)建事務(wù)定義和事務(wù)狀態(tài)。在try塊中執(zhí)行update方法和其它方法,并在最后根據(jù)執(zhí)行情況手動提交或回滾事務(wù)。

通過這種方式,可以確保update方法的操作在事務(wù)中進(jìn)行,且在其它方法中發(fā)生異常時能夠回滾。如果采取這個方式請確保在相應(yīng)的配置類中將transactionManager正確配置為適用于你的應(yīng)用程序的事務(wù)管理器實(shí)現(xiàn)。

總結(jié)

以上為個人經(jīng)驗(yàn),希望能給大家一個參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • 淺談Java中的n種隨機(jī)數(shù)產(chǎn)生辦法

    淺談Java中的n種隨機(jī)數(shù)產(chǎn)生辦法

    眾所周知,隨機(jī)數(shù)是任何一種編程語言最基本的特征之一。而生成隨機(jī)數(shù)的基本方式也是相同的:產(chǎn)生一個0到1之間的隨機(jī)數(shù)??此坪唵危袝r我們也會忽略了一些有趣的功能。
    2015-09-09
  • Java中Scanner的常用方法總結(jié)(一次學(xué)懂)

    Java中Scanner的常用方法總結(jié)(一次學(xué)懂)

    這篇文章主要給大家介紹了關(guān)于Java中Scanner常用方法的相關(guān)資料,Java中的Scanner是一個用于讀取用戶輸入的類,它可以讀取各種類型的數(shù)據(jù),包括整數(shù)、浮點(diǎn)數(shù)、字符串等等,需要的朋友可以參考下
    2023-11-11
  • 淺談Java轉(zhuǎn)義符\\|

    淺談Java轉(zhuǎn)義符\\|

    java中\(zhòng)需要用\\來表示吧這個你應(yīng)該知道,而split中傳入的參數(shù)是什么呢 他并不是普通的字符串 你可以查一下api文檔 它要求傳入的是正則表達(dá)式 而正則表達(dá)式也需要這個\所以在這里有這個\\
    2015-06-06
  • Java中生成唯一ID的方法示例

    Java中生成唯一ID的方法示例

    這篇文章主要介紹了Java中生成唯一ID的方法示例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-02-02
  • 使用java生成字母驗(yàn)證碼

    使用java生成字母驗(yàn)證碼

    這篇文章主要介紹了使用java生成字母驗(yàn)證碼的相關(guān)資料,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2016-12-12
  • springboot學(xué)習(xí)之構(gòu)建簡單項(xiàng)目搭建步驟詳解

    springboot學(xué)習(xí)之構(gòu)建簡單項(xiàng)目搭建步驟詳解

    這篇文章主要介紹了springboot學(xué)習(xí)之構(gòu)建簡單項(xiàng)目搭建步驟詳解,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-10-10
  • 詳解JVM 中的StringTable

    詳解JVM 中的StringTable

    這篇文章主要介紹了JVM 中的StringTable,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2021-03-03
  • java修改JFrame默認(rèn)字體方式

    java修改JFrame默認(rèn)字體方式

    這篇文章主要介紹了java修改JFrame默認(rèn)字體方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-11-11
  • 淺談SpringSecurity重寫默認(rèn)配置

    淺談SpringSecurity重寫默認(rèn)配置

    這篇文章主要介紹了SpringSecurity重寫默認(rèn)配置,包括注入Bean、擴(kuò)展WebSecurityConfigurerAdapter、重寫端點(diǎn)授權(quán)配置及實(shí)現(xiàn)AuthenticationProvider,感興趣的可以了解一下
    2025-01-01
  • java?hutool工具類處理JSON的使用方法

    java?hutool工具類處理JSON的使用方法

    hutool是一個java基礎(chǔ)工具類,該工具類經(jīng)過長期的發(fā)展,API已經(jīng)非常齊全,下面這篇文章主要給大家介紹了關(guān)于java?hutool工具類處理JSON的使用方法,需要的朋友可以參考下
    2024-04-04

最新評論