spring事务失效

事务失效的几种类型

  1. 数据库引擎不支持事务。
  2. 没有被Spring管理。
  3. 方法不是public的。
  4. 自身调用问题。
  5. 数据源没有配置事务管理器。
  6. 不支持事务。
  7. 异常被吃了。
  8. 异常类型错误。

事务失效类型:

数据库引擎不支持事务

  这里以 MySQL 为例,其 MyISAM 引擎是不支持事务操作的,InnoDB 才是支持事务的引擎,一般要支持事务都会使用 InnoDB。
  根据 MySQL 的官方文档:https://dev.mysql.com/doc/refman/8.0/en/storage-engine-setting.html

没有被Spring管理

  Spring中的事务基于AOP实现,则事务类必须被Spring管理,进行代理,才能支持事务。

方法不是public的

  @Transaction只对方法名为public的才会生效,其他的不生效。private,static,final方法不能添加事务,添加了也不会生效。

自身调用问题

  1. service类中调用本类自己的方法,由于没有经过spring代理,事务不会生效。
  2. 一个无事务的方法调用另一个有事务的方法,事务是不会起作用的。这种情况,可以内部维护一个自己注入的bean,使用这个属性来调用。或者利用AOP上下文来获取代理对象,利用代理对象调用。
  3. 有事务的调用有事务的被调用的不能新开启事务。被调用的开启的新事务不会生效。
  4. 有事务的调用无事务的会生效。
  5. 无事务的调用无事务的,这种情况就会没有事务。

  事务是否生效主要看是否通过代理,没有通过代理就不会生效。

数据源没有配置事务管理器

  数据源必须开启事务管理器:

  1. @EnableTransactionManagement // 启注解事务管理,等同于xml配置方式的 <tx:annotation-driven />
  2. @EnableTransactionManagement 在springboot1.4以后可以不写。框架在初始化的时候已经默认给我们注入了两个事务管理器的Bean(JDBC的DataSourceTransactionManager和JPA的JpaTransactionManager ),其实这就包含了我们最常用的Mybatis和Hibeanate了。当然如果不是AutoConfig的而是自己自定义的,请使用该注解开启事务

不支持事务

  Propagation设置错误,Propagation用于配置事务的传播行为。Propagation.NOT_SUPPORTED: 表示不以事务运行,当前若存在事务则挂起。

异常被吃了

  异常被捕获了,然后不进行抛出,那么无法认为有异常,事务就不会回滚。在service中不应该进行事务的捕获,而进行抛出,在controller中进行异常捕获,这样既支持事务也捕获了异常。

异常类型错误

  Spring的事务管理默认是针对Error异常和RuntimeException异常以及其子类进行事务回滚。对runtimeException并不需要抛出,error需要抛出异常,并进行捕获。如果想对其他异常进行支持,则需要配置:@Transactional(rollbackFor = Exception.class)

业务和事务必须要在同一个线程中

  不在同一个线程,则事务影响不到。


事务的隔离级别

事务会引起的问题:

脏读:

  当A事务对数据进行修改,但是这种修改还没有提交到数据库中,B事务同时在访问这个数据,由于没有隔离,B获取的数据有可能被A事务回滚,这就导致了数据不一致的问题。

丢失修改:

  当A事务访问数据100,并且修改为100-1=99,同时B事务读取数据也是100,修改数据100-1=99,最终两个事务的修改结果为99,但是实际是98。事务A修改的数据被丢失了。

不可重复读:

  指A事务在读取数据X=100的时候,B事务把数据X=100修改为X=200,这个时候A事务第二次读取数据X的时候,发现X=200了,导致了在整个A事务期间,两次读取数据X不一致了,这就是不可重复读。

幻读:

  幻读和不可重复读类似。幻读表现在,当A事务读取表数据时候,只有3条数据,这个时候B事务插入了2条数据,当A事务再次读取的时候,发现有5条记录了,平白无故多了2条记录,就像幻觉一样。

  不可重复读的重点是修改: 同样的条件 , 你读取过的数据 , 再次读取出来发现值不一样了,重点在更新操作。
  幻读的重点在于新增或者删除:同样的条件 , 第 1 次和第 2 次读出来的记录数不一样,重点在增删操作。

Spring定义的隔离级别:

TransactionDefinition.ISOLATION_DEFAULT: 数据库的默认隔离级别,MySQL默认采用的 REPEATABLE_READ隔离级别。
TransactionDefinition.ISOLATION_READ_UNCOMMITTED: 最低的隔离级别,最低的隔离级别,允许读取未提交的数据变更,可能会导致脏读、幻读或不可重复读。
TransactionDefinition.ISOLATION_READ_COMMITTED: 允许读取并发事务已经提交的数据,未提交的不可读取,可以阻止脏读,但是幻读或不可重复读仍有可能发生。
TransactionDefinition.ISOLATION_REPEATABLE_READ: 对同一字段的多次重复读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生。MySQL中通过MVCC解决了该隔离级别下出现幻读的可能。
TransactionDefinition.ISOLATION_SERIALIZABLE: 串行化隔离级别,该级别可以防止脏读、不可重复读以及幻读,但是串行化会影响性能。

Propagation,传播行为:

指多个方法调用时,事务对多个方法之间传播的影响。

PROPAGATION_REQUIRED: 如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。这是最常见的选择。
PROPAGATION_SUPPORTS: 支持当前事务,如果当前没有事务,就以非事务方式执行。
PROPAGATION_MANDATORY: 使用当前的事务,如果当前没有事务,就抛出异常。
PROPAGATION_REQUIRES_NEW: 新建事务,如果当前存在事务,把当前事务挂起。
PROPAGATION_NOT_SUPPORTED: 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
PROPAGATION_NEVER: 以非事务方式执行,如果当前存在事务,则抛出异常。
PROPAGATION_NESTED: 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。