事务的传播机制(案例演示)

事务的传播机制(案例演示)


案例介绍:

数据库表:user表,两条数据:
在这里插入图片描述
Mapper逻辑
两条sql

  1. update1:修改张三的pass字段为2。
  2. update2:修改李四的pass字段为2。
    @Update("Update user set pass = 2 where id = 1")
    void update1();

    @Update("Update user set pass = 2 where id = 2")
    void update2();

Service逻辑:
两个Service,两个方法:

  1. test01:对应update1
  2. test02:对应update2
    (在同一个类中,一个方法调用另外一个有注解(比如@Async,@Transational)的方法,注解是不会生效的)
@Service
public class shiwuServiceImpl {

    @Autowired
    TestMapper testMapper;

    @Autowired
    shiwuServiceImpl2 serviceImpl2;


    public void test01(){
        testMapper.update1();
    }
}
@Service
public class shiwuServiceImpl2 {


    @Autowired
    TestMapper testMapper;
    
    public void test02(){
        testMapper.update2();
    }


}


事务的传播机制机制:

简单的理解就是多个事务方法相互调用时,事务如何在这些方法间传播。

举个栗子,方法A是一个事务的方法,方法A执行过程中调用了方法B,那么方法B有无事务以及方法B对事务的要求不同都会对方法A的事务具体执行造成影响,同时方法A的事务对方法B的事务执行也有影响,这种影响具体是什么就由两个方法所定义的事务传播类型所决定。

在Spring中对于事务的传播行为定义了七种类型分别是:

1,required:

Spring默认的事务传播类型,如果当前没有事务,则自己新建一个事务,如果当前存在事务,则加入这个事务

Service1:

    @Transactional(propagation = Propagation.REQUIRED)
    public void test01(){
        testMapper.update1();
        serviceImpl2.test02();
    }

Service2:

    public void test02(){
        testMapper.update2();
        int a = 1/0;
    }
  1. test01方法为程序的入口,被Transactional修饰,默认传播行为是REQUIRED。
  2. test01方法中试图将数据库中的张三的pass属性改为2。
  3. test01方法调用了Service2中的test02方法。
  4. test02方法试图将数据库中李四的属性改为2。
  5. test02方法报错!
  6. 全部回滚。

我们看看数据库结果:
在这里插入图片描述
Spring默认的事务传播类型,如果当前没有事务,则自己新建一个事务,如果当前存在事务,则加入这个事务
符合了上面红字中的情况。

现在我们来看看另一种情况(被调用的方法加上Transactional注解):

Service1:

    public void test01(){
        testMapper.update1();
        serviceImpl2.test02();
    }

Service2:

    @Transactional(propagation = Propagation.REQUIRED)
    public void test02(){
        testMapper.update2();
        int a = 1/0;
    }
  1. test01方法没有开启事务,将张三的pass属性改为2。
  2. test01方法调用了Service2中的test02方法
  3. test02方法被Transactional修饰,默认传播行为是REQUIRED。
  4. test02方法报错!
  5. test02方法回滚。

我们看看数据库结果:
在这里插入图片描述

Spring默认的事务传播类型,如果当前没有事务,则自己新建一个事务,如果当前存在事务,则加入这个事务
符合了上面红字中的情况。

2,supports:

如果当前存在事务,则加入当前事务,如果当前没有事务,就以非事务方法执行

Service1:

    public void test01(){
        testMapper.update1();
        serviceImpl2.test02();
    }

Service2:

    @Transactional(propagation = Propagation.SUPPORTS)
    public void test02(){
        testMapper.update2();
        int a = 1/0;
    }
  1. test01方法没有开启事务,将张三的pass属性改为2。
  2. test01方法调用了Service2中的test02方法
  3. test02方法被Transactional修饰,传播行为是SUPPORTS。
  4. test02方法报错!
  5. 两个操作均成功

我们看看数据库结果:
在这里插入图片描述
如果当前存在事务,则加入当前事务,如果当前没有事务,就以非事务方法执行
符合了上面红字中的情况。
SUPPORTS和REQUIRED的区别就在于上面红字中的情况。。所以另一种情况我们就不举例了。

3,mandatory:

当前存在事务,则加入当前事务,如果当前事务不存在,则抛出异常。

这个我们就不举例了,一眼就看懂了。这种属性的目的就是标注我当前这个方法必须要在事务中进行,否则我就直接报错,方法体我是一行都不会去执行的!

4,requires_new:

创建一个新事务,如果存在当前事务,则挂起该事务。

可以理解为被requires_new修饰的方法,在执行时,不论当前是否存在事务,总是会新建一个事务,如果调用方有事务,就将调用方事务挂起!

