Spring 事务的传播行为

最近复习Spring,看到传播行为这里,有些许疑惑,然后各种百度,返现竟然没有详细的介绍,就几篇比较有质量的文章,没点耐心和基础还看不懂。经过一天多的反复钻研,我把我对传播行为的理解梳理一下。

我们什么时候会用到Spring的传播行为?

在我们使用Spring事务的时候,很有可能遇到一下这种情况在方法A调用方法B,方法B被注解称一个事务,然后这时候方法A要在方法B的基础上做一些操作,这个时候你就要想啊,我要不要把方法A也设置成一个事务呢?方法A种的一下操作想要和方法B保持一致性,有的方法又不想,这时候咋办呢?是不是蒙了?
这个时候就需要使用Spring的事务的传播行为了。

什么是事务的传播行为

什么是事务的传播行为呢,我的理解是当方法A上开启事务了,方法A中调用B这个时候,方法A中的事务能不能到达方法B这一层。

public void  testPropagation(){
        methodA(){
            MethodDoSomethingBefor();
            methodB();
            MethodDoSomethingAfter();
        }
    }
    @Transactional //开启事务
    public void methodB(){
        doSomething();
    }

首先,事务由七种传播行为,我来一一解释下。
在这里插入图片描述
引用人家的图,特此鸣谢
首先要明确一点啊,这其中传播行为倒是配在被调用的方法上的,也就是方法B上,就是说当方法A种需要使用到方法B时,讨论这时候方法A事务是个什么鬼样子(重点)。不要像我一样一开始觉得会有7*7=49种排列组合,只有七种哦(方法A的注解是@Transactional(),方法B是这七种),这是往后看的前提。

下面结合代码,一个一个的分析:
PROPAGATION_REQUIRED: 表示当前方法必须运行在事务中。如果当前事务存在,方法将会在该事务中运行。否则,会启动一个新的事务。

public void  methodA(){
        customerMapper.insert(new Customer(5l,"我是第一个","你好",0));
        methodB();
    }
    @Transactional //开启事务
    public void methodB(){
        orderMapper.insert(new Order(null ,10l,10l,10l));
        int a =1/0;
    }

在这里插入图片描述
现在是方法A没有开启事务,方法B开启了事务,两者表都是空的,执行方法A
结果:
在这里插入图片描述
结果是都插进去了,所以方法A如果没有开启事务的话,方法B也没法开启。
都开启以后就可以了,看结果rollBack了。
在这里插入图片描述

 public void methodA() {
        customerMapper.insert(new Customer(5l, "我是第一个", "你好", 0));
        methodB();
        int a = 1 / 0;

    }

    @Transactional(propagation = Propagation.REQUIRED) //开启事务
    public void methodB() {
        orderMapper.insert(new Order(null, 10l, 10l, 10l));
//        int a =1/0;

    }

