• Spring事务失效常见的八种场景


    1. 方法自调用

    第一类,@Transactional注解未生效情况,其实这种并不是事物失效,仅仅是注解失效,注解写了更没写一样

    解决方案:

    • 将被调用的方法拆到单独的bean中,让切面起作用
    • 自己注入自己获取代理类
    • @EnableAspectJAutoProxy(exposeProxy = true) + UserService userService = (UserService) AopContext.currentProxy();

    Spring事务是基于AOP的,只有当代理对象调用某个方法时,Spring的事务才会生效,而在一个方法中调用this.xxx()方法时,由于this并不是代理对象,所以会导致事务失效

    @Transactional
    public void a(){
        jdbcTemplate.execute("insert into t1 values(1)");
        throw new RuntimeException();
    }
    
    // 自调用
    public void b(){
        // 由于调用者并不是代理对象,所以切面失败导致事物失效
        a();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    还有一种情况

    // 此时相当于`a`方法上面根本就没贴事物注解一样
    @Transactional(propagation = Propagation.NEVER)
    public void a(){
        jdbcTemplate.execute("insert into t1 values(1)");
        throw new RuntimeException();
    }
    
    // 调用方法也加了事物注解,此时会进行回滚,但是是由b方法的事物就行回滚的
    @Transactional
    public void b(){
        // 由于调用者并不是代理对象,所以切面失败导致事物失效
        a();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    所以解决方案就是用代理对象调用即可

    public class UserService {
        @Autowired // 自己注入自己
        private UserService userService;
        @Transactional
        public void a(){
            jdbcTemplate.execute("insert into t1 values(1)");
            throw new RuntimeException();
        }
    
        // 事物生效
        public void b(){
            userService.a();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    或者可以通过代理上下文获取当前类的代理对象,但是这种需要我们在配置类上面设置属性@EnableAspectJAutoProxy(exposeProxy = true)

    public class UserService {
        @Transactional
        public void a(){
            jdbcTemplate.execute("insert into t1 values(1)");
            throw new RuntimeException();
        }
    
        // 事物生效
        public void b(){
            // 获取当前类的代理对象
            UserService userService = (UserService) AopContext.currentProxy();
            userService.a();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    2. 方法修饰符为private

    • cglib代理是基于父子类的,如果方法私有无法在子类中获取到,无法重写,只能执行父类的方法,但是父类中没有jdbcTemplate,会抛出NPE
    • 多态口诀:new谁调用谁的方法,谁创建使用谁的属性
    public class UserService {
        // cglib代理是基于父子类的,如果方法私有无法在子类中获取到,只能执行父类的方法,但是父类中没有`jdbcTemplate`,`NPE`
        @Transactional
        private void a(){
            jdbcTemplate.execute("insert into t1 values(1)");
            throw new RuntimeException();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    3. 方法是final的

    同上,代理类无法重写代理的方法

    4. 单独的线程调用

    Mybatis或者JdbcTemplate执行SQL时,会从ThreadLocal中去获取数据库连接对象,如果开启事物的线程和执行SQL的线程不是同一个线程,就拿不到数据库连接对象,这样,Mybatis或者JdbcTemplate就会重新创建一个数据库连接用来执行SQL,如果这个事物自动事物提交autocommit默认开启的话,就不会因为异常而进行回滚

    public class UserService {
        // 事物失效
        @Transactional
        private void a(){
            new Thread(() -> {
                jdbcTemplate.execute("insert into t1 values(1)");
            	throw new RuntimeException();
            }).start();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    5. Spring中没加@Configuration注解

    在Spring中,由于MybatisJdbcTemplate会从ThreadLocal中获取数据库连接,但是ThreadLocal里面存的是map,即ThreadLocal>的结构

    如果没有添加@Configuration注解,会导致Map中存的DateSource和MybatisJdbcTemplate对象不相等,从而拿不到数据库连接,导致自己会新建一个,从而导致事物失效

    注意:在SpringBoot中由于自动装箱,不会出现这种问题

    6. 异常被吃掉

    Spring默认只会对RuntimeExceptionError进行回滚

    7. 类没有被Spring管理

    8. 数据库没有开启事物

  • 相关阅读:
    OpenYurt环境搭建(遇到的各种坑和解决办法)
    不需要标注数据的语义分割!ETH&鲁汶大学提出MaskDistill,用Transformer来进行无监督语义分割,SOTA!...
    Momentum Contrast for Unsupervised Visual Representation Learning 论文学习
    单片机---1MHz方波的产生(中断和查询方式)
    uniapp 使用mqtt 报错 socketTask onOpen is not a function
    Python分析并绘制可视化动态地图,实时查询全球疫情数据(11月最新...)
    深度学习之NLP
    vue pc项目 router.js 配置详解
    计算机中丢失缺少mfc100.dll文件该如何解决?
    机器学习笔记 十四:k-近邻算法(kNN)的实现
  • 原文地址:https://blog.csdn.net/fengxiandada/article/details/127949583