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

Spring中使用事務(wù)嵌套時(shí)需要警惕的問題分享

 更新時(shí)間:2023年04月23日 08:59:04   作者:JAVA旭陽  
最近項(xiàng)目上有一個(gè)使用事務(wù)相對(duì)復(fù)雜的業(yè)務(wù)場景報(bào)錯(cuò)了。在絕大多數(shù)情況下,都是風(fēng)平浪靜,沒有問題。其實(shí)內(nèi)在暗流涌動(dòng),在有些異常情況下就會(huì)報(bào)錯(cuò),這種偶然性的問題很有可能就會(huì)在暴露到生產(chǎn)上造成事故,那究竟是怎么回事呢?本文就來簡單講講

前言

最近項(xiàng)目上有一個(gè)使用事務(wù)相對(duì)復(fù)雜的業(yè)務(wù)場景報(bào)錯(cuò)了。在絕大多數(shù)情況下,都是風(fēng)平浪靜,沒有問題。其實(shí)內(nèi)在暗流涌動(dòng),在有些異常情況下就會(huì)報(bào)錯(cuò),這種偶然性的問題很有可能就會(huì)在暴露到生產(chǎn)上造成事故,那究竟是怎么回事呢?

問題描述

我們用一個(gè)簡單的例子模擬下,大家也可以看看下面這段代碼輸出的結(jié)果是什么。

1.在類SecondTransactionService定義一個(gè)簡單接口transaction2,插入一個(gè)用戶,同時(shí)必然會(huì)拋出錯(cuò)誤

@Override
@Transactional(rollbackFor = Exception.class)
public void transaction2() {
    System.out.println("do transaction2.....");
    User user = new User("tx2", "111", 18);
    // 插入一個(gè)用戶
    userService.insertUser(user);
    // 跑錯(cuò)了
    throw new RuntimeException();
}

2.在另外一個(gè)類FirstTransactionService定義一個(gè)接口transaction1,它調(diào)用transaction2方法,同時(shí)做了try catch處理

@Override
@Transactional(rollbackFor = Exception.class)
public void transaction1() {
    System.out.println("do transaction1 .......");
    try {
        // 調(diào)用另外一個(gè)事務(wù),try catch住
        secondTransactionService.transaction2();
    } catch (Exception e) {
        e.printStackTrace();
    }

    // 插入當(dāng)前用戶tx1
    User user = new User("tx1", "111", 18);
    userService.insertUser(user);
}

3.定義一個(gè)controller,調(diào)用transaction1方法

@GetMapping("/testNestedTx")
public String testNestedTx() {
    firstTransactionService.transaction1();
    return "success";
}

大家覺得調(diào)用這個(gè)http接口,最終數(shù)據(jù)庫插入的是幾條數(shù)據(jù)呢?

問題結(jié)果

正確答案是數(shù)據(jù)庫插入了0條數(shù)據(jù)。

同時(shí)控制臺(tái)也報(bào)錯(cuò)了,報(bào)錯(cuò)原因是:org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only

是否和你預(yù)想的一樣呢?你知道是為什么嗎?

原因追溯

其實(shí)原因很簡單,我們都知道,一個(gè)事務(wù)要么全成功提交事務(wù),要么失敗全部回滾。如果出現(xiàn)在一個(gè)事務(wù)中部分SQL要回滾,部分SQL要提交,這不就主打的一個(gè)”前后矛盾,精神分裂“嗎?

controller.testNestedTx() 
  || 
  / 
FirstTransactionService.transaction1()   REQUIRED隔離級(jí)別
       || 
       || 
       || 捕獲異常,提交事務(wù),出錯(cuò)啦
       / || 
FirstTransactionService.transaction2()   REQUIRED隔離級(jí)別
       || || 
       || 拋出異常,標(biāo)記事務(wù)為rollback only
       =======================

1.事務(wù)的隔離級(jí)別為REQUIRED,那么發(fā)現(xiàn)沒有事務(wù)開啟一個(gè)事務(wù)操作,有的話,就合并到這個(gè)事務(wù)中,所以transaction1()transaction2()是在同一個(gè)事務(wù)中。

