溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

怎么使用@Transactional設(shè)置嵌套事務(wù)不回滾

發(fā)布時間:2021-07-30 18:13:23 來源:億速云 閱讀:538 作者:chen 欄目:開發(fā)技術(shù)

本篇內(nèi)容介紹了“怎么使用@Transactional設(shè)置嵌套事務(wù)不回滾”的有關(guān)知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!

@Transactional 設(shè)置嵌套事務(wù)不回滾

@Transactional(rollbackFor = Exception.class)
    public void testA(RequestSchedulingVO requestSchedulingVO) {
     ...業(yè)務(wù)...
        BService.testB(param);
    }
@Override
    @Transactional(propagation = Propagation.REQUIRES_NEW, readOnly = false, noRollbackFor = BusinessException.class)
//    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    public void testB(Long param) throws BusinessException {
        ...業(yè)務(wù)...
        try{
        }cach(Exception e){
         throw new BusinessException(BaiduFaceErrorCode.FACE_CREATE_CONVERSATION_ERROR);
        }
    }

這里的事務(wù)要求是方法testB報錯(BusinessException異常)時,testB事務(wù)不回滾,testA事務(wù)回滾.

方案一:

testB設(shè)置事務(wù)為REQUIRES_NEW(創(chuàng)建一個新的事務(wù),如果當(dāng)前存在事務(wù),則把當(dāng)前事務(wù)掛起),并設(shè)置不回滾的異常類

方案二:

testB設(shè)置事務(wù)為NOT_SUPPORTED(以非事務(wù)方式運行,如果當(dāng)前存在事務(wù),則把當(dāng)前事務(wù)掛起)。

@Transactional回滾問題(try catch、嵌套)

Spring 事務(wù)注解 @Transactional 本來可以保證原子性,如果事務(wù)內(nèi)有報錯的話,整個事務(wù)可以保證回滾,但是加上try catch或者事務(wù)嵌套,可能會導(dǎo)致事務(wù)回滾失敗。測試一波。

準(zhǔn)備

建兩張表,模擬兩個數(shù)據(jù)操作

