事务的使用:

        Spring boot的事务管理非常简单,只需要在需要开启事务的方法添加@Transactional注解即可,如需添加事务传播行为和事务隔离级别,则在括号后面添加propagation属性和isolation属性。例如:@Transactional(propagation = Propagation.REQUIRES_NEW,isolation = Isolation.SERIALIZABLE)


事务的属性(传播行为和隔离级别):

  • 事务的传播行为:一个开启事务的方法A调用另外一个开启事务的方法B,两个方法都有事务那究竟该使用哪个事务,这便是事务的传播行为。@Transactional注解中的propagation属性用来指定传播行为。Spring提供的传播行为有7种,使用得比较多的就是以下两种。
  1. Propagation.REQUIRED 默认值,使用原来的事务
  2. Propagation.REQUIRED_NEW,将原来的事务先挂起,开启一个新的事务
  • 事务的隔离级别:当指定隔离级别或者隔离级别指定为DEFAULT时,都是和数据库保持同一个隔离级别,@Transactional注解中的Isolation属性用来指定隔离级别。当Isolation指定的隔离级别跟数据库不一致时,使用的是Isolation指定的隔离级别。
  1. Isolation = Isolation.DEFAULT    默认与数据库保持一致
  2. Isolation = Isolation.READ_UNCOMMITTED 可未提交
  3. Isolation  = Isolation.READ_COMMITTED 读已提交
  4. Isolation = Isolation.REPEATABLE_READ  可重复读
  5. 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的事务管理