spring中事务的传播行为

前言

问题

​ **TransactionDefinition中的各传播行为到底是什么?**想要做个代码测试来彻底了解一下。以下是网上的一些文字描述:

  • PROPAGATION_REQUIREDE:如果当前存在一个事务,则加入当前事务。如果不存在任何事务,则创建一个新的事务。总之,要至少保证在一个事务中运行。PROPAGATION_REQUIRED常作为默认的事务传播行为。

  • PROPAGATION_SUPPORTS:如果当前存在一个事务,则加入当前事务。如果当前不存在事务,则直接执行。对于一些查询方法来说,PROPAGATION_SUPPORTS通常是比较合适的传播行为选择。如果当前方法直接执行,那么不需要事务的支持。如果当前方法被其他方法调用,而其他方法启动了一个事务,使用PROPAGATION_SUPPORTS可以保证当前方法能够加入当前事务,并洞察当前事务对数据资源所做的更新。、

  • PROPAGATION_MANDATORY: PROPAGATION_MANDATORY强制要求当前存在一个事务,如果不存在,则抛出异常。如果某个方法需要事务支持,但自身又不管理事务提交或者回滚,那么比较适合使用PROPAGATION_MANDATORY

  • PROPAGATION_REQUIRES_NEW:不管当前是否存在事务,都会创建新的事务。如果当前存在事务,会将当前的事务挂起(Suspend)。如果某个业务对象所做的事情不想影响到外层事务,PROPAGATION_REQUIRES_EW应该是合适的选择。比如,假设当前的业务方法需要向数据库中更新某些日志信息,但即使这些日志信息更新失败,我们也不想因为该业务方法的事务回演, 而影响到外层事务的成功提交。因为这种情况下,当前业务方法的事务成功与否对外层事务来说是无关紧要的。

  • PROPAGATION_NOT_SUPPORTE:不支持当前事务,而是在没有事务的情况下执行。如果当前存在事务的话,当前事务原则上将被挂起(Suspend),但这要看对应的PlatformTransaction- Manager实现类是否支持事务的挂起。

  • PROPAGATION-NEVER:永远不需要当前存在事务,如果存在当前事务,则抛出异常

  • PROPAGATION_NESTED。如果存在当前事务,则在当前事务的一个嵌套事务中执行,否则与PRPAGATION_REQUIRED的行为类似,即创建新的事务,在新创建的事务中执行。

测试目的

​ 将上述文字描述用代码的方式演绎一遍,判断与理解是否有误。由于测试繁琐,因此测试只针对部分传播行为进行。

​ 测试传播行为:

  • PROPAGATION_REQUIREDE
  • PROPAGATION_REQUIRES_NEW
  • PROPAGATION_NOT_SUPPORTED
  • PROPAGATION-NEVER

正文

测试准备

​ 主要准备三个事务,再嵌套的基础上查看执行后的数据库结果。每次测试完后数据库结果还原回最开始的状态。

测试代码

/**
 * 测试事务的使用
 */
@Service
public class TransactionTest {
	private static Logger logger = LoggerFactory.getLogger(TransactionTest.class);
	private static DefaultTransactionDefinition definition;
	private static DefaultTransactionDefinition definition2;
	private static DefaultTransactionDefinition definition3;
    private static DefaultTransactionDefinition definition4;

	@Resource
	BookMapper bookMapper;
	@Resource(name = "transactionManager")
	private DataSourceTransactionManager transactionManager;

	{
		definition = new DefaultTransactionDefinition();
		definition2 = new DefaultTransactionDefinition(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
		definition3 = new DefaultTransactionDefinition(TransactionDefinition.PROPAGATION_NOT_SUPPORTED);
        definition4 = new DefaultTransactionDefinition(TransactionDefinition.PROPAGATION_NEVER);
	}

	/**
	 * 开始入口
	 */
	public void start() throws InterruptedException {
		transaction1();
	}

	/**
	 * 事务1
	 */
	public void transaction1() throws InterruptedException {
		TransactionStatus transaction = transactionManager.getTransaction(definition);
		logger.info("事务1开始执行...");
		bookMapper.updateBook(new BookDo("音乐书", 6, "这是一首优美的音乐"));
		transaction2();
		transaction3();
		transactionManager.commit(transaction);
	}

	/**
	 * 事务2
	 */
	public void transaction2() throws InterruptedException {
		TransactionStatus transaction = transactionManager.getTransaction(definition);
		logger.info("事务2开始执行...");
		BookDo bookDo = bookMapper.selectBook(6);
		logger.info("查询出来的book为:" + bookDo);
	}

	/**
	 * 事务3
	 */
	public void transaction3() {
		TransactionStatus transaction = transactionManager.getTransaction(definition);
		logger.info("事务3开始执行...");
		bookMapper.updateBook(new BookDo("心理书", 6, "这是一本心理书籍"));
		transactionManager.commit(transaction);
	}
}

数据库

在这里插入图片描述

测试开始

PROPAGATION_REQUIREDE

​ 由于DefaultTransactionDefinition类默认传播行为即PROPAGATION_REQUIREDE,因此使用构造方法直接创建该类。

  1. 结果预期:

    按照文字描述,PROPAGATION_REQUIREDE行为会在事务1开启时创建一个事务,事务2和事务3由于是被事务1的方法调用,因此被嵌套进事务1当中,当commit之后,数据库中只会收到一个事务。因此事务2中select出来的结果应该是音乐书(我们姑且把查询语句也当成事务理解),而数据库中的最终修改结果应该为心理书

  2. 测试截图:

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

  1. 测试结果:

​ 符合预期。

PROPAGATION_REQUIRES_NEW

  1. 代码改动部分,其余保持不变:
    在这里插入图片描述

  2. 结果预期:

    根据文字描述,PROPAGATION_REQUIRES_NEW为每次操作创建一个新的事务,所以我们推断会有两个事务提交到数据库,但又因为PROPAGATION_REQUIRES_NEW会将外层事务挂起,因此总共应该有三个事务被提交到了数据库。由于mysql的模式事务隔离级别为可重复读,因此事务2查询出来的结果应该为地理书,事务1被挂起,事务3会等待事务1解锁后执行,由于事务3又是被嵌套在事务1的方法里的,根据子方法事务先完成原理,事务1又会等待事务3完成后才会commit。这时就会出现死锁的情况。超时后程序报错,而数据库结果未改动,仍然是地理书

  3. 测试截图:

    执行**SELECT * FROM INFORMATION_SCHEMA.INNODB_TRX;**查看当前数据库的事务情况。

在这里插入图片描述

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

  1. 测试结果:

    符合预期。

PROPAGATION_NOT_SUPPORTED

  1. 代码变动部分,其余保持不变:

在这里插入图片描述

  1. 测试结果:

    PROPAGATION_REQUIRES_NEW中测试结果一致。

PROPAGATION-NEVER

  1. 代码变动部分,其余保持不变:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eSwztjGc-1673938110694)(C:\Users\xxd\AppData\Roaming\Typora\typora-user-images\image-20230117144412575.png)]

  1. 结果预期:

    根据文字描述,当使用PROPAGATION-NEVER传播行为时,当执行的当前事务被另一个事务所嵌套,则抛出异常。因此程序不能得到正确的执行。

  2. 测试截图:

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Pfc9pIrv-1673938110694)(C:\Users\xxd\AppData\Roaming\Typora\typora-user-images\image-20230117144628085.png)]

  3. 测试结果:

    符合预期。

参考

https://www.jianshu.com/p/42c835193e92,作者:七离_82cd,来源:简书

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值