事务的使用:
Spring boot的事务管理非常简单,只需要在需要开启事务的方法添加@Transactional注解即可,如需添加事务传播行为和事务隔离级别,则在括号后面添加propagation属性和isolation属性。例如:@Transactional(propagation = Propagation.REQUIRES_NEW,isolation = Isolation.SERIALIZABLE)
事务的属性(传播行为和隔离级别):
- 事务的传播行为:一个开启事务的方法A调用另外一个开启事务的方法B,两个方法都有事务那究竟该使用哪个事务,这便是事务的传播行为。@Transactional注解中的propagation属性用来指定传播行为。Spring提供的传播行为有7种,使用得比较多的就是以下两种。
- Propagation.REQUIRED 默认值,使用原来的事务
- Propagation.REQUIRED_NEW,将原来的事务先挂起,开启一个新的事务
- 事务的隔离级别:当指定隔离级别或者隔离级别指定为DEFAULT时,都是和数据库保持同一个隔离级别,@Transactional注解中的Isolation属性用来指定隔离级别。当Isolation指定的隔离级别跟数据库不一致时,使用的是Isolation指定的隔离级别。
- Isolation = Isolation.DEFAULT 默认与数据库保持一致
- Isolation = Isolation.READ_UNCOMMITTED 可未提交
- Isolation = Isolation.READ_COMMITTED 读已提交
- Isolation = Isolation.REPEATABLE_READ 可重复读
- Isolation = Isolation.SERIALIZABLE 序列化
事务失效:
上图可以清楚看到B方法的传播行为是REQUIRED_NEW,也就是执行到B方法的时候,先挂起A方法的事务,然后B方法新开启一个事务。也就是执行完B方法后,出现异常,按道理也不会回滚B方法,只会回滚A方法,也就是数据库应该存在一条UserName是B的数据。
但是,执行完发现,数据库一条数据也没有,问题已经很明显了,B方法的事务不起作用。为什么会这样呢,那是因为@Transactional注解是基于AOP实现的,一定要用代理对象去调用才会生效,但是上面的代码明显是用this在调用,不是代理对象,所以事务失效了。
修改代码,注入代理对象调用B方法,再次执行!
此时发现数据库有数据了,B方法的事务生效了。
传播行为设置不当,导致隔离级别失效:
B方法此时开启的隔离级别是读未提交,也就是按道理来说可以读到未提交的脏数据
首先在数据库,开启事务,更新数据,但是先不提交,那么此时就会有脏数据,B方法此时开启的隔离级别是读未提交,也就是按道理来说可以读到这条未提交的脏数据
但是,奇怪的是,设置读未提交,读取出来的数据居然是已提交的数据而不是脏数据,这是因为B方法没有设置隔离级别,没有设置隔离级别那么默认就是Propagation.REQUIRED,使用原来的事务,也就是A方法的事务,而A方法没有设置隔离级别,没有设置隔离级别默认就是和数据库保持一个隔离级别,也就是可重复的隔离级别,所以读取到才是已提交的干净数据。
将隔离级别设置为REQUIRED_NEW,即B方法自己新开一个事务。
B方法新开一个事务,便会使用自己设置的隔离级别,即读未提交,那么此时读取到的就是未提交的脏数据了。
更多推荐
高频面试题之SpringBoot的事务管理
发布评论