• Spring事务@Transactional 注解下,事务失效的七种场景


    Spring事务@Transactional 注解下,事务失效的七种场景

    1. 异常被捕获后没有抛出
      当异常被捕获后,并且没有再抛出,那么deleteUserA是不会回滚的。
    @Transactional
    public void deleteUser() {
        userMapper.deleteUserA();
        try {
            int i = 1 / 0;
            userMapper.deleteUserB();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    1. 抛出非运行时异常
      异步虽然抛出了,但是抛出的是非RuntimeException类型的异常,依旧不会生效。如果指定了回滚异常类型为Exception,那么就可以回滚非RuntimeException类型异常了。
      @Transactional(rollbackFor = Exception.class)
    @Transactional
    public void deleteUser() throws MyException{
        userMapper.deleteUserA();
        try {
            int i = 1 / 0;
            userMapper.deleteUserB();
        } catch (Exception e) {
            throw new MyException();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    1. 方法内部直接调用
      如果先调用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();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    修改方式,把当前类自己注入一下调用即可

    @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();
     }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    1. 新开启一个线程
      如下的方式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();    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    1. 注解到private方法上
      private修饰的方式,spring无法生成动态代理
    @Transactional
    private void deleteUser() throws MyException{
        userMapper.deleteUserA();
        int i = 1/0;
        userMapper.deleteUserB();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    1. 数据库本身不支持
      mysql数据库,必须设置数据库引擎为InnoDB。
    2. 事务传播属性设置错误
      注意传播属性的设置,比如设置了:PROPAGATION_NOT_SUPPORIED(以非事务的方式执行,如果当前有事务则把当前事务挂起
  • 相关阅读:
    Python 编程竟然如此幽默!揭秘程序员们的搞笑日常,快来看看吧!
    k8s篇之一、环境搭建
    ALM物联网管理平台助力中台上云 数字化转型让展示更直观清晰
    Unity UI Toolkit学习笔记-Visual Tree
    电脑系统升级Win11变卡顿?如何解决!
    油气田勘探数字化转型现状及展望
    Spring 6【p命名空间和c命名空间】(五)-全面详解(学习总结---从入门到深化)
    torch.nn中的L1Loss和MSELoss
    珠海航展有图扑 | 数字孪生方案助力智慧航天
    鸿蒙系统优缺点,能否作为开发者选择
  • 原文地址:https://blog.csdn.net/hcyxsh/article/details/133773261