Spring事务传播

什么是事务传播

事务传播行为是为了解决业务层方法之间互相调用的事务问题,当一个事务方法被另一个事务方法调用时,事务该以何种状态存在?例如新方法可能继续在现有事务中运行,也可能开启一个新事务,并在自己的事务中运行,等等,这些规则就涉及到事务
的传播性。

大白话就是,多个业务相互调用,业务如果开启了业务,业务会变成什么样?(新建一个业务还是共用同一个业务)

Spring的事务传播

Spring定义了七种事务传播行为:

传播性描述
REQUIRED如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务
SUPPORTS如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行
MANDATORY如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常
REQUIRES_NEW创建一个新的事务,如果当前存在事务,则把当前事务挂起
NOT_SUPPORTED以非事务方式运行,如果当前存在事务,则把当前事务挂起
NEVER以非事务方式运行,如果当前存在事务,则抛出异常
NESTED如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于REQUIRED

那么这七种传播性是什么意思呢,下面通过一个例子解释其含义

举例理解

前期准备

1、建立数据库
在这里插入图片描述

2、建立一个springboot项目,连接数据库

可以使用其他方式建立项目和连接数据库,这里就不想详细展示,重点在业务流程

在这里插入图片描述
3、编写两个业务流程

@Service
public class Service1 {
    @Resource
    private AccountMapper accountMapper;
    @Resource
    private Service2 service2;

    public void handle1(){
        Account account = new Account();
        account.setId(1);
        account.setBalance(100D);
        accountMapper.update(account);
        service2.handle2();
    }
}


@Service
public class Service2 {
    @Resource
    private AccountMapper accountMapper;
    public void handle2() {
        Account account = new Account();
        account.setId(2);
        account.setBalance(100D);
        accountMapper.update(account);
    }

调用handle1方法

@SpringBootTest
class TranscationApplicationTests {
    
    @Resource
    private Service1 service1;
    @Test
    void test(){
        service1.handle1();
    }

}

ps:此时两个事务都没有开启事务,开启事务使用@Transactional

1、REQUIRED

REQUIRED是默认的传播事务,如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。

  • handle1,handle2均开启事务时:
.....
@Transactional		//	默认是REQUIRED
public void handle1()
....

@Transactional
public void handle2()

这个时候调用handle1,会开启一个事务,handle1执行到service2.handle2()方法时,因为handle2是REQUIRED,所以handle2()会加入刚刚handle1的事务中,即二者共用一个事务。

此时调用handle1方法,查询数据库
在这里插入图片描述
发现更改成功。
因为二者是共用一个事务,所以如果,这两个方法任意一个发生错误,回滚,两个都会回滚。如在handle2手动添加错误,其他不变(将balance重置):

@Transactional
    public void handle2() {
        Account account = new Account();
        account.setId(2);
        account.setBalance(100D);
        accountMapper.update(account);
        int i = 1 / 0;
    }

在这里插入图片描述
在这里插入图片描述
发现数据库没有更新!

  • handle2开启事务,handle1没有开启事务
    按上面的说明,那应该在执行service2.handle1()时是没有事务的,就不存在回滚之类什么的,执行到service2.handle2()就新建一个事务,执行完之后就关闭事务。

按这个逻辑,那如果handle2报错,需要回滚,handle1就不会受影响。更改代码查看结果:

@Service
public class Service1 {
    @Resource
    private AccountMapper accountMapper;
    @Resource
    private Service2 service2;
	
    public void handle1(){
        Account account = new Account();
        account.setId(1);
        account.setBalance(100D);
        accountMapper.update(account);
        service2.handle2();
    }
}


@Service
public class Service2 {
    @Resource
    private AccountMapper accountMapper;
    @Transactional
    public void handle2() {
        Account account = new Account();
        account.setId(2);
        account.setBalance(100D);
        accountMapper.update(account);
        int i = 1/0;	//报错 事务回滚
    }

在这里插入图片描述
发现只有id为1的balance更新了,id为2的没有更新。这样就验证了上面的推断了!

2、REQUIRES_NEW

与REQUIRES_NEW区别在于,如果已经存在,就将外部的事务挂起,再建新的事务,会有两个事务。

  • 如果外部(handle1)没有事务,内部事务(handle2)为REQUIRES_NEW,就新建一个事务(和REQUIRES的情况一样)
  • 如果外部存在事务,就新建一个事务,并将外部事务挂起。(此时有两个事务)
@Service
public class Service1 {
	....
	@Transactional
    public void handle1(){
        Account account = new Account();
        account.setId(1);
        account.setBalance(100D);
        accountMapper.update(account);
        service2.handle2();
    }
}

......
@Transactional(propagation = Propagation.REQUIRES_NEW)
    public void handle2() {
        Account account = new Account();
        account.setId(2);
        account.setBalance(100D);
        accountMapper.update(account);
    }

此时

handle2发生错误回滚时,handle1不影响,因为它是独立的一个新事务。

在handle2加入int i =1/0;执行查看数据库

在这里插入图片描述
因为handle1和handle2是不同的事务,handle2报错回滚并不影响handle1事务的提交,故handle1可以更改成功,handle2更改失败。

3、NESTED

如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于 REQUIRED

外部事务回滚会导致内部事务回滚

更改代码如下:

@Service
public class Service1 {
	....
	@Transactional
    public void handle1(){
        Account account = new Account();
        account.setId(1);
        account.setBalance(100D);
        accountMapper.update(account);
        service2.handle2();
        int i = 1/0;
    }
}

......
@Transactional(propagation = Propagation.NESTED)
    public void handle2() {
        Account account = new Account();
        account.setId(2);
        account.setBalance(100D);
        accountMapper.update(account);
    }

执行发现数据库没有更改
在这里插入图片描述

内部事务回滚,外部事务不受影响
更改代码如下:

@Service
public class Service1 {
	....
	@Transactional
    public void handle1(){
        Account account = new Account();
        account.setId(1);
        account.setBalance(100D);
        accountMapper.update(account);
        service2.handle2();
    }
}

......
@Transactional(propagation = Propagation.NESTED)
    public void handle2() {
        Account account = new Account();
        account.setId(2);
        account.setBalance(100D);
        accountMapper.update(account);
        int i = 1/0;
    }

执行发现数据库没有更改???
在这里插入图片描述
这是为什么呢?子事务回滚,外部事务也会回滚吗?

其实不是这样的,我们仔细看下代码会发现,因为handle2发生了异常,即我们在handle1 执行service2.handle2()语句是会报错的,所以这时候handle1也是会报错,所以才回滚的,我们只需要将这个异常抛出即可验证。

 public void handle1(){
        Account account = new Account();
        account.setId(1);
        account.setBalance(100D);
        accountMapper.update(account);
         try{
        service2.handle2();
       }catch (Exception e){
           e.printStackTrace();
       }
    }

再次执行,查看数据
在这里插入图片描述
这样就符合上面所说的了!

4、MANDATORY

handle1事务传播是MANDATORY:
● handel2有事务,handle1加入handle2事务
● handle2没有事务,handle1抛出异常

@Service
public class Service1 {
	....
    public void handle1(){
        Account account = new Account();
        account.setId(1);
        account.setBalance(100D);
        accountMapper.update(account);
        service2.handle2();
    }
}

......
@Transactional(propagation = Propagation.MANDATORY)
    public void handle2() {
        Account account = new Account();
        account.setId(2);
        account.setBalance(100D);
        accountMapper.update(account);
    }

从事handle1没有事务,就会抛出异常报错
在这里插入图片描述

小结

其他几个就不一一举例了,大家可以按照这个方法一一验证。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值