CREATE TABLE `user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(20) DEFAULT NULL,
  `age` smallint(3) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
 
CREATE TABLE `role` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `role_name` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;

測試

根據(jù)排列組合原理,我們進(jìn)行四種測試:1、無try catch、無嵌套;2、有try catch、無嵌套;3、無try catch、有嵌套;4、都有。

最簡單測試

如果我們單純@Transactional,事務(wù)可以正常回滾嗎?

@GetMapping("/saveNormal0")
    @Transactional
    public void saveNormal0() throws Exception {
        int age = random.nextInt(100);
        User user = new User().setAge(age).setName("name:"+age);
        userService.save(user);
        throw new RuntimeException();
    }

如果事務(wù)內(nèi)報了RuntimeException錯誤,事務(wù)可以回滾。

@GetMapping("/saveNormal0")
    @Transactional
    public void saveNormal0() throws Exception {
        int age = random.nextInt(100);
        User user = new User().setAge(age).setName("name:"+age);
        userService.save(user);
        throw new Exception();
    }

如果事務(wù)內(nèi)報了Exception錯誤(非RuntimeException錯誤),事務(wù)不可以回滾。

@GetMapping("/saveNormal0")
    @Transactional( rollbackFor = Exception.class)
    public void saveNormal0() throws Exception {
        int age = random.nextInt(100);
        User user = new User().setAge(age).setName("name:"+age);
        userService.save(user);
        throw new Exception();
    }

如果是Exception錯誤(非RuntimeException),加上 rollbackFor = Exception.class 參數(shù)也可以實現(xiàn)回滾。

結(jié)論一:對于@Transactional可以保證RuntimeException錯誤的回滾,如果想保證非RuntimeException錯誤的回滾,需要加上rollbackFor = Exception.class 參數(shù)。

try catch 影響

經(jīng)過博主多種情況測試,發(fā)現(xiàn)try catch對回滾這個事本身沒有什么影響,結(jié)論一照樣成立。try catch只是對異常是否可以被@Transactional 感知 到有影響。如果錯誤拋到切面可以感知到的地步,那就可以起作用。

@GetMapping("/saveTryCatch")
    @Transactional( rollbackFor = Exception.class)
    public void saveTryCatch() throws Exception{
        try{
            int age = random.nextInt(100);
            User user = new User().setAge(age).setName("name:"+age);
            userService.save(user);
            throw new Exception();
        }catch (Exception e){
            throw e;
        }
    }

比如上面一段代碼就回滾了。

@GetMapping("/saveTryCatch")
    @Transactional( rollbackFor = Exception.class)
    public void saveTryCatch() throws Exception{
        try{
            int age = random.nextInt(100);
            User user = new User().setAge(age).setName("name:"+age);
            userService.save(user);
            throw new Exception();
        }catch (Exception e){
        }
    }

然而,將catch中的錯誤不繼續(xù)網(wǎng)上拋,切面無法感知到錯誤,無法進(jìn)行處理,那么事務(wù)就無法回滾了。

結(jié)論二:try catch只是對異常是否可以被@Transactional 感知 到有影響。如果錯誤拋到切面可以感知到的地步,那就可以起作用。

事務(wù)嵌套 影響

首先經(jīng)過實驗,結(jié)論一仍然成立,即,當(dāng)不加上rollbackFor = Exception.class 的時候,無論內(nèi)外報RuntimeException,都會回滾;無論內(nèi)外報 非RuntimeException 錯誤,都不會回滾。如果加上rollbackFor = Exception.class,無論內(nèi)外怎么報錯,都會回滾。這些代碼就不給出了。接下來,試下下面兩種情況:

   @GetMapping("/out")
    @Transactional( rollbackFor = Exception.class)
    public void out() throws Exception{
        innerService.inner();
        int age = random.nextInt(100);
        User user = new User().setAge(age).setName("name:" + age);
        userService.save(user);
        throw new Exception();
    }
    @Transactional
    public void inner() throws Exception{
        Role role = new Role();
        role.setRoleName("roleName:"+new Random().nextInt(100));
        roleService.save(role);
//        throw new Exception();
    }

情況一,外面事務(wù)加上rollbackFor = Exception.class,里面事務(wù)不加,測試內(nèi)外分別報錯的情況(為了簡化代碼量,只給出了外面報錯的代碼),都可以回滾。因為,無論如何,錯誤都拋給了外面那個事務(wù)進(jìn)行處理,而外面那個加上了rollbackFor = Exception.class,具備處理非RuntimeException錯誤的能力,所以都可以讓事務(wù)進(jìn)行正常回滾。

下面看情況二,里面的事務(wù)加上rollbackFor = Exception.class,外面不加,外面報錯。

   @GetMapping("/out")
    @Transactional
    public void out() throws Exception{
        innerService.inner();
        int age = random.nextInt(100);
        User user = new User().setAge(age).setName("name:" + age);
        userService.save(user);
        throw new Exception();
    }
    
    @Transactional( rollbackFor = Exception.class)
    public void inner() throws Exception{
        Role role = new Role();
        role.setRoleName("roleName:"+new Random().nextInt(100));
        roleService.save(role);
    }

事務(wù)都無法回滾,這是我們有個疑問,里面的事務(wù)明明有很強(qiáng)的處理能力啊,為什么和外面一起回滾失敗呢,別著急,等等聊這個。

然后試下里面報錯:

@GetMapping("/out")
    @Transactional
    public void out() throws Exception{
        innerService.inner();
        int age = random.nextInt(100);
        User user = new User().setAge(age).setName("name:" + age);
        userService.save(user);
    }
     @Transactional( rollbackFor = Exception.class)
    public void inner() throws Exception{
        Role role = new Role();
        role.setRoleName("roleName:"+new Random().nextInt(100));
        roleService.save(role);
        throw new Exception();
    }

咦,這回都進(jìn)行了正常的回滾。我的天,這回外面沒有處理能力,為什么接受里面拋出來的錯誤,也進(jìn)行了回滾?。。】瓷先?,就好像里外事務(wù)總是同生共死的對不對?原來,@Transactional還有個參數(shù),看下源碼,這個注解還有默認(rèn)值:

Propagation propagation() default Propagation.REQUIRED;

REQUIRED的意思是說,事務(wù)嵌套的時候,如果發(fā)現(xiàn)已經(jīng)有事務(wù)存在了,就加入這個事務(wù),而不是新建一個事務(wù),所以根本就不存在兩個事務(wù),一直只有一個!至于,此參數(shù)其他值,本文不進(jìn)行測試?;氐缴厦娴膯栴},當(dāng)外面報錯的時候,此時查看事務(wù),沒有增加rollbackFor = Exception.class參數(shù),即沒有處理非RuntimeException能力,所以代碼走完,貌似“兩個事務(wù)”,都回滾失敗了。當(dāng)里面報錯的時候,事務(wù)已經(jīng)添加上了處理非RuntimeException能力,所以,代碼走完就回滾成功了。

結(jié)論三:由于REQUIRED屬性,“兩個事務(wù)”其實是一個事務(wù),處理能力看報錯時刻,是否添加了處理非RuntimeException的能力。

try catch和事務(wù)嵌套 共同影響

在結(jié)論一二三成立的條件下,探索共同影響的問題就簡單多了,由于情況太多,就不進(jìn)行過多的代碼展示了。

結(jié)論

結(jié)論一:

對于@Transactional可以保證RuntimeException錯誤的回滾,如果想保證非RuntimeException錯誤的回滾,需要加上rollbackFor = Exception.class 參數(shù)。

結(jié)論二:

try catch只是對異常是否可以被@Transactional 感知 到有影響。如果錯誤拋到切面可以感知到的地步,那就可以起作用。

結(jié)論三:

由于REQUIRED屬性,“兩個事務(wù)”其實是一個事務(wù),處理能力看報錯時刻,是否添加了處理非RuntimeException的能力。

“怎么使用@Transactional設(shè)置嵌套事務(wù)不回滾”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識可以關(guān)注億速云網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實用文章!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI