聊聊 @Transactional 事务注解

@Transactional 注解是 Spring 开发中很常用的一个注解,它能保证方法内多个数据库操作要么同时成功、要么同时失败。

但使用 @Transactional 注解时需要注意许多的细节,不然你会发现@Transactional总是莫名其妙的就失效了。

一、@Transactional 注解使用

@Transactional 可以作用在接口、类、类方法

  • 作用于类:当把 @Transactional 注解放在类上时,表示所有该类的public方法都配置相同的事务属性信息。

  • 作用于方法:当类配置了 @Transactional,方法也配置了 @Transactional,方法的事务会覆盖类的事务配置信息。

  • 作用于接口:不推荐这种使用方法,因为一旦标注在 Interface 上并且配置了 Spring AOP 使用 CGLib 动态代理,将会导致 @Transactional 注解失效

@Transactional 注解的属性

propagation 属性

propagation 代表事务的传播行为,默认值为 Propagation.REQUIRED,其他的属性信息如下:

  • Propagation.REQUIRED:若当前存在事务,则加入该事务,若不存在事务,则创建一个新的事务。

  • Propagation.SUPPORTS:若当前存在事务,则加入该事务;若不存在事务,则以非事务的方式继续运行。

  • Propagation.MANDATORY:若当前存在事务,则加入该事务;若不存在事务,则抛出异常。

  • Propagation.REQUIRES_NEW:重新创建一个新的事务,若当前存在事务,暂停当前的事务。

  • Propagation.NOT_SUPPORTED:以非事务的方式运行,若当前存在事务,暂停当前的事务。

  • Propagation.NEVER:以非事务的方式运行,若当前存在事务,则抛出异常。

isolation 属性

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

  • Isolation.DEFAULT:使用底层数据库默认的隔离级别。
  • Isolation.READ_UNCOMMITTED
  • Isolation.READ_COMMITTED
  • Isolation.REPEATABLE_READ
  • Isolation.SERIALIZABLE

timeout 属性

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

readOnly 属性

readOnly :指定事务是否为只读事务,默认值为 false。

若一次执行多条查询语句,且多条查询 SQL 需保证整体的读一致性时,设置为 true;否则,在前条 SQL 查询之后,后条 SQL 查询之前,数据被其他用户改变,则该次整体的统计查询将会出现读数据不一致的状态。

rollbackFor 属性

rollbackFor :用于指定能够触发事务回滚的异常类型,可以指定多个异常类型。

noRollbackFor 属性

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

二、@Transactional 失效场景

Spring 中对注解的解析都是基于代理的,如果目标方法无法被 Spring 代理到,那么它将无法被 Spring 进行事务管理。

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

如果 @Transactional 注解应用在非public 修饰的方法上,Transactional将会失效。

protected TransactionAttribute computeTransactionAttribute(Method method,
    Class targetClass) {
        // Don't allow no-public methods as required.
        if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
        return null;
}

此方法会检查目标方法的修饰符是否为 public,不是 public 则不会获取 @Transactional 的属性配置信息。

注意:protectedprivate 修饰的方法上使用 @Transactional 注解,虽然事务无效,但不会有任何报错。

2、被final、static关键字修饰的类或方法

CGLib 是通过生成目标类子类的方式生成代理类的,被 final、static 修饰后,无法继承父类与父类的方法。

3、@Transactional 注解属性 propagation 设置错误

这种失效是由于配置错误,若是错误的配置以下三种 propagation,事务将不会发生回滚。

PROPAGATION_SUPPORTS:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。

PROPAGATION_NOT_SUPPORTED:以非事务方式运行,如果当前存在事务,则把当前事务挂起。

PROPAGATION_NEVER:以非事务方式运行,如果当前存在事务,则抛出异常。

4、@Transactional 注解属性 rollbackFor 设置错误

rollbackFor 可以指定能够触发事务回滚的异常类型。Spring 默认抛出了未检查unchecked异常(继承自 RuntimeException 的异常)或者 Error 才回滚事务;其他异常不会触发回滚事务。

如果在事务中抛出其他类型的异常,但却期望 Spring 能够回滚事务,就需要指定 rollbackFor 属性。

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

在同一个类中有 A、B 两个方法,A 方法调用 B 方法。若 A 未声明注解事务,B 无论是否声明注解事务,是否为 public,方法 B 的事务是不会起作用的。

那为啥会出现这种情况?其实这还是由于使用 Spring AOP 代理造成的,因为只有当事务方法被当前类以外的代码调用时,才会由 Spring 生成的代理对象来管理。

6、异常被 catch 导致 @Transactional 失效

7、数据库引擎不支持事务

这种情况出现的概率并不高,事务能否生效数据库引擎是否支持事务是关键。常用的MySQL数据库默认使用支持事务的innodb引擎。一旦数据库引擎切换成不支持事务的myisam,那事务就从根本上失效了。

你可能感兴趣的:(聊聊 @Transactional 事务注解)