@Transaction 是 Spring 提供用来控制事务回滚/提交的一个注解,让我们从编程式注解转换到声明式注解。在这里就不做过多的撰述,今天主要来看下 @Transaction 里面的属性使用。
@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 主要用来指定不同的事务管理器,满足在同一个系统中,存在不同的事务管理器。如果在 Spring 中,配置了多个数据源声明了多个事务管理器,可以通过该参数来进行指定事务管理器。
事务传播行为有一下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 异常或其子类)
@Service
public class ServiceB{
@Transaction(propagation = Propagation.NESTED)
public void B(){
//伪代码,执行数据库修改操作
}
}
接下来将B方法的 propagation 修改为 NESTED 事务传播机制
通过如上案例,希望可以帮助大家掌握这两个事务传播机制的差异。
isolation 对应的事务隔离级别与 MySQL 一致,有不熟悉的同学可以另外了解一下。主要用来避免脏读、不可重复读以及幻读的问题。
设置为 true:表示只读,如果该方法内存在增、删、改操作则会抛出异常;
设置为false(默认):表示读写,增、删、改、查操作都允许;
这两个属性都是用来指定回滚的异常类型
可能有同学会有疑问,为什么需要知道回滚的异常类型呢?不是默认有异常就回滚嘛?
Spring默认抛出了未检查unchecked异常(继承自 RuntimeException 的异常)或者 Error才回滚事务;其他异常不会触发回滚事务。如果在事务中抛出其他类型的异常,但却期望 Spring 能够回滚事务,就需要指定 rollbackFor 或者 rollbackForClassName 属性。
与 rollbackFor 和 rollbackForClassName 相反,是用来指定不回滚的异常类型,使用方法一致。
spring 也提供了 @Transaction 对应的手动回滚方式
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
如果在代码中一定要 catch 住异常记得使用手动回滚的方式或者重新抛出一个异常。
接下来看一下场景的 @Transaction 注解失效的场景,希望大家以后能够避免踩坑:
本文重点阐述了 @Transaction 注解的属性使用,帮助大家快速了解。希望能够有所帮助!!!