36.从零开始学springboot-事务失效问题详解

前言

小编最近使用springboot事务时,发现了点问题

问题

ServiceA

    @Transactional
    public void addStudent() {
        Student student = new Student();
        student.setAge(20);
        student.setName("学生");
        studentMapper.insert(student);
        int i = 1 / 0;
    }

      public void addStudent2() {
        addStudent();
    }

可以看出,我们在addStudent()方法上加了事务注解@Transactional,
所以,方法内抛出任意异常,都会回退,记录不会插入成功!

addStudent2()方法仅仅是内部调用了addStudent()方法,没有其它操作,效果应该和addStudent()一模一样.

我们在Controller直接调用addStudent()方法,会发现抛出了异常,记录没有被成功插入,而是被回退了.

image.png

但是,当我们调用addStudent2()时,发现虽然也抛出异常了,但是记录居然插入成功了!!

为什么会出现这种情况?

原因

SpringBoot 会扫描@Transactional的类和方法,并通过动态代理实现启动事务的代理方法。

在一个Service内部,事务方法之间的嵌套调用,普通方法和事务方法之间的嵌套调
用,都不会开启新的事务. 是因为Spring采用动态代理机制来实现事务控制,而动态
代理最终都是要调用原始对象的,而原始对象在去调用方法时,是不会再触发代理
了!

一句话描述就是

同一个类中,非事务方法(addStudent2)调用事务方法(addStudent),事务是无法生效的.

解决方案

  1. 将带有@Transactional注解的方法移到另一个类中,发起类之间的方法调用。
    ServiceB
    @Transactional(propagation = Propagation.REQUIRED)
    public void addStudent() {
        Student student = new Student();
        student.setAge(20);
        student.setName("学生");
        studentMapper.insert(student);
        int i = 1 / 0;
    }

ServiceA

    public void addStudent2() {
        ServiceB.addStudent();
    }
  1. 在入口类添加注解方式暴漏代理对象,
@EnableAspectJAutoProxy(exposeProxy = true)

image.png

记得在pom.xml引入依赖

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aspects</artifactId>
</dependency>

image.png

然后在Service中通过代理对象AopContext.currentProxy()去调用方法。

    @Transactional(propagation = Propagation.REQUIRED)
    public void addStudent() {
        Student student = new Student();
        student.setAge(20);
        student.setName("学生");
        studentMapper.insert(student);
        int i = 1 / 0;
    }

    public void addStudent2() {
        ((TransactionService)AopContext.currentProxy()).addStudent();
    }

以上两种方式均能使得事务生效.

说到这里,顺便看了下事务的传播机制

SpringBoot 事务传播机制

@Transactional(propagation=Propagation.REQUIRED)
如果有事务, 那么加入事务, 没有的话新建一个(@Transactional 默认使用这种方式)

@Transactional(propagation=Propagation.NOT_SUPPORTED)
容器不为这个方法开启事务

@Transactional(propagation=Propagation.REQUIRES_NEW)
不管是否存在事务,都创建一个新的事务,原来的挂起,新的执行完毕,继续执行老的事务

@Transactional(propagation=Propagation.MANDATORY)
必须在一个已有的事务中执行,否则抛出异常

@Transactional(propagation=Propagation.NEVER)
必须在一个没有的事务中执行,否则抛出异常(与Propagation.MANDATORY相反)

@Transactional(propagation=Propagation.SUPPORTS)
如果其他bean调用这个方法,在其他bean中声明事务,那就用事务.如果其他bean没有声明事务,那就不用事务.

请关注我的订阅号

订阅号.png

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

码哥说

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值