这个是methodA()中有异常,结果(每次重新执行钱都把数据库清空了,也是回滚了
在这里插入图片描述
结论:方法B设置传播行为是PROPAGATION_REQUIRED以后,方法A和方法B整合为一个事务,并且方法A必须开启事务,要不然不会回滚,为什么不会回滚呢,前面提到过,是方法A中的事务能不能到达方法B这一层,如果A不开启事务,则默认是没有开启事务,方法B也得不到传递。

PROPAGATION_SUPPORTS: 表示当前方法不需要事务上下文,但是如果存在当前事务的话,那么该方法会在这个事务中运行
我的理解就是随着方法A,方法A有事务就按事务执行,方法A没有,就不按事务执行。

方法A开启事务,抛异常,没有执行,回滚了

@Transactional()
    public void methodA() {
        customerMapper.insert(new Customer(5l, "我是第一个", "你好", 0));
        methodB();
//        int a = 1 / 0;
        System.out.println("执行了");

    }

    @Transactional(propagation = Propagation.SUPPORTS) //开启事务
    public void methodB() {
        orderMapper.insert(new Order(null, 10l, 10l, 10l));
        int a =1/0;

    }

在这里插入图片描述
方法A关闭事务,插入进去了

//    @Transactional()
    public void methodA() {
        customerMapper.insert(new Customer(5l, "我是第一个", "你好", 0));
        methodB();
//        int a = 1 / 0;
        System.out.println("执行了");

    }

    @Transactional(propagation = Propagation.SUPPORTS) //开启事务
    public void methodB() {
        orderMapper.insert(new Order(null, 10l, 10l, 10l));
        int a =1/0;

    }

在这里插入图片描述
结论,跟随方法A,开启事务,抛异常时数据回滚,不开启事务,可以插入。也符合第一条说的,事务的传播行为时看事务能不能传递到B这一程。

PROPAGATION_MANDATORY: 如果已经存在一个事务,支持当前事务。如果没有一个活动的事务,则抛出异常。

public void methodA() {
        customerMapper.insert(new Customer(5l, "我是第一个", "你好", 0));
        bookService.methodB();
//        int a = 1 / 0;
        System.out.println("执行了");
    }
   
   这个方法在bookService中,如果是同一个方法,不会抛异常(所以这也要注意,当使用事务的传播行为的时候,要是两个service)
@Transactional(propagation = Propagation.MANDATORY)
    public void methodB() {
        orderMapper.insert(new Order(null, 10l, 10l, 10l));
//        int a =1/0;
    }

在这里插入图片描述
重点
PROPAGATION_REQUIRED_NEW 表示当前方法必须运行在它自己的事务中。一个新的事务将被启动。如果存在当前事务,在该方法执行期间,当前事务会被挂起。如果使用JTATransactionManager的话,则需要访问TransactionManager

这个表示,在方法A的基础上再建一个事务,相互独立。看代码

 @Transactional
    public void methodA() {
        try{
            customerMapper.insert(new Customer(5l, "我是第一个", "你好", 0));
            bookService.methodB();
        }catch (Exception e){
            customerMapper.insert(new Customer(5l, "我是第二个", "你好", 0));
        }


    }
@Transactional(propagation = Propagation.REQUIRES_NEW)
    public void methodB() {
        orderMapper.insert(new Order(null, 10l, 10l, 10l));
        int a =1/0;
    }

在这里插入图片描述
结果一个插入两条,一个没有插入,是不是很疑惑?我们一点点梳理。
首先介绍一个概念,事务挂起,就是代码事务进去等待状态,等其他的代码执行完,再执行。

再说,PROPAGATION_REQUIRED_NEW,它是不管有没有事务,都重新开启一个事务,或者这样表示

main(){ 
  TransactionManager tm = null; 
try{ 
  //获得一个JTA事务管理器 
    tm = getTransactionManager(); 
    tm.begin();//开启一个新的事务 
    Transaction ts1 = tm.getTransaction(); 
    doSomeThing(); 
    tm.suspend();//挂起当前事务 
    try{ 
      tm.begin();//重新开启第二个事务 
      Transaction ts2 = tm.getTransaction(); 
      methodB(); 
      ts2.commit();//提交第二个事务 
   } 
  Catch(RunTimeException ex){ 
      ts2.rollback();//回滚第二个事务 
  } 
  finally{ 
     //释放资源 
   } 
    //methodB执行完后,复恢第一个事务 
    tm.resume(ts1); 
doSomeThingB(); 
    ts1.commit();//提交第一个事务 
} 
catch(RunTimeException ex){ 
   ts1.rollback();//回滚第一个事务 
} 
finally{ 
   //释放资源 
} 
}

这里引用了别人博客的代码,特此鸣谢
我们都知道,事务的原理就是一个 try{}catch{}finally{}的形式,有异常回滚,没有问题commit,最后释放资源,这里的PROPAGATION_REQUIRED_NEW就是在方法A的try{}catch{}中又做了一层try{}catch{},这就有可能诞生很多的情况
1).方法B中有异常,方法B中的操作回滚,不会影响到方法A的操作(前提是方法try{}catch{}了)
2).方法A中有异常,但是方法B中的操作可以正常进行,因为方法B在自己的事务中commit了已经

@Transactional
    public void methodA() {
        customerMapper.insert(new Customer(5l, "我是第一个", "你好", 0));
        bookService.methodB();
        int a=1/0;
        customerMapper.insert(new Customer(5l, "我是第二个", "你好", 0));

    }
@Transactional(propagation = Propagation.REQUIRES_NEW)
    public void methodB() {
        orderMapper.insert(new Order(null, 10l, 10l, 10l));
    }

在这里插入图片描述
总结 :PROPAGATION_REQUIRED_NEW 不管有没有都重新开启一个事务

PROPAGATION_NOT_SUPPORTED: 表示该方法不应该运行在事务中。如果存在当前事务,在该方法运行期间,当前事务将被挂起。如果使用JTATransactionManager的话,则需要访问TransactionManager(这里又提到了挂起,也就是说,这里不管你有没有事务,我都执行,并且不影响A的执行)

 @Transactional
    public void methodA() {
        try {
            customerMapper.insert(new Customer(5l, "我是第一个", "你好", 0));
            bookService.methodB();
//        int a=1/0; 
        }catch (Exception e){
            customerMapper.insert(new Customer(5l, "我是第二个", "你好", 0));
        }
    }


