1 简要说明

声明式事务管理建立在 AOP 之上的。其本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务。

简而言之,@Transactional 注解在代码执行出错的时候能够进行事务的回滚。

2 使用说明

  1. 在启动类上添加 @EnableTransactionManagement 注解。

  2. 在类上使用,类的所有 public 方法将都具有该类型的事务属性

  3. 也可以在方法级别使用该标注来覆盖类级别的定义。

3 属性

propagation

propagation 代表事务的传播行为,默认值为 Propagation.REQUIRED。

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

rollbackFor & noRollbackFor

  • rollbackFor

在 @Transactional 注解中如果不配置 rollbackFor 属性,那么事物只会在遇到 RuntimeException 的时候才会回滚,加上 rollbackFor=Exception.class, 可以让事物在遇到非运行时异常时也回滚。
而至于什么是运行时异常 (RuntimeException),什么是非运行时异常,可通过下图所示理解(图片截取网络)

在这里插入图片描述

  • noRollbackFor
    抛出指定的异常类型,不回滚事务,也可以指定多个异常类型。

isolation

isolation :事务的隔离级别,默认值为 Isolation.DEFAULT。

隔离级别 说明
TransactionDefinition.ISOLATION_DEFAULT 使用后端数据库默认的隔离级别,Mysql 默认采用的 REPEATABLE_READ隔离级别 Oracle 默认采用的 READ_COMMITTED隔离级别.
TransactionDefinition.ISOLATION_READ_UNCOMMITTED 最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读
TransactionDefinition.ISOLATION_READ_COMMITTED 允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生
TransactionDefinition.ISOLATION_REPEATABLE_READ 对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生。
TransactionDefinition.ISOLATION_SERIALIZABLE 最高的隔离级别,完全服从ACID的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。但是这将严重影响程序的性能。通常情况下也不会用到该级别。

timeout

事务的超时时间,默认值为 -1。如果超过该时间限制但事务还没有完成,则自动回滚事务。

readOnly

指定事务是否为只读事务,默认值为 false;为了忽略那些不需要事务的方法,比如读取数据,可以设置 read-only 为 true。

4 注解失效问题

正常情况下,只要在方法上添加 @Transactional 注解就完事了,但是需要注意的是,虽然使用简单,但是如果不合理地使用注解,还是会存在注解失效的问题。

@Transactional 应用在非 public 修饰的方法上

事务拦截器在目标方法执行前后进行拦截,内部会调用方法来获取 Transactional 注解的事务配置信息,调用前会检查目标方法的修饰符是否为 public,不是 public 则不会获取 @Transactional 的属性配置信息。

@Transactional 注解属性 rollbackFor 设置错误

rollbackFor 可以指定能够触发事务回滚的异常类型。Spring 默认抛出了未检查 unchecked 异常(继承自 RuntimeException 的异常)或者 Error 才回滚事务;其他异常不会触发回滚事务。如果在事务中抛出其他类型的异常,但却期望 Spring 能够回滚事务,就需要指定 rollbackFor 属性。

同一个类中方法调用,导致 @Transactional 失效

开发中避免不了会对同一个类里面的方法调用,比如有一个类 Test,它的一个方法 A,A 再调用本类的方法 B(不论方法 B 是用 public 还是 private 修饰),但方法 A 没有声明注解事务,而 B 方法有。则外部调用方法 A 之后,方法 B 的事务是不会起作用的。这也是经常犯错误的一个地方。
那为啥会出现这种情况?其实这还是由于使用 Spring AOP 代理造成的,因为只有当事务方法被当前类以外的代码调用时,才会由 Spring 生成的代理对象来管理。

异常被你的 catch “吃了” 导致 @Transactional 失效

如果你手动的 catch 捕获这个异常并进行处理,事务管理器会认为当前事务应该正常 commit,就会导致注解失效,如果非要捕获且不失效,就必须在代码块内 throw new Exception 抛出异常。

数据库引擎不支持事务

开启事务的前提就是需要数据库的支持,我们一般使用的 Mysql 引擎时支持事务的,所以一般不会出现这种问题。

开启多线程任务时,事务管理会受到影响

因为线程不属于 spring 托管,故线程不能够默认使用 spring 的事务,也不能获取 spring 注入的 bean 在被 spring 声明式事务管理的方法内开启多线程,多线程内的方法不被事务控制。
如下代码,线程内调用 insert 方法,spring 不会把 insert 方法加入事务就算在 insert 方法上加入 @Transactional 注解,也不起作用。

更详细了内容查看 https://www.w3cschool.cn/article/34084832.html