Spring 事务注意事项

442 阅读4分钟

「这是我参与 2022 首次更文挑战的第 4 天,活动详情查看:2022 首次更文挑战

穷则变,变则通,通则久。

在日常的 java 项目开发过程中,会为了保证业务操作的完整性,会在业务操作的整个过程和某个过程中使用到事务,利用事务来保障数据的一致性和完整性,避免数据错乱的发生。这里就从实战的角度出发,以 springboot 搭建的 ssm 项目来分享一下事务操作的注意事项。

配置事务的注意事项

springboot 项目中,事务配置生效的关注点:

1 开启事务注解 @EnableTransactionManagement
2 在配置文件中配置事务管理器,PlatformTransactionManager
3 需要开启事务的方法上加上注解 @Transactional

有以上的三点才能保障事务在项目中能够生效。

事务失效的场景

  • 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 中的,只有在同一个线程上下文下,事务的操作才能生效。

其它

在事务方法中,需要保证操作方法的内部只有 关于数据库的操作,诸如耗时的 IO 操作或者其它与业务无关的事情可以放在外部进行处理或者准备,这样能够降低单个事务的操作时间,提高系统的响应率和吞吐。