2.transaction2()拋出異常,那么事務(wù)會(huì)被標(biāo)記為rollback only, 源碼如下所示:

3.transaction1()由于try catch 異常,正常運(yùn)行,想必就要可以提交事務(wù)了,在提交事務(wù)的時(shí)候,會(huì)檢查rollback標(biāo)記,如果是true, 這時(shí)候就會(huì)拋出上面的異常了。源碼如下圖所示:

這下,是不是很清楚知道報(bào)錯(cuò)的原因了,那想想該怎么處理呢?

解決之道

知道了根本原因之后,是不是解決的方案就很明朗了,我們可以通過調(diào)整事務(wù)的傳播方式分拆多個(gè)事務(wù)管理,或者讓一個(gè)事務(wù)"前后一致",做一個(gè)誠信的好事務(wù)。

try catch放到內(nèi)層事務(wù)中,也就是transaction2()方法中,這樣內(nèi)層事務(wù)會(huì)跟著外部事務(wù)進(jìn)行提交或者回滾。

@Override
    @Transactional(rollbackFor = Exception.class)
    public void transaction2() {
        try {
            System.out.println("do transaction2.....");
            User user = new User("tx2", "111", 18);
            userService.insertUser2(user);
            throw new RuntimeException();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

如果希望內(nèi)層事務(wù)拋出異常時(shí)中斷程序執(zhí)行,直接在外層事務(wù)的catch代碼塊中拋出e,這樣同一個(gè)事務(wù)就都會(huì)回滾。

如果希望內(nèi)層事務(wù)回滾,但不影響外層事務(wù)提交,需要將內(nèi)層事務(wù)的傳播方式指定為PROPAGATION_NESTED。PROPAGATION_NESTED基于數(shù)據(jù)庫savepoint實(shí)現(xiàn)的嵌套事務(wù),外層事務(wù)的提交和回滾能夠控制嵌內(nèi)層事務(wù),而內(nèi)層事務(wù)報(bào)錯(cuò)時(shí),可以返回原始savepoint,外層事務(wù)可以繼續(xù)提交。

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

前面提到了事務(wù)的傳播機(jī)制,我們?cè)倏炊加心膸追N。

  • PROPAGATION_REQUIRED:加入到當(dāng)前事務(wù)中,如果當(dāng)前沒有事務(wù),就新建一個(gè)事務(wù)。這是最常見的選擇,也是Spring中默認(rèn)采用的方式。
  • PROPAGATION_SUPPORTS:支持當(dāng)前事務(wù),如果當(dāng)前沒有事務(wù),就以非事務(wù)方式執(zhí)行。
  • PROPAGATION_MANDATORY :支持當(dāng)前事務(wù),如果當(dāng)前沒有事務(wù),就拋出異常。
  • PROPAGATION_REQUIRES_NEW:新建一個(gè)事務(wù),如果當(dāng)前存在事務(wù),把當(dā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ù),則進(jìn)行與PROPAGATION_REQUIRED類似的操作。

如何理解PROPAGATION_NESTED的傳播機(jī)制呢,和PROPAGATION_REQUIRES_NEW又有什么區(qū)別呢?我們用一個(gè)例子說明白。

  • 定義serviceA.methodA()PROPAGATION_REQUIRED修飾;
  • 定義serviceB.methodB()以表格中三種方式修飾;
  • methodA中調(diào)用methodB;

總結(jié)

在我的項(xiàng)目中之所以會(huì)報(bào)“rollback-only”異常的根本原因是代碼風(fēng)格不一致的原因。外層事務(wù)對(duì)錯(cuò)誤的處理方式是返回true或false來告訴上游執(zhí)行結(jié)果,而內(nèi)層事務(wù)是通過拋出異常來告訴上游(這里指外層事務(wù))執(zhí)行結(jié)果,這種差異就導(dǎo)致了“rollback-only”異常。大家也可以去review自己項(xiàng)目中的代碼,是不是也偷偷犯下同樣的錯(cuò)誤了。

到此這篇關(guān)于Spring中使用事務(wù)嵌套時(shí)需要警惕的問題分享的文章就介紹到這了,更多相關(guān)Spring事務(wù)嵌套內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • java中@ConfigurationProperties失效的問題解決

    java中@ConfigurationProperties失效的問題解決

    在Java開發(fā)中,使用@ConfigurationProperties注解讀取配置文件時(shí),如果配置類中的屬性設(shè)置為static,將無法正確讀取配置值,本文就來介紹一下具體解決方法,感興趣的可以了解一下
    2024-09-09
  • SpringBoot配置文件方式,在線yml文件轉(zhuǎn)properties

    SpringBoot配置文件方式,在線yml文件轉(zhuǎn)properties

    這篇文章主要介紹了SpringBoot配置文件方式,在線yml文件轉(zhuǎn)properties,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-07-07
  • SpringBoot Admin使用及心跳檢測原理分析

    SpringBoot Admin使用及心跳檢測原理分析

    這篇文章主要介紹了SpringBoot Admin使用及心跳檢測原理分析,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-11-11
  • 在Java中Scanner的用法總結(jié)

    在Java中Scanner的用法總結(jié)

    這篇文章主要介紹了在Java中Scanner的用法總結(jié),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-10-10
  • Java類的初始化實(shí)踐分析

    Java類的初始化實(shí)踐分析

    這篇文章主要介紹了Java類的初始化,結(jié)合具體實(shí)例形式較為詳細(xì)的分析了java類初始化的常見情況、操作技巧與相關(guān)注意事項(xiàng),需要的朋友可以參考下
    2019-07-07
  • java使用HttpSession實(shí)現(xiàn)QQ訪問記錄

    java使用HttpSession實(shí)現(xiàn)QQ訪問記錄

    這篇文章主要介紹了java使用HttpSession實(shí)現(xiàn)QQ的訪問記錄的相關(guān)資料,需要的朋友可以參考下
    2016-03-03
  • 為zookeeper配置相應(yīng)的acl權(quán)限

    為zookeeper配置相應(yīng)的acl權(quán)限

    這篇文章主要介紹了為zookeeper配置相應(yīng)的acl權(quán)限的相關(guān)實(shí)例,具有一定參考價(jià)值,需要的朋友可以了解下。
    2017-09-09
  • Java將對(duì)象寫入文件讀出_序列化與反序列化的實(shí)例

    Java將對(duì)象寫入文件讀出_序列化與反序列化的實(shí)例

    下面小編就為大家?guī)硪黄狫ava將對(duì)象寫入文件讀出_序列化與反序列化的實(shí)例。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-08-08
  • Java判斷時(shí)間段內(nèi)文件是否更新的方法

    Java判斷時(shí)間段內(nèi)文件是否更新的方法

    這篇文章主要介紹了Java判斷時(shí)間段內(nèi)文件是否更新的方法,通過實(shí)例形式講述了定時(shí)器、類加載器及線程等方法實(shí)現(xiàn)判斷文件更新的功能,具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2014-12-12
  • java如何根據(jù)提供word模板導(dǎo)出word文檔詳解

    java如何根據(jù)提供word模板導(dǎo)出word文檔詳解

    在日常的開發(fā)工作中,我們時(shí)常會(huì)遇到導(dǎo)出Word文檔報(bào)表的需求,比如公司的財(cái)務(wù)報(bào)表、醫(yī)院的患者統(tǒng)計(jì)報(bào)表、電商平臺(tái)的銷售報(bào)表等等,這篇文章主要給大家介紹了關(guān)于java如何根據(jù)提供word模板導(dǎo)出word文檔的相關(guān)資料,需要的朋友可以參考下
    2023-09-09

最新評(píng)論

宾川县| 手游| 天等县| 开封市| 方山县| 阳谷县| 施秉县| 依兰县| 巴东县| 芦山县| 绥江县| 博湖县| 文化| 富蕴县| 贵定县| 同德县| 敦煌市| 彭山县| 桓台县| 抚远县| 鞍山市| 额敏县| 潼南县| 江阴市| 乌拉特中旗| 万州区| 肇东市| 德阳市| 灵寿县| 仙桃市| 盐山县| 广水市| 太白县| 三门峡市| 瑞昌市| 连南| 浦城县| 高陵县| 多伦县| 顺昌县| 钦州市|