@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 相关所有的属性值

字段名类型含义
valueString主要用来指定不同的事务管理器
propagationenum: Propagation可选的事务传播行为设置
isolationenum: Isolation可选的事务隔离级别设置
readOnlyboolean读写或只读事务,默认读写
timeoutint (in seconds granularity)事务超时时间设置
rollbackForClass对象数组,必须继承自Throwable导致事务回滚的异常类数组
rollbackForClassName类名数组,必须继承自Throwable导致事务回滚的异常类名字数组
noRollbackForClass对象数组,必须继承自Throwable不会导致事务回滚的异常类数组
noRollbackForClassName类名数组,必须继承自Throwable不会导致事务回滚的异常类名字数组

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

含义
REQUIRED如果当前存在事务,则加入该事务;
如果当前不存在事务,则创建一个新的事务;
SUPPORTS如果当前存在事务,则加入该事务;
如果当前不存在事务,则以非事务的方式继续运行;
MANDATORY如果当前存在事务,则加入该事务;
如果当前不存在事务,则抛出异常;
REQUIRES_NEW如果当前不存在事务,重新创建一个新的事务;
如果当前存在事务,则暂停当前的事务;
NOT_SUPPORTED以非事务的方式运行
如果当前存在事务,则暂停当前的事务
NEVER以非事务的方式运行
如果当前存在事务,则抛出异常
NESTED如果当前存在事务,则在该事务内嵌套事务运行;
如果当前不存在事务,则创建一个新的事务;

isolation事务隔离级别,有4种

隔离级别名称含义脏读不可重复读幻读
读未提交ISOLATION_READ_UNCOMMITTEDT1读取T2未提交的东西
读已提交ISOLATION_READ_COMMITTEDoracle默认,事务提交后可读×
可重复读ISOLATION_REPEATABLE_READmysql默认,针对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

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