JAVA面试题分享一百三十三:Spring的事务使用时注意点?

一、注意事项

1、不要在接口上声明@Transactional,而要在具体类的方法上使用 @Transactional 注解,否则注解可能无效。

2、不要图省事,将@Transactional放置在类级的声明中,放在类声明,会使得所有方法都有事务。故@Transactional应该放在方法级别,不需要使用事务的方法,就不要放置事务,比如查询方法。否则对性能是有影响的。

3、@Transactional 的事务开启 ,或者是基于接口的 或者是基于类的代理被创建。所以在同一个类中一个方法调用另一个方法有事务的方法,事务是不会起作用的 。

4、用REQUIRES_NEW的时候,发现没有起作用
分析了一下,原因是A方法(有事务)调用B方法(要独立新事务),如果两个方法写在同一个类里,spring的事务会只处理能同一个。

解决方案1:需要将两个方法分别写在不同的类里。
解决方案2:方法写在同一个类里,但调用B方法的时候,将service自己注入自己,用这个注入对象来调用B方法。

5、使用了@Transactional的方法,只能是public,@Transactional注解的方法都是被外部其他类调用才有效,故只能是public。道理和上面的有关联。故在 protected、private 或者 package-visible 的方法上使用 @Transactional 注解,它也不会报错,但事务无效。

6、默认只有RuntimeExcetion会触发回滚,经验证明确实如此,所以rollbackFor可以自行设置如下:rollbackFor = {Exception.class},当然具体业务具体处理,可能有的业务抛出的某些异常并不需要触发回滚,所以此时应该细化处理异常。

@Transactional(propagation = Propagation.REQUIRES_NEW,rollbackFor={/*自定义Exception,可以是多个*/ IOException.class})

上面的这个rollbackFor配置,可以理解为:当配置这个事务的方法内返回了IOException,则会触发回滚操作。

7、MySQL数据库表引擎应为InnoDB,否则不支持事务。

二、事务失效的场景

1、 查看数据库中使用的表是否都是 InnoDB 的引擎,有些表为了追求写入的性能,比如记录日志的表,会将引擎改为 MyiSAM,如果数据库引擎不支持,那么添加的事务也就不会生效。

2、 事务要加在 public 方法上,否则事务将不会起作用,如果要用在非 public 方法上,事务不会生效。如果方法被 final 修饰, spring 事务底层使用了 aop, 也就是通过jdk动态代理或者cglib,生成了代理类,而被final 或者 staitc的方法,是不能重写或者代理,因此事务就不会生效。

3、 在事务内部调用本类的另一个事务方法,那么被调用的一方事务不会生效。因为spring 的事务是使用动态代理生效的,本类内部的调用相当于是this.method 的调用,没有经过spring代理所以事务不会生效。如果非要在类内部进行调用,需要按照如下图操作,updateUserInfo 调用方法 modifyUserInfo,需要使用 AopContext获取当前对象进行调用执行。同时还要设置 proxyTargetClass= true,改值默认为 false

4、 注意事务设置的隔离级别,如果项目中配置了多个数据源,就会有多个事务管理器,在执行事务的时候需要指定其事务管理器。事务的传播属性和隔离级别是默认的,超时时间默认是 -1,不设置超时时间。异常回滚和异常不回滚需要根据业务的情况进行设置。

 

5、 方法内部不能将异常 try-catch ,如果方法内部都把异常给处理了,那么事务捕捉不到异常信息,事务自然不会回滚,也就不会生效。方法内部把异常信息给处理了,就不能被事务Aop 拦截进行处理,自然就会进行回滚操作。

6、 建议事务 @Transactional 注解需要在具体的实现类上进行标注,而不是在接口层标注。注解是不能继承的,所以只有在基于接口的代理(PROXY)时事务才生效。如果正在使用基于类(AspectJ)代理时,那么注解不会被基于类的代理识别,对象也不会被事务代理所包装,因此事务是不能生效的。

7、 多线程调用,在项目开发过程中多线程的应用场景是很多的,在多线程的场景下,主线程所在的 Context 和多线程执行下的方法不在同一个上下文,我们知道 spring 的事务是通过数据库连接来实现的,数据库连接是存放在一个 ThreadLocal 中的,只有在同一个线程上下文下,事务的操作才能生效。

8、在事务方法中加锁,可能会导致锁失效

无论是Java自带的锁,还是分布式锁,都有可能出现没锁住的情况

原因是解锁先于事务提交,一旦锁释放后其它线程就可以获取到锁了,由于事务还没提交,所以新线程读到的还是旧数据(跟前一个线程读取到的数据是一样的),这就相当于多个线程做了一模一样的事情了。

正确的做法是:要么别加事务,要么把锁加在事务方法外面

9、调用内部(同一个类中)方法的话,事务不会生效

10、事务方法被声明成private或者final,导致事务不生效

三、事务即便生效也不一定能回滚

通过 AOP 实现事务处理可以理解为,使用 try…catch…来包裹标记了 @Transactional 注解的方法,当方法出现了异常并且满足一定条件的时候,在 catch 里面我们可以设置事务回滚,没有异常则直接提交事务。

这个的一定条件满足以下两条:

  • 只有异常传播出了标记了 @Transactional 注解的方法,事务才能回滚

  • 默认情况下,出现 RuntimeException(非受检异常)或 Error 的时候,Spring 才会回滚事务。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

之乎者也·

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

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

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

打赏作者

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

抵扣说明:

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

余额充值