個人學習系列 - @Transactional失效的3種情況

周兆東發表於2021-11-30
面試的時候遇到過這個問題,當時一臉懵逼。現在記錄一下。。。

@Transactional失效場景

1. 在類內部呼叫呼叫類內部@Transactional標註的方法

1.1 定義一個錯誤的@Transactional標註實現,設定一個內部呼叫
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {

    @Resource
    UserMapper userMapper;

    @Override
    public void oneTest() {
        oneTestNoPublic();
    }

    @Transactional(rollbackFor = Exception.class)
    public void oneTestNoPublic(){
        // 插入一條資料
        int num = userMapper.insert(new User("小紅", "青島市", 18));
        if (num > 0){
            // 插入後製造一個錯誤
            System.out.println(1 / 0);
        }
        // 再次插入一條資料
        userMapper.insert(new User("小強", "煙臺市", 21));
    }
}
1.2 測試用例
@RestController
@RequestMapping("/transactional")
public class TransactionalTestController {

    @Resource
    IUserService userService;

    /**
     * 在類內部呼叫呼叫類內部@Transactional標註的方法
     */
    @GetMapping("/one")
    public void oneTest(){
        try {
            userService.oneTest();
        } catch (Exception e){
            e.printStackTrace();
        }
    }

}
1.3 執行後,控制檯報錯:

image.png

1.4 檢視資料庫,發現並沒有回滾new User("小紅", "青島市", 18)這條資料

image.png

呼叫一個方法在類內部呼叫內部被@Transactional標註的事務方法,執行結果是事務不會正常開啟。userMapper.insert(new User("小紅", "青島市", 18)) 操作沒有進行回滾。

2. @Transactional註解標註方法修飾符為非public

2.1 新寫一個TestServiceImpl,將@Transactional註解標註方法修飾符為非public
@Service
public class TestServiceImpl {

    @Resource
    UserMapper userMapper;

    @Transactional(rollbackFor = Exception.class)
    void twoTestNoPublic(){
        // 插入一條資料
        int num = userMapper.insert(new User("小紅", "青島市", 18));
        if (num > 0){
            // 插入後製造一個錯誤
            System.out.println(1 / 0);
        }
        // 再次插入一條資料
        userMapper.insert(new User("小強", "煙臺市", 21));
    }
}
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {

    @Resource
    TwoTestServiceImpl twoTestService;

    @Override
    public void twoTest() {
        twoTestService.twoTestNoPublic();
    }
}
2.2 測試用例
@RestController
@RequestMapping("/transactional")
public class TransactionalTestController {

    @Resource
    IUserService userService;

    /**
     * @Transactional註解標註方法修飾符為非public
     */
    @GetMapping("/two")
    public void twoTest(){
        try {
            userService.twoTest();
        } catch (Exception e){
            e.printStackTrace();
        }
    }

}
2.3 執行後,控制檯報錯:

image.png

2.4 檢視資料庫,發現並沒有回滾new User("小紅", "青島市", 18)這條資料

image.png

以上的訪問方式,導致事務沒開啟,因此在方法丟擲異常時,userMapper.insert(new User("小紅", "青島市", 18)) 操作沒有進行回滾。如果oneTestNoPublic()方法改為public的話將會正常開啟事務,userMapper.insert(new User("小紅", "青島市", 18)) 將會進行回滾。

3. 事務方法內部捕捉了異常,且沒有丟擲新的異常

3.1 寫一個內部捕捉異常,且不再丟擲新的異常的方法
@Service
public class TestServiceImpl {

    @Resource
    UserMapper userMapper;

    @Transactional(rollbackFor = Exception.class)
    public void threeTestNoPublic(){
        // 插入一條資料
        int num = userMapper.insert(new User("小紅", "青島市", 18));
        if (num > 0){
            try {
                // 插入後製造一個錯誤
                System.out.println(1 / 0);
            } catch (Exception e) {
                e.printStackTrace();
                return;
            }
        }
        // 再次插入一條資料
        userMapper.insert(new User("小強", "煙臺市", 21));
    }
}
3.2 測試用例
@RestController
@RequestMapping("/transactional")
public class TransactionalTestController {

    @Resource
    IUserService userService;

    /**
     * 事務方法內部捕捉了異常,且沒有丟擲新的異常
     */
    @GetMapping("/three")
    public void threeTest(){
        try {
            userService.threeTest();
        } catch (Exception e){
            e.printStackTrace();
        }
    }

}
3.3 執行後,控制檯報錯:

image.png

3.4 檢視資料庫,發現並沒有回滾new User("小紅", "青島市", 18)這條資料

image.png

如果需要在catch裡面回滾資料的話,就需要丟擲新的異常或者在catch裡面使用如下程式碼:TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();

好了,上面三種就是@Transactional註解不起作用,@Transactional註解失效的主要原因。

個人部落格地址:

http://www.zhouzhaodong.xyz/

測試程式碼地址:

https://gitee.com/zhouzhaodon...

相關文章