什么是挂起?

Service1:

    @Transactional(propagation = Propagation.REQUIRED)
    public void test01(){
        testMapper.update1();
        serviceImpl2.test02();
    }

Service2:

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void test02(){
        testMapper.update2();
        int a = 1/0;
    }
  1. test01有事务
  2. test02事务属性为REQUIRES_NEW
  3. test02报错
  4. 全部回滚。

这里可以知道,挂起的意思就是test01执行到serviceImpl2.test02();这个步骤的时候,这个时候test01中的修改并没有提交,也没有回滚,处于一个挂起的状态! 一旦test02()方法报错,而test01没有进行Try catch补获,test01也会进行回滚!

好,我们看看另一种情况!

Service1

    @Transactional(propagation = Propagation.REQUIRED)
    public void test01(){
        testMapper.update1();
        serviceImpl2.test02();
        int a = 1/0;
    }

Service2

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void test02(){
        testMapper.update2();
    }
  1. test01()开启普通事务,第一步试图修改张三的数据
  2. 第二步调用的Service2的test02方法。
  3. 这个时候test01方法的事务挂起了,没有提交也没有回滚。
  4. test02方法开启了一个新的事务,把李四的数据改掉了,没有报错,提交了事务。
  5. 回到test01中
  6. test01报错,由于test02已经提交。只有test01回滚。

结果:
在这里插入图片描述

5,not-supported:

始终以非事务方式执行,如果当前存在事务,则挂起当前事务

这个就不写例子了,很好理解。重点在于:

  1. 由not-supported修饰的test02方法如果报错了。
  2. 而刚好调用处test01没有进行异常捕获。
  3. test01和test02都会回滚!
6,never:

不使用事务,如果当前事务存在,则抛出异常

这个也很简单。重点在于:

  1. test01方法开启了事务,调用test02方法
  2. test02是由never修饰的,发现我的调用方居然是开启了事务!我不同意!报错!方法体内一行代码都不执行
  3. 此时如果test01没有进行异常捕获。
  4. test01回滚!
7,nested:

如果当前事务存在,则在嵌套事务中执行。如果当前事务不存在事务,就和REQUIRED的操作一样(开启一个事务)

  • 和REQUIRES_NEW的区别
    REQUIRES_NEW是新建一个事务并且新开启的这个事务与原有事务无关,而NESTED则是当前存在事务时(我们把当前事务称之为父事务)会开启一个嵌套事务(称之为一个子事务)。
    在NESTED情况下父事务回滚时,子事务也会回滚,而在REQUIRES_NEW情况下,原有事务回滚,不会影响新开启的事务。

  • 和REQUIRED的区别
    REQUIRED情况下,调用方存在事务时,则被调用方和调用方使用同一事务,那么被调用方出现异常时,由于共用一个事务,所以无论调用方是否catch其异常,事务都会回滚
    而在NESTED情况下,被调用方发生异常时,调用方可以catch其异常,这样只有子事务回滚,父事务不受影响

Service1

    @Transactional(propagation = Propagation.REQUIRED)
    public void test01(){
        testMapper.update1();
        serviceImpl2.test02();
        int a = 1/0;
    }

Service2

    @Transactional(propagation = Propagation.NESTED)
    public void test02(){
        testMapper.update2();
    }
  1. test01开启了事务,试图修改张三的数据。
  2. test01调用了test02.
  3. test02被NESTED修饰,就开启了一个嵌套事务(这个事务属于外面的大事务)。并完成了自己的操作。
  4. 回到test01,test01报错。自己的事务连同嵌套的子事务一起回滚。

结果:
在这里插入图片描述

好,我们看看另一种情况!

Service1

    @Transactional(propagation = Propagation.REQUIRED)
    public void test01(){
        testMapper.update1();
        try {
            serviceImpl2.test02();
        }catch (Exception e){
            System.out.println("test02报错了");
        }
    }

Service2

    @Transactional(propagation = Propagation.NESTED)
    public void test02(){
        testMapper.update2();
        int a = 1/0;
    }
  1. test01开启了事务,并对test02方法进行了异常捕获。
  2. test02开启了子事务,报错了。test02回滚!
  3. test01捕获了test02的报错,不影响test01的任何操作。
  4. 所以最后,test01操作成功,test02回滚!

结果:
在这里插入图片描述

好了 基本已经讲完,欢迎大家评论区指出不足,一起学习进步!

大家看完了点个赞,码字不容易啊。。。

  • 7
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

易柏州Innovation

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

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

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

打赏作者

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

抵扣说明:

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

余额充值