Spring事务传播机制示例验证

配置

以下验证部分的配置:

技术栈版本
MySQL5.7.42
SpringBoot2.6.13

简述

下面说到的当前是指父方法是否有事务传递下来。

  • REQUIRED(默认):当前存在事务则加入当前事务,不存在则创建一个事务。因为是共用事务,所以父方法catch子方法的异常还是会全部回滚。
  • SUPPORTS:当前存在事务则加入当前事务,不存在则以非事务方式运行。
  • MANDATORY:当前存在事务则加入当前事务,不存在则抛出异常。
  • REQUIRES_NEW:创建一个新事务,当前存在事务则挂起当前事务。
  • NOT_SUPPORTED:始终以非事务方法运行,当前存在事务则挂起当前事务。
  • NEVER:以非事务方法运行,当前存在事务则抛出异常。
  • NESTED:当前存在事务则创建子事务,父事务回滚会导致子事务回滚。而子事务回滚,如果父事务catch了异常则父事务不会回滚。当前不存在事务则创建新事务。

各种事务传播机制,指的是子事务使用该传播机制子方法和父方法的事务有什么关系。

后面的验证,如果没有特殊说明,默认父方法都正确处理子方法抛出的异常。事务是否回滚要结合数据库看,因为其中一个方法抛异常就会导致几个方法都回滚。

一、验证REQUIRED的事务传播机制

1.子事务回滚

	@Autowired
	private IMyAppUserService myAppUserService;
	@Override
	@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
	public void testA() {
		try {
			MyAppUser myAppUser = new MyAppUser();
			myAppUser.setUserNo("A" + UUID.randomUUID());
			myAppUser.setNickName("A事务");
			this.save(myAppUser);
			myAppUserService.testB();
		} catch (Exception e) {
			TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
			log.error("A事务回滚了");
		}
	}

	@Override
	@Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRED)
	public void testB() {
		try {
			MyAppUser myAppUser = new MyAppUser();
			myAppUser.setUserNo("B" + UUID.randomUUID());
			myAppUser.setNickName("B事务");
			this.save(myAppUser);
			int a=10/0;
		} catch (Exception e) {
			TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
			log.error("B事务回滚了");
		}
	}

结果:父事务回滚了,子事务也回滚了。符合共用一个事务的说法。

这里其实还涉及到一个知识点,网上找资料显示,这里会接着抛出异常,是因为子事务先回滚了,但父方法并不知道,于是继续执行,到后面要提交父事务的时候才发现子事务回滚了,父事务也就没法提交了,跟着回滚并抛出异常提醒。(这属于是子事务回滚带这父事务回滚)

在这里插入图片描述
在这里插入图片描述

2.父事务回滚

	@Autowired
	private IMyAppUserService myAppUserService;
	@Override
	@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
	public void testA() {
		try {
			MyAppUser myAppUser = new MyAppUser();
			myAppUser.setUserNo("A" + UUID.randomUUID());
			myAppUser.setNickName("A事务");
			this.save(myAppUser);
			myAppUserService.testB();
			int a=10/0;
		} catch (Exception e) {
			TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
			log.error("A事务回滚了");
		}
	}

	@Override
	@Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRED)
	public void testB() {
		try {
			MyAppUser myAppUser = new MyAppUser();
			myAppUser.setUserNo("B" + UUID.randomUUID());
			myAppUser.setNickName("B事务");
			this.save(myAppUser);
		} catch (Exception e) {
			TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
			log.error("B事务回滚了");
		}
	}

结果:父事务回滚了,子事务也回滚了,符合共用一个事务的说法。

在这里插入图片描述
在这里插入图片描述

结论:REQUIRED事务传播级别下,父子共用事务,随便一个回滚都会导致整个事务回滚。

二、验证REQUIRES_NEW的事务传播机制

1.子事务回滚

	@Autowired
	private IMyAppUserService myAppUserService;
	@Override
	@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
	public void testA() {
		try {
			MyAppUser myAppUser = new MyAppUser();
			myAppUser.setUserNo("A" + UUID.randomUUID());
			myAppUser.setNickName("A事务");
			this.save(myAppUser);
			myAppUserService.testB();
		} catch (Exception e) {
			TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
			log.error("A事务回滚了");
		}
	}

	@Override
	@Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRES_NEW)
	public void testB() {
		try {
			MyAppUser myAppUser = new MyAppUser();
			myAppUser.setUserNo("B" + UUID.randomUUID());
			myAppUser.setNickName("B事务");
			this.save(myAppUser);
			int a=10/0;
		} catch (Exception e) {
			TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
			log.error("B事务回滚了");
		}
	}

结果:子事务回滚,父事务没有回滚。
在这里插入图片描述
在这里插入图片描述

2.父事务回滚

	@Autowired
	private IMyAppUserService myAppUserService;
	@Override
	@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
	public void testA() {
		try {
			MyAppUser myAppUser = new MyAppUser();
			myAppUser.setUserNo("A" + UUID.randomUUID());
			myAppUser.setNickName("A事务");
			this.save(myAppUser);
			myAppUserService.testB();
			int a=10/0;
		} catch (Exception e) {
			TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
			log.error("A事务回滚了");
		}
	}

	@Override
	@Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRES_NEW)
	public void testB() {
		try {
			MyAppUser myAppUser = new MyAppUser();
			myAppUser.setUserNo("B" + UUID.randomUUID());
			myAppUser.setNickName("B事务");
			this.save(myAppUser);
		} catch (Exception e) {
			TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
			log.error("B事务回滚了");
		}
	}

