前言:事务开发在工作过程中也是经常用到的,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;
}
参考链接: