最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

解決Spring事務@Transactional多層嵌套失效問題

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

場景

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

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

偽代碼如下:

1、AService實現(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?
        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ù)關聯(lián)其它數(shù)據(jù)
        if (StringUtils.isNotBlank(aCode)){
            setOtherData(param);
        }

        return aCode;
    }

    @Transactional(rollbackFor = Exception.class)
    public void setOtherData(TestAParam param) throws BaseException {
        // 其它關聯(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ù)空校驗與提醒處理
    }

}

2、BService 和 CService 實現(xiàn)類(CService實現(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ù)校驗

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

        // 其它操作

    }
}

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

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

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

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

二、事務失效原因

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

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

2.2 方法不是Public

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

2.3 未被 Spring 管理的Bean

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

2.4 當前類的調(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)代理來達到事物管理的目的,當前類調(diào)用的方法上面加 @Transactional 這個是沒有任何作用的,因為調(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 新開啟一個事務,那么新開的事務管用么?

答案是:不管用!

因為它們發(fā)生了自身調(diào)用,就調(diào)該類自己的方法,而沒有經(jīng)過 Spring 的代理類,默認只有在外部調(diào)用事務才會生效,這也是老生常談的經(jīng)典問題了。

還有就是在沒有指定事務傳播行為時,從源碼中可以看到默認是使用 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
    }    
}

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

三、Spring的事務傳播行為

Spring 事務的傳播行為說的是,當多個事務同時存在的時候, Spring 如何處理這些事務的行為。

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

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

四、解決思路

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

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

五、采取方法

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

偽代碼如下:

@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?
        this.judgeNull(param);
        // 編程式事務
        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ù)關聯(lián)其它數(shù)據(jù)
            if (StringUtils.isNotBlank(aCode)){
                setOtherData(param);
            }
            // 提交事務
            transactionManager.commit(status);
            return aCode;
        } catch (Exception e) {
            // 回滾事務
            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 {
        // 其它關聯(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ù)空校驗與提醒處理
    }

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

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

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

總結

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

相關文章

  • java實現(xiàn)左旋轉(zhuǎn)字符串

    java實現(xiàn)左旋轉(zhuǎn)字符串

    這篇文章主要為大家詳細介紹了java實現(xiàn)左旋轉(zhuǎn)字符串,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2019-03-03
  • springboot+thymeleaf 文件上傳功能的實現(xiàn)代碼

    springboot+thymeleaf 文件上傳功能的實現(xiàn)代碼

    這篇文章主要介紹了springboot+thymeleaf 文件上傳功能的實現(xiàn)代碼,代碼簡單易懂,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-11-11
  • SpringCloud Eureka Provider及Consumer的實現(xiàn)

    SpringCloud Eureka Provider及Consumer的實現(xiàn)

    這篇文章主要介紹了SpringCloud Eureka 提供者及調(diào)用者的實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2019-10-10
  • 從內(nèi)存方面解釋Java中String與StringBuilder的性能差異

    從內(nèi)存方面解釋Java中String與StringBuilder的性能差異

    我們通常會發(fā)現(xiàn)使用StringBuffer或StringBuilder創(chuàng)建出來的字符串在拼接時回避String要來得快,尤其是StringBuilder,本文就從內(nèi)存方面解釋Java中String與StringBuilder的性能差異,需要的朋友可以參考下
    2016-05-05
  • SpringBoot整合RabbitMQ的5種模式的注解綁定詳解

    SpringBoot整合RabbitMQ的5種模式的注解綁定詳解

    這篇文章主要介紹了SpringBoot整合RabbitMQ的5種模式的注解綁定詳解,RabbitMQ 是一個消息中間件,它接收消息并且轉(zhuǎn)發(fā),是"消費-生產(chǎn)者模型"的一個典型的代表,一端往消息隊列中不斷的寫入消息,而另一端則可以讀取或者訂閱隊列中的消息,需要的朋友可以參考下
    2024-01-01
  • spring boot使用RabbitMQ實現(xiàn)topic 主題

    spring boot使用RabbitMQ實現(xiàn)topic 主題

    本篇文章主要介紹了spring boot使用RabbitMQ實現(xiàn)topic 主題,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-03-03
  • 使用Spring Boot的LoggersEndpoint管理日志級別

    使用Spring Boot的LoggersEndpoint管理日志級別

    這篇文章主要為大家介紹了使用Spring Boot的LoggersEndpoint管理日志級別,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-11-11
  • Java編程時間日期API實例解析

    Java編程時間日期API實例解析

    本文主要介紹了Java編程時間日期API實例解析的相關內(nèi)容,分享了一則實例,具有一定借鑒價值,需要的朋友可以參考下。
    2018-01-01
  • Spring 異步執(zhí)行器(Executor)配置策略與命名實踐

    Spring 異步執(zhí)行器(Executor)配置策略與命名實踐

    這篇文章主要介紹了Spring 異步執(zhí)行器(Executor)配置策略與命名實踐,本文結合實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2026-02-02
  • Java使用鎖解決銀行取錢問題實例分析

    Java使用鎖解決銀行取錢問題實例分析

    這篇文章主要介紹了Java使用鎖解決銀行取錢問題,結合實例形式分析了java線程同步與鎖機制相關原理及操作注意事項,需要的朋友可以參考下
    2019-08-08

最新評論

古田县| 凌源市| 固阳县| 银川市| 明星| 永兴县| 延川县| 徐汇区| 广南县| 耿马| 普陀区| 兴化市| 宁乡县| 无为县| 东乡县| 上犹县| 南丰县| 天津市| 安顺市| 三原县| 来凤县| 德化县| 武穴市| 宁国市| 桦甸市| 黔东| 郴州市| 南投市| 四会市| 商都县| 白河县| 凤庆县| 五莲县| 玉田县| 洛宁县| 汉沽区| 赣州市| 兴仁县| 赤城县| 庆安县| 丹东市|