@Transactional(propagation = Propagation.NOT_SUPPORTED)
    public void methodB() {
        orderMapper.insert(new Order(null, 10l, 10l, 10l));
        int a =1/0;
    }

在这里插入图片描述

总结: PROPAGATION_NOT_SUPPORTED 不支持事务,有事务就要被挂起**

PROPAGATION_NEVER: 这个和PROPAGATION_MANDATORY正好相反,这个是有事务抛异常

@Transactional
    public void methodA() {
            customerMapper.insert(new Customer(5l, "我是第一个", "你好", 0));
            bookService.methodB();
    }
     @Transactional(propagation = Propagation.NEVER)
    public void methodB() {
        orderMapper.insert(new Order(null, 10l, 10l, 10l));
    }

在这里插入图片描述

PROPAGATION_NESTED: 如果一个活动的事务存在,则运行在一个嵌套的事务中. 如果没有活动事务, 则按TransactionDefinition.PROPAGATION_REQUIRED 属性执行。这是一个嵌套事务,使用JDBC 3.0驱动时,仅仅支持DataSourceTransactionManager作为事务管理器。需要JDBC 驱动的java.sql.Savepoint类。有一些JTA的事务管理器实现可能也提供了同样的功能。使用PROPAGATION_NESTED,还需要把PlatformTransactionManager的nestedTransactionAllowed属性设为true;而 nestedTransactionAllowed属性值默认为false;

是不是很懵,直接上代码逻辑

main(){ 
Connection con = null; 
Savepoint savepoint = null; 
try{ 
   con = getConnection(); 
   con.setAutoCommit(false); 
   doSomeThingA(); 
   savepoint = con.setSavepoint(); 
   try{ 
       methodB(); 
   }catch(RuntimeException ex){ 
      con.rollback(savepoint); 
   } 
   finally{ 
     //释放资源 
  }

   doSomeThingB(); 
   con.commit(); 
} 
catch(RuntimeException ex){ 
  con.rollback(); 
} 
finally{ 
   //释放资源 
} 
}

是不是看着跟REQUIRED_NEW没有区别,不都是在新建一个事务吗,区别就在于上面说的,他是基于JDBC 3.0驱动的,savepoint = con.setSavepoint();,并且在内层少了一个commit这代表什么呢,就是内存出现问题,只会滚内层到savepoint ,外层出现问题全部回滚。

//    @Transactional
    public void methodA() {
        try{
            customerMapper.insert(new Customer(5l, "我是第一个", "你好", 0));
            bookService.methodB();
//            int a=1/0;
        }catch (Exception e){

        }
    }
    
     @Transactional(propagation = Propagation.NESTED)
    public void methodB() {
        orderMapper.insert(new Order(null, 10l, 10l, 10l));
        int a =1/0;
    }

在这里插入图片描述
这里外层没有开启事务,只有内层回滚了,外层执行成功

@Transactional(propagation = Propagation.NESTED)
    public void methodB() {
        orderMapper.insert(new Order(null, 10l, 10l, 10l));
//        int a =1/0;
    }
 @Transactional
    public void methodA() {
        try{
            customerMapper.insert(new Customer(5l, "我是第一个", "你好", 0));
            bookService.methodB();
            int a=1/0;
        }catch (Exception e){

        }
    }

在这里插入图片描述
最后我发现一个问题

 @Transactional
    public void methodA() {
        try{
            customerMapper.insert(new Customer(5l, "我是第一个", "你好", 0));
            bookService.methodB();
            int a=1/0;
        }catch (Exception e){

        }
    }
@Transactional(propagation = Propagation.REQUIRES_NEW)
    public void methodB() {
        orderMapper.insert(new Order(null, 10l, 10l, 10l));
//        int a =1/0;
    }

在这里插入图片描述
当外层有异常的时候,只要try{}catch{}也执行成功了,说明了spring的事务其实就是基于AOP在我们主要代码逻辑上加了获取连接,关闭自动提交,开启事务,catch异常打印异常信息,回滚,最后关闭资源的工作,如果自己try{}catch{}了 那么就不会回滚了
参考:https://www.cnblogs.com/yixianyixian/p/8372832.html

https://www.open-open.com/lib/view/open1350865116821.html

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值