JAVA面试题分享五百六十二:你真的会用@Transactional吗?

目录

前言

源码

失效场景


前言

今天跟大家讲一讲 @Transactional,这个注解相信大家应该都用过,它能保证方法内多个数据库操作要么同时成功、要么同时失败。使用 @Transactional 注解时需要注意许多的细节,虽然它看起来简单,但不知道底层原理的话,用起来往往达不到我们想要的效果。

事务分为编程式事务和声明式事务两种。编程式事务指在代码中手动的管理事务的提交、回滚等操作,代码侵入性比较强。声明式事务是基于 AOP 面向切面的,它将具体业务与事务处理部分解耦,代码侵入性很低,声明式事务也有两种实现方式,一种是基于 TX 和 AOP 的 xml 配置文件方式,二种就是基于 @Transactional 注解了,实际开发中 @Transactional 用的比较多。

@Transactional 可以作用在类上,当作用在类上的时候,表示所有该类的 public 方法都配置相同的事务属性信息。@Transactional 也可以作用在方法上,当方法上也配置了 @Transactional,方法的事务会覆盖类的事务配置信息。

今天我们通过看 @Transactional 的源码来和大家重新认识一下 @Transactional 的用法。

源码

transactionManager 和 value 是一组: 大多数项目只需要一个事务管理器,但是在有些项目中为了提高效率、或者有多个完全不同又不相干的数据源,所以会有多个事务管理器,这里填的就是你想用的事务管理器的 Bean 的 id。

isolation属性: 是事务的隔离级别,默认值为 Isolation.DEFAULT。这里有四个隔离级别,在Innodb里面默认用的是 RR 级别,具体这四个级别是什么意思,等后面写 LBCC 和 MVCC 的时候再和大家介绍。

· Isolation.DEFAULT:使用底层数据库默认的隔离级别。

· Isolation.READ_UNCOMMITTED

· Isolation.READ_COMMITTED

· Isolation.REPEATABLE_READ

· Isolation.SERIALIZABLE

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

readOnly属性 : 指定事务是否为只读事务,默认值为false;为了忽略那些不需要事务的方法,比如读取数据,可以设置 read-only 为 true。

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

现在我们来看个例子,来加深下这个属性的用法。这里我写了一个转账的方法,里面抛了一个InterruptedException 异常,此时数据库的事务会回滚吗?

 执行一下,报错了

我们来看下数据库,金额产生变化了,之前是1000 和 0 的,现在变成了 900 和 100,这和我们预期的不符啊,我们想的是发生异常了,事务将进行回滚,怎么还能执行成功呢?

不要慌,现在我们来换一个异常,此时换成了抛出一个运行时异常

再执行一下

我们看下数据库还是1000 和 0,完好无损,说明回滚成功了。

那为什么两个异常,一个异常回滚成功了,一个异常回滚失败呢,现在我们来看下源码。看到这里小伙伴明白了吗,原来回滚是只判断异常类型是RuntimeException 和 Error 的才会进行回滚,别的异常类型是不会回滚的。

 看到这里小伙伴可能有点懵,什么是 RuntimeException 类型和 Error 类型呢,这里我用 idel 的工具画了一个 UML 类图,小伙们可以看下,RuntimeException 和 Error 我也用红框圈出来了。所以小伙伴如果想要捕获所有的异常,这时候就可以用 @Transactional(rollbackFor = Exception.class),这是一个偷懒的写法,当然这里也可以指定多个,可以指定自己的业务异常等等。

noRollbackFor属性:这个就是和上面相反的了,抛出指定的异常类型,不回滚事务,也可以指定多个异常类型。

最后说下propagation属性,这个也是本篇文章的核心重点,它一共有七种传播行为,我们来分别看下。

 当我们不指定的时候默认使用的是 Propagation.REQUIRED,另外两个常用的是 Propagation.REQUIRES_NEW 和 Propagation.NESTED,而另外四种我们基本是不会去使用的,所以小伙伴也没必要去了解,那么这几个传播行为到底是什么意思,我们光看概念可能一头雾水,不知所云,没关系,用实战演练的方式来领略下到底怎么用法,就会更加熟悉了。

失效场景

到这里我们差不多也要接近尾声了,最后我们来说下 @Transactional 的失效场景。

1、@Transactional 应用在非 public 修饰的方法上,@Transactional 将会失效。protected、private 修饰的方法上使用 @Transactional 注解,虽然事务无效,但不会有任何报错,这是我们很容犯错的一点。

2、@Transactional 注解属性 propagation 设置错误。PROPAGATION_SUPPORTS、PROPAGATION_NOT_SUPPORTED、PROPAGATION_NEVER上面这三种我都没有讲到,因为用了它们事务是不会发生回滚,加了等于没加。

3、@Transactional 注解属性 rollbackFor 设置错误,这个上面演示给大家看了,默认的只会对 RuntimeException 类型和 Error 类型的才进行回滚。如果在事务中抛出其他类型的异常,却希望 Spring 能够回滚事务,就需要指定 rollbackFor 属性。

4、同一个类中方法调用,导致 @Transactional 失效。这个我在上面做实验的时候已经演示过了,总方法调子方法的时候,要放在不同的 service 里面,如果放在一个类里面,事务调用是不会生效的。

5、异常被吃了,这个在实验五里面也说了,吃掉以后虽然不影响其他事务,但是其他数据的 commit 会造成数据不一致,所以有些时候 try catch 反倒会画蛇添足。

6、数据库引擎不支持事务,这一点很简单,myisam 引擎是不支持事务的,innodb 引擎支持事务。

7、数据源没有配置事务管理器,这个也很简单,要使用事务肯定要配事务管理器。Hibernate 用的是HibernateTransactionManager,JDBC 和 Mybatis 用的是 DataSourceTransactionManager。

8、最后就是一个方法内多数据库(多数据源)的情况下会失效,这个也是我同事做实验得出来的。

  • 17
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

之乎者也·

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值