@Transactional注解事務失效的八種原因分析


@Transactional是一種基於注解管理事務的方式,spring通過動態代理的方式為目標方法實現事務管理的增強。

@Transactional使用起來方便,但也需要注意引起@Transactional失效的場景,本文總結了七種情況,下面進行逐一分析。

1、異常被捕獲后沒有拋出

當異常被捕獲后,並且沒有再拋出,那么deleteUserA是不會回滾的。

@Transactional
public void deleteUser() {
    userMapper.deleteUserA();
    try {
        int i = 1 / 0;
        userMapper.deleteUserB();
    } catch (Exception e) {
        e.printStackTrace();
    }
}

2、拋出非運行時異常

異步雖然拋出了,但是拋出的是非RuntimeException類型的異常,依舊不會生效。

@Transactional
public void deleteUser() throws MyException{
    userMapper.deleteUserA();
    try {
        int i = 1 / 0;
        userMapper.deleteUserB();
    } catch (Exception e) {
        throw new MyException();
    }
}

如果指定了回滾異常類型為Exception,那么就可以回滾非RuntimeException類型異常了。

@Transactional(rollbackFor = Exception.class)

3、方法內部直接調用

如果先調用deleteUser(),那么deleteUserA()是不會回滾的,其原因就是@Transactional根本沒生成代理,如果直接調用deleteUser2()那么沒問題,deleteUserA()會回滾。

public void deleteUser() throws MyException{
    deleteUser2();
}

@Transactional
public void deleteUser2() throws MyException{
    userMapper.deleteUserA();
    int i = 1 / 0;
    userMapper.deleteUserB();
}

修改方式,把當前類自己注入一下調用即可。

@Service
public class UserService {
    @Autowired
    private UserMapper userMapper;
    //自己注入自己
    @Autowired
    UserService userService;

    public void deleteUser() throws MyException{
        userService.deleteUser2();
    }

    @Transactional
    public void deleteUser2() throws MyException{
        userMapper.deleteUserA();
        int i = 1 / 0;
        userMapper.deleteUserB();
    }
}

4、新開啟一個線程

如下的方式deleteUserA()也不會回滾,因為spring實現事務的原理是通過ThreadLocal把數據庫連接綁定到當前線程中,新開啟一個線程獲取到的連接就不是同一個了。

@Transactional
public void deleteUser() throws MyException{
    userMapper.deleteUserA();
    try {
        //休眠1秒,保證deleteUserA先執行
        Thread.sleep(1000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    new Thread(() -> {
        int i = 1/0;
        userMapper.deleteUserB();
    }).start();    
}

5、注解到private方法上

idea直接會給出提示Methods annotated with ‘@Transactional’ must be overridable ,原理很簡單,private修飾的方式,spring無法生成動態代理。

@Transactional
private void deleteUser() throws MyException{
    userMapper.deleteUserA();
    int i = 1/0;
    userMapper.deleteUserB();
}

6、數據庫本身不支持

mysql數據庫,必須設置數據庫引擎為InnoDB。

7、事務傳播屬性設置錯誤

注意傳播屬性的設置,比如設置了:PROPAGATION_NOT_SUPPORIED(以非事務的方式執行,如果當前有事務則把當前事務掛起)。

8、操作數據庫使用了truncate關鍵字

整個業務里面只要有 truncate 就會導致事務回滾失效


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM