@Transaction注解及失效详解
@Transaction注解回滚失效详解
@Transaction特性
1、@Transactional 注解只能应用到接口方法、类、还有public方法上。
2、一般在service类标签上添加@Transactional,这样可以将整个service类纳入spring事务管理,在每个业务方法执行时都会开启一个事务,不过这些事务采用相同的管理方式。
3、默认情况下,Spring会对unchecked异常进行事务回滚;如果是checked异常则不回滚。
checked异常:Error或者RuntimeException(比如空指针,1/0)的异常;
unchecked异常:继承自java.lang.Exception得异常统称为Checked Exception,如IOException、TimeoutException等。
4、只读事务:
@Transactional(propagation=Propagation.NOT_SUPPORTED,readOnly=true)
只读标志只在事务启动时应用,否则即使配置也会被忽略。
启动事务会增加线程开销,数据库因共享读取而锁定(具体跟数据库类型和事务隔离级别有关)。通常情况下,仅是读取数据时,不必设置只读事务而增加额外的系统开销。
@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 | 不会导致事务回滚的异常类名字数组 |
propagation事务传播行为有一下7种,默认是 REQUIRED 传播机制。
值 | 含义 |
REQUIRED | 如果当前存在事务,则加入该事务; 如果当前不存在事务,则创建一个新的事务; |
SUPPORTS | 如果当前存在事务,则加入该事务; 如果当前不存在事务,则以非事务的方式继续运行; |
MANDATORY | 如果当前存在事务,则加入该事务; 如果当前不存在事务,则抛出异常; |
REQUIRES_NEW | 如果当前不存在事务,重新创建一个新的事务; 如果当前存在事务,则暂停当前的事务; |
NOT_SUPPORTED | 以非事务的方式运行 如果当前存在事务,则暂停当前的事务 |
NEVER | 以非事务的方式运行 如果当前存在事务,则抛出异常 |
NESTED | 如果当前存在事务,则在该事务内嵌套事务运行; 如果当前不存在事务,则创建一个新的事务; |
isolation事务隔离级别,有4种
隔离级别 | 名称 | 含义 | 脏读 | 不可重复读 | 幻读 |
读未提交 | ISOLATION_READ_UNCOMMITTED | T1读取T2未提交的东西 | √ | √ | √ |
读已提交 | ISOLATION_READ_COMMITTED | oracle默认,事务提交后可读 | × | √ | √ |
可重复读 | ISOLATION_REPEATABLE_READ | mysql默认,针对update操作;会出现幻读现象,幻读针对的是insert操作 | × | × | √ |
串行化 | ISOLATION_SERIALIZABLE | 解决了脏读、不可重复读和幻读,但是效率比较低 | × | × | × |
rollbackFor 和 rollbackForClassName
这两个属性都是用来指定回滚的异常类型
rollbackFor:通过类进行指定,如@Transactional(rollbackFor = {Exception.class})
rollbackForClassName:通过类名进行指定,如@Transactional(rollbackForClassName = {"java.lang.Exception"}),默认回滚RuntimeException,如空指针异常也属于RuntimeException
Spring默认抛出了未检查unchecked异常(继承自 RuntimeException 的异常)或者 Error才回滚事务;其他异常不会触发回滚事务。如果在事务中抛出其他类型的异常,但却期望 Spring 能够回滚事务,就需要指定 rollbackFor 或者 rollbackForClassName 属性。
noRollbackFor 和 noRollbackForClassName
与 rollbackFor 和 rollbackForClassName 相反,是用来指定不回滚的异常类型,使用方法一致。
手动回滚
spring 也提供了 @Transaction 对应的手动回滚方式
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
如果在代码中一定要 catch 住异常记得使用手动回滚的方式或者重新抛出一个异常。
失效场景
- 接下来看一下场景的 @Transaction 注解失效的场景,希望大家以后能够避免踩坑:@Transactional 应用在非 public 修饰的方法上
- @Transactional 注解属性 propagation 设置问题
- @Transactional 注解属性 rollbackFor、noRollbackFor 设置问题
- 同一个类中方法调用,导致 @Transactional 失效
- 异常被 catch 导致 @Transactional 失效
1、抛出自定义异常导致事务不能正确回滚
@Transactional
@Override
public void transfer(String name) throws FileNotFindException{
xxx
}
原因:Spring 默认只会回滚非检查异常
解法:配置 rollbackFor 属性
@Transactional(rollbackFor = Exception.class)
2、try-catch 异常导致事务不能正确回滚
@Transactional
@Override
public void transfer(String name) {
// 模拟检查时异常
try {
xx;
} catch (FileNotFoundException e) {
e.printStackTrace();
}
System.out.println("success");
}
原因:事务通知只有捉到了目标抛出的异常,才能进行后续的回滚处理,如果目标自己处理掉异常,事务通知无法知悉
解法1:异常原样抛出
- 在 catch 块添加
throw new RuntimeException(e);
解法2:手动设置 TransactionStatus.setRollbackOnly()
- 在 catch 块添加
TransactionInterceptor.currentTransactionStatus().setRollbackOnly();
还有其他场景导致失效,如AOP 切面顺序导致导致事务不能正确回滚,非 public 方法导致的事务失效,父子容器导致的事务失效等更多可参考@Transaction注解失效的八种情况及解决办法_活跃的咸鱼的博客-CSDN博客_transaction失效来自黑马程序员java面试题案例准备:数据库表数据如下,要实现的目标就是刘备曹操孙权向自己手下员工发工资转账,这是一个事务的典型应用场景。数据库操作相关方法 @Override public Account getAccountByName(String name) { String querySql="select * from t_account where a_name=?"; BeanPropertyRowMapper<Account>.https://blog.csdn.net/weixin_44991304/article/details/123169078
更多推荐
所有评论(0)