结果:父事务回滚,子事务没有回滚。
在这里插入图片描述
在这里插入图片描述

结论:REQUIRES_NEW事务传播级别下,父事务和子事务是完全互相独立的事务,不会互相影响。

三、验证NESTED的事务传播机制

提前说明一点,如果子事务的异常往外抛,父方法没有正确处理,就相当于父方法也发生了异常,就会导致都回滚,这和事务传播机制没有关系。

1.子事务回滚

现在测试子事务回滚,但是子事务处理了自己的异常(或者父方法额外处理了),看看父事务会怎么样。

	@Autowired
	private IMyAppUserService myAppUserService;
	@Override
	@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
	public void testA() {
		try {
			MyAppUser myAppUser = new MyAppUser();
			myAppUser.setUserNo("A" + UUID.randomUUID());
			myAppUser.setNickName("A事务");
			this.save(myAppUser);
			myAppUserService.testB();

		} catch (Exception e) {
			TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
			log.error("A事务回滚了");
		}

	}

	@Override
	@Transactional(rollbackFor = Exception.class,propagation = Propagation.NESTED)
	public void testB() {
		try {
			MyAppUser myAppUser = new MyAppUser();
			myAppUser.setUserNo("B" + UUID.randomUUID());
			myAppUser.setNickName("B事务");
			this.save(myAppUser);
			int a=10/0;
		} catch (Exception e) {
			TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
			log.error("B事务回滚了");
		}
	}

结果:结合日志和数据库,父事务的数据成功插入了。只有子事务回滚了,父事务不受影响。
在这里插入图片描述
在这里插入图片描述

2.父事务回滚

	@Autowired
	private IMyAppUserService myAppUserService;
	@Override
	@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
	public void testA() {
		try {
			MyAppUser myAppUser = new MyAppUser();
			myAppUser.setUserNo("A" + UUID.randomUUID());
			myAppUser.setNickName("A事务");
			this.save(myAppUser);
			myAppUserService.testB();
			int a=10/0;
		} catch (Exception e) {
			TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
			log.error("A事务回滚了");
		}

	}

	@Override
	@Transactional(rollbackFor = Exception.class,propagation = Propagation.NESTED)
	public void testB() {
		try {
			MyAppUser myAppUser = new MyAppUser();
			myAppUser.setUserNo("B" + UUID.randomUUID());
			myAppUser.setNickName("B事务");
			this.save(myAppUser);
		} catch (Exception e) {
			TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
			log.error("B事务回滚了");
		}
	}

结果:子事务的数据也没有存下来,说明在回滚父事务时,会把子事务也一起回滚(虽然没有进入子方法的catch里面)。
在这里插入图片描述
在这里插入图片描述

3.子事务回滚,但父事务未正确处理子事务异常

这个其实很好理解,子方法的异常抛到父方法,父方法没有额外用一个try catch专门处理调用子方法的异常,自然就导致父方法也回滚。

	@Autowired
	private IMyAppUserService myAppUserService;
	@Override
	@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
	public void testA() {
		try {
			MyAppUser myAppUser = new MyAppUser();
			myAppUser.setUserNo("A" + UUID.randomUUID());
			myAppUser.setNickName("A事务");
			this.save(myAppUser);
			myAppUserService.testB();
			int a=10/0;
		} catch (Exception e) {
			TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
			log.error("A事务回滚了");
		}
	}

	@Override
	@Transactional(rollbackFor = Exception.class,propagation = Propagation.NESTED)
	public void testB() {
		try {
			MyAppUser myAppUser = new MyAppUser();
			myAppUser.setUserNo("B" + UUID.randomUUID());
			myAppUser.setNickName("B事务");
			this.save(myAppUser);
		} catch (Exception e) {
			TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
			log.error("B事务回滚了");
			throw new RuntimeException("把异常抛出去");
		}
	}

结果:两个事务都回滚了。
在这里插入图片描述
在这里插入图片描述
结论:NESTED事务传播级别下,父事务和子事务存在影响关系,父事务回滚,子事务一定也回滚;子事务回滚,如果父事务正确处理子方法的异常,则父事务不会回滚。

总结论

  1. REQUIRED事务传播级别下,父子共用事务,随便一个回滚都会导致整个事务回滚。
  2. REQUIRES_NEW事务传播级别下,父事务和子事务是完全互相独立的事务,不会互相影响。
  3. NESTED事务传播级别下,父事务和子事务存在影响关系,父事务回滚,子事务一定也回滚;子事务回滚,如果父事务正确处理子方法的异常,则父事务不会回滚。

在某些场景下,如果子方法是用来处理一些不太重要的内容,希望这些子方法抛异常不会影响主流程,主流程回滚会回滚这些子方法,则可以考虑给这些子方法使用NESTED传播机制,例如日志打印。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值