事务隔离级别:
- 脏读:读取到了被回滚的数据,它毫无意义。
- 虚读(不可重复读):由于其他事务更新数据,两次读取的数据不一致。
- 幻读:由于其他事务执行插入删除操作,而又无法感知到表中记录条数发生变化,当下次再读取时会莫名其妙多出或缺失数据,就像产生幻觉一样。
(对于虚读和幻读的区分:虚读是某个数据前后读取不一致,幻读是整个表的记录数量前后读取不一致)
使用Spring事务管理
- transactionManager:指定事务管理器
- propagation:事务传播规则,一个事务可以包括N个子事务
- isolation:事务隔离级别,不多说了
- timeout:事务超时时间
- readOnly:是否为只读事务,不同的数据库会根据只读属性进行优化,比如MySQL一旦声明事务为只读,那么久不允许增删改操作了。
- rollbackFor和noRollbackFor:发生指定异常时回滚或是不回滚,默认发生任何异常都回滚
Spring默认的传播级别是PROPAGATION_REQUIRED
,那么我们来看看,它是如何传播的,现在我们的Service
类中一共存在两个事务,而一个事务方法包含了另一个事务方法:
@Transactional
public void test() {
test2();
if(true) throw new RuntimeException("我是测试异常!"); //发生异常时,会回滚另一个事务吗?
}
@Transactional
public void test2() {
mapper.insertStudent();
}
最后我们得到结果,另一个事务被回滚了,也就是说,相当于另一个事务直接加入到此事务中了,也就是表中所描述的那样。
如果单独执行test2()
则会开启一个新的事务,而执行test()
则会直接让内部的test2()
加入到当前事务中。
@Transactional
public void test() {
test2();
}
@Transactional(propagation = Propagation.SUPPORTS)
public void test2() {
mapper.insertStudent();
if(true) throw new RuntimeException("我是测试异常!");
}
现在我们将test2()
的传播级别设定为SUPPORTS
,那么这时如果单独调用test2()
方法,并不会以事务的方式执行,当发生异常时,虽然依然存在AOP增强,但是不会进行回滚操作,而现在再调用test()
方法,才会以事务的方式执行。
我们接着来看MANDATORY
,它非常严格,如果当前方法并没有在任何事务中进行,会直接出现异常:
@Transactional
public void test() {
test2();
}
@Transactional(propagation = Propagation.MANDATORY)
public void test2() {
mapper.insertStudent();
if(true) throw new RuntimeException("我是测试异常!");
}
NESTED
级别表示如果存在外层事务,则此方法单独创建一个子事务,回滚只会影响到此子事务,实际上就是利用创建Savepoint,然后回滚到此保存点实现的。NEVER
级别表示此方法不应该加入到任何事务中,其余类型适用于同时操作多数据源情况下的分布式事务管理,这里暂时不做介绍。