对Spring中的事务传播行为与事务隔离级别的理解

前言:事务开发在工作过程中也是经常用到的,Spring提供了声明式事务,借助于AOP以实现对应用程序无侵入的实现事务功能,而事务有关的知识,最主要的应该就是事务的传播(Spring提供的工具与规范)与事务隔离级别(数据库自身)了。下面对这两个知识点进行一次总结和记录。

目录

什么是事务?

如何理解事务的隔离级别?

如何理解事务传播行为?


什么是事务?

       事务是一个由有限操作集合组成的逻辑单元。事务操作包含两个目的,数据一致以及操作隔离。数据一致是指事务提交时保证事务内的所有操作都成功完成,并且更改永久生效;事务回滚时,保证能够恢复到事务执行之前的状态。操作隔离则是指多个同时执行的事务之间应该相互独立,互不影响。

       事务是一个比较广泛的概念,事务管理资源除了我们熟知的数据库外,还可以包含消息队列、文件系统等。当然,一般来说,我们说的事务单指“数据库事务”。接下来我们会以MySQL数据库、Spring声明式事务为主要研究对象,但是很多事务概念、接口抽象和实现方式同时适用于其他情况。

如何理解事务的隔离级别?

先列出事务隔离级别分类:

MySQL的InnoDB引擎提供四种隔离级别(即ACID中的I):读未提交(READ UNCOMMITTED)、读已提交(READ COMMITTED)、可重复读(REPEATABLE READ)和串行化(SERIALIZABLE)。

个人认为对于隔离级别从锁的角度来分析会更容易理解,也避免陷入脏读,不可重复读,幻读的漩涡。(我个人刚看这几个词时时很恍惚的)注意:这些读写问题指的均是一个事务内的问题。

READ UNCOMMITTED:

读未提交。即是对写操作数据不进行隔离,读不加任何锁,故导致读到了某些未提交的数据,但是数据最终回滚了,以至于读到了错误数据。

READ COMMITTED:

读已提交。即对写操作的数据进行隔离(未提交前对其他事务不可见,貌似有了读锁(想到了Spring循环依赖3层缓存...)),因此不会读到未提交的信息。

REPEATABLE READ:

可重复读。即多次读取的数据是同样的信息,使用读写锁,读写串行,会导致效率降低。但MySQL可以使用MVCC来实现可重复读,其可以保证读写并行。

SERIALIZABLE:

串行化。即使用读写锁,(理论上)读写串行。(值得注意的是串行化的时候并不一定都是表锁串行,这个跟索引及操作数据有关,索引,间隙锁等,需要深入了解...)

参考链接:

MySQL 事务的实现原理,写得太好了!_乐字节小乐的博客-CSDN博客_mysql事务实现原理

MySQL事务隔离级别的实现原理 - 废物大师兄 - 博客园

如何理解事务传播行为?

按照传播特性分类:

与上层属于同一事务:REQUIRED,SUPPORTS;(下层事务try会报rollback异常,因为是同一个事务,try后下层异常无法传递,导致无法回滚,但由于未提交效果上等同于同时回滚)

新建一个事务:REQUIRES_NEW,NESTED;(下层事务需try,才会体现。否则抛出异常传递到上层,对上层来说还是异常了。当然也会回滚)它们两个的区别是,REQUIRES_NEW上层异常不会使下层回滚,而NESTED会使下层也回滚

其他:

NOT_SUPPORTED,无事务,但抛出异常上层会回滚,一样的道理。

NEVER,上层有事务,则抛出异常。

MANDATORY,上层无事务,则抛出异常。

这里顺便说明一下,Spring中事务失效的情况,按照事务的实现来分析就好记了:1:数据库不支持或未开启事务。(事务本身实现基于数据库)2:非IOC中的对象。(声明式事务必须是Spring管理的bean)3:自调用,private,final方法等。(Spring事务实现依赖于AOP(代理))

测试代码未补全,后面补上测试代码后可以更清晰的看到他们的区别。//TODO


@Test
	//@Transactional(propagation = Propagation.REQUIRED)
	public void updateParent() {
		WaterConfigDo record = new WaterConfigDo();
		record.setId(10L);
		record.setRemark("modified by REQUIRED_NEW");
		waterConfigMapper.updateByPrimaryKeySelective(record);
		transactionTest.updateChildren();
		int error = 1/0;
    }
	
	@Transactional(propagation = Propagation.REQUIRED)
	public void updateChildren() {
		WaterInfoDo record = new WaterInfoDo();
		record.setWaterId(10L);
		record.setRemark("modified by REQUIRED_NEW");
		waterInfoMapper.updateByPrimaryKeySelective(record);
		int error = 1/0;
    }

    @Test
	//@Transactional(propagation = Propagation.REQUIRED)
	public void updateParent() {
		WaterConfigDo record = new WaterConfigDo();
		record.setId(10L);
		record.setRemark("modified by REQUIRED_SUPPORTS");
		waterConfigMapper.updateByPrimaryKeySelective(record);
		transactionTest.updateChildren();
		int error = 1/0;
    }
	
	@Transactional(propagation = Propagation.SUPPORTS)
	public void updateChildren() {
		WaterInfoDo record = new WaterInfoDo();
		record.setId(10L);
		record.setRemark("modified by REQUIRED_SUPPORTS");
		waterInfoMapper.updateByPrimaryKeySelective(record);
		int error = 1/0;
    }

 

    @Test
	@Transactional(propagation = Propagation.REQUIRED)
	public void updateParent() {
		WaterConfigDo record = new WaterConfigDo();
		record.setId(10L);
		record.setRemark("modified by REQUIRED");
		waterConfigMapper.updateByPrimaryKeySelective(record);
		transactionTest.updateChildren();
		int error = 1/0;
    }
	
	@Transactional(propagation = Propagation.SUPPORTS)
	public void updateChildren() {
		WaterInfoDo record = new WaterInfoDo();
		record.setId(10L);
		record.setRemark("modified by SUPPORTS");
		waterInfoMapper.updateByPrimaryKeySelective(record);
		// int error = 1/0;
    }

 

    @Test
	@Transactional(propagation = Propagation.REQUIRED)
	public void updateParent() {
		WaterConfigDo record = new WaterConfigDo();
		record.setId(10L);
		record.setRemark("modified by REQUIRED");
		waterConfigMapper.updateByPrimaryKeySelective(record);
		transactionTest.updateChildren();
		int error = 1/0;
    }
	
	@Transactional(propagation = Propagation.REQUIRES_NEW)
	public void updateChildren() {
		WaterInfoDo record = new WaterInfoDo();
		record.setId(10L);
		record.setRemark("modified by REQUIRES_NEW");
		waterInfoMapper.updateByPrimaryKeySelective(record);
		// int error = 1/0;
    }
    @Transactional(propagation = Propagation.REQUIRED)
	public void updateParent() {
		WaterConfigDo record = new WaterConfigDo();
		record.setId(10L);
		record.setRemark("modified by REQUIRED");
		waterConfigMapper.updateByPrimaryKeySelective(record);
		transactionTest.updateChildren();
		int error = 1/0;
    }
	
	@Transactional(propagation = Propagation.REQUIRES_NEW)
	public void updateChildren() {
		WaterInfoDo record = new WaterInfoDo();
		record.setId(10L);
		record.setRemark("modified by REQUIRES_NEW");
		waterInfoMapper.updateByPrimaryKeySelective(record);
		// int error = 1/0;
    }

 参考链接:

Spring事物传播级别NESTED和REQUIRES_NEW的区别 - 简书

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值