Spring 之 @Transaction 详解

  • Post author:
  • Post category:其他

介绍

@Transaction 是 Spring 提供用来控制事务回滚/提交的一个注解,让我们从编程式注解转换到声明式注解。在这里就不做过多的撰述,今天主要来看下 @Transaction 里面的属性使用。

作用域

@Transaction 可以写在类、接口、方法上

  • 当标注在类上的时候:表示给该类所有的 public 方法添加上 @Transaction 注解
  • 当标注在接口上的时候:Spring 建议不要在接口或者接口方法上使用该注解,因为这只有在使用基于接口的代理时它才会生效。像 CGLib 动态代理采用继承的方式将会导致 @Transactional 注解失效
  • 当标注在方法上的时候:事务的作用域就只在该方法上生效,并且如果类及方法上都配置 @Transaction 注解时,方法的注解会覆盖类上的注解

属性

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Transactional {
    @AliasFor("transactionManager")
    String value() default "";

    @AliasFor("value")
    String transactionManager() default "";

    Propagation propagation() default Propagation.REQUIRED;

    Isolation isolation() default Isolation.DEFAULT;

    int timeout() default -1;

    boolean readOnly() default false;

    Class<? extends Throwable>[] rollbackFor() default {};

    String[] rollbackForClassName() default {};

    Class<? extends Throwable>[] noRollbackFor() default {};

    String[] noRollbackForClassName() default {};
}

可以看到 @Transaction 相关所有的属性值

字段名 类型 含义
value String 主要用来指定不同的事务管理器
满足在同一个系统中,存在不同的事务管理器
propagation enum: Propagation 可选的事务传播行为设置
isolation enum: Isolation 可选的事务隔离级别设置
readOnly boolean 读写或只读事务,默认读写
timeout int (in seconds granularity) 事务超时时间设置
rollbackFor Class对象数组,必须继承自Throwable 导致事务回滚的异常类数组
rollbackForClassName 类名数组,必须继承自Throwable 导致事务回滚的异常类名字数组
noRollbackFor Class对象数组,必须继承自Throwable 不会导致事务回滚的异常类数组
noRollbackForClassName 类名数组,必须继承自Throwable 不会导致事务回滚的异常类名字数组

value

value 主要用来指定不同的事务管理器,满足在同一个系统中,存在不同的事务管理器。如果在 Spring 中,配置了多个数据源声明了多个事务管理器,可以通过该参数来进行指定事务管理器。

propagation

事务传播行为有一下7种,默认是 REQUIRED 传播机制。

含义
REQUIRED

如果当前存在事务,则加入该事务;

如果当前不存在事务,则创建一个新的事务;

SUPPORTS

如果当前存在事务,则加入该事务;

如果当前不存在事务,则以非事务的方式继续运行;

MANDATORY

如果当前存在事务,则加入该事务;

如果当前不存在事务,则抛出异常;

REQUIRES_NEW

如果当前不存在事务,重新创建一个新的事务;

如果当前存在事务,则暂停当前的事务;

NOT_SUPPORTED

以非事务的方式运行

如果当前存在事务,则暂停当前的事务

NEVER

以非事务的方式运行

如果当前存在事务,则抛出异常

NESTED

如果当前存在事务,则在该事务内嵌套事务运行;

如果当前不存在事务,则创建一个新的事务;

这里比较容易引起疑问的是 REQUIRED 和 NESTED 有什么差别?也是面试的重灾区。

@Service
public class ServiceA{
    @Autowired
    private ServiceB serviceB;
    
    @Transaction
    public void A(){
        try{
            serviceB.B();
        }catch(Exception e){
            e.printStackTrace();
        } 
        //伪代码,执行数据库修改操作
    }
}
@Service
public class ServiceB{
    
    @Transaction(propagation = Propagation.REQUIRED)
    public void B(){
        //伪代码,执行数据库修改操作
    }
}

如上存在 ServiceA、ServiceB 两个类和A、B两个方法(这里指的异常都是 RuntimeException 异常或其子类)

  • 情况一:A方法中出现了异常,结果A、B方法修改操作都会被回滚
  • 情况二:B方法中出现了异常,结果A、B方法修改操作都会被回滚
@Service
public class ServiceB{
    
    @Transaction(propagation = Propagation.NESTED)
    public void B(){
        //伪代码,执行数据库修改操作
    }
}

接下来将B方法的 propagation 修改为 NESTED 事务传播机制

  • 情况一:A方法中出现了异常,结果A、B方法修改操作都会被回滚
  • 情况二:B方法中出现了异常,结果B方法修改操作被回滚,A方法修改操作提交

通过如上案例,希望可以帮助大家掌握这两个事务传播机制的差异。

isolation

isolation 对应的事务隔离级别与 MySQL 一致,有不熟悉的同学可以另外了解一下。主要用来避免脏读、不可重复读以及幻读的问题。

 

readOnly

设置为 true:表示只读,如果该方法内存在增、删、改操作则会抛出异常;

设置为false(默认):表示读写,增、删、改、查操作都允许;

rollbackFor 和 rollbackForClassName

这两个属性都是用来指定回滚的异常类型

  • rollbackFor:通过类进行指定,如@Transactional(rollbackFor = {Exception.class})
  • rollbackForClassName:通过类名进行指定,如@Transactional(rollbackForClassName = {“java.lang.Exception”})

可能有同学会有疑问,为什么需要知道回滚的异常类型呢?不是默认有异常就回滚嘛?

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

noRollbackFor 和 noRollbackForClassName

与 rollbackFor 和 rollbackForClassName 相反,是用来指定不回滚的异常类型,使用方法一致。

手动回滚

spring 也提供了 @Transaction 对应的手动回滚方式

TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();

如果在代码中一定要 catch 住异常记得使用手动回滚的方式或者重新抛出一个异常。

失效场景

接下来看一下场景的 @Transaction 注解失效的场景,希望大家以后能够避免踩坑:

  1. @Transactional 应用在非 public 修饰的方法上
  2. @Transactional 注解属性 propagation 设置问题
  3. @Transactional 注解属性 rollbackFor、noRollbackFor 设置问题
  4. 同一个类中方法调用,导致 @Transactional 失效
  5. 异常被 catch 导致 @Transactional 失效

总结

本文重点阐述了 @Transaction 注解的属性使用,帮助大家快速了解。希望能够有所帮助!!!


版权声明:本文为baidu_38083619原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。