5、Spring 中的事务@Trasactional
5、Spring 中的事务@Trasactional
1.知识回顾
Spring 中的事务有两种实现方式:编程式事务
、声明式事务
。
- 编程式事务:在代码中显式地指定事务的开始、提交和回滚。
- 声明式事务:通过 AOP 和 XML 或 @Transactional 注解来实现,使得开发人员可以将事务管理与业务逻辑解耦。
补充:编程式事务实现代码示例如下:
import org.springframework.transaction.support.TransactionTemplate;
@Autowired
private TransactionTemplate transactionTemplate;
public void doSomething () {
transactionTemplate.execute(status->{
// TODO: 事务内逻辑
});
}
2.@Transactional
@Transactional 是 org.springframework 提供的 spring-tx 包中的注解,在 Mybatis Plus 包中包含该依赖。
- 可用于修饰方法和类。修饰类时,被修饰类的所有 public 方法都会有事务,建议修饰在方法上。
- 被修饰的方法在抛出异常之后,已经执行的 sql 会自动回滚。
- 使用方式:@Transactional(rollbackFor=Exception.class)
SpringBoot 中默认开启事务。
3.rollbackFor 属性
rollbackFor 属性 用于指定能够触发事务回滚的一出场类型,可以指定多个,用逗号分隔。
- rollbackFor 默认值为 UncheckedException,包括了 RuntimException 和 Error。
- 当我们直接使用 @Transactional 不指定 rollbackFor 时,Exception 及其子类都不会触发。
补充:关于异常的知识
- Java 异常模型中,定义有 Throwable、Exception 和 Error。其中 Exception 和 Error 是 Throwable 的派生类。
- Exception 表示由于网络故障、文件损坏、设备错误、用户输入非法等情况导致的异常,例如:NullPointerException。
- Error 表示 Java 运行时环境出现的错误,例如:OutOfMemoryError 等。
- Exception 可以分为运行时异常(RuntimeException 及其子类)和非运行时异常(Exception 中除了 Runtime Exception 及其子类之外的类)。
- 非运行时异常是检查异常(checked exceptions),一定要 try/catch,因为这类异常是可预料的,编译阶段就检查的出来。
- Error 和运行时异常就是非检查异常(unchecked exceptions),不需要 try/catch,因为这类异常是不可预料的,编辑阶段不会检查。
rollbackFor 实验结果:
- 不加 rollbackFor 属性,抛出 RuntimeException,正常回滚;
- 不加 rollbackFor 属性,抛出 IOException,不回滚;
- 不加 rollbackFor 属性,抛出 OutOfMemoryError,正常回滚;
- 加上 rollbackFor=Exception.class 属性,抛出 IOException,正常回滚;
- 加上 rollbackFor=Exception.class 属性,抛出 OutOfMemoryError,正常回滚
rollbackFor 实验结果说明:
1、 不加rollbackFor,使用默认值,当出现检查时异常,不会回滚
;
2、 rollbackFor=Exception.class不会覆盖Error的回滚
;
3、 综上所述,推荐使用:@Transactional(rollbackFor=Exception.class)
;
4.@Transactional 的实现原理
@Transactional
是 通过 AOP
的方式来实现管理事务的,具体来看如下代码:
事务 aop 处理类 TransactionInterceptor
:
package org.springframework.transaction.interceptor;
@SuppressWarnings("serial")
public class TransactionInterceptor extends TransactionAspectSupport implements MethodInterceptor, Serializable {
@Override
@Nullable
public Object invoke(MethodInvocation invocation) throws Throwable {
// Work out the target class: may be {@code null}.
// The TransactionAttributeSource should be passed the target class
// as well as the method, which may be from an interface.
Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);
// Adapt to TransactionAspectSupport's invokeWithinTransaction...
return invokeWithinTransaction(invocation.getMethod(), targetClass, invocation::proceed);
}
}
调用TransactionAspectSupport
的 invokeWithinTransaction
分三种情况:
1、 响应式编程事务处理
;
2、 命令式编程事务处理
;
3、 没有事务的方法处理
;
5.@Transactional 的事务传播行为
实验过程: 这里主要是通过一系列实验来了解@Transactional 的事务传播,过程为父方法-调用->子方法,通过分别在父方法和子方法中添加注解和抛出异常来进行测试。
实验结果:(Service 内相互调用)
(根据 AOP 原理,Service 间相互调用@Transactional 注解修饰的方法事务是生效的)
实验说明:
- 事务是否回滚,取决于是否是跨实体调用,只有跨实体调用的方法事务才生效;
- 因为 Spring 的事务是基于 AOP 的,只有当第一个 Service 有 @Transactional 注释的话,才会在开始执行 DML 之前进入切面来开启事务;
- 如果第一个 Service 没有 @Transactional 注释的话,就已经从切面进入到服务中了,在第一个方法调用第二个方法的时候是服务内部的调用,并不会经过切面,所以就算执行 DML 之后抛出异常了,也不会回滚。
6.补充:12 种@Transactional 事务失效场景
整理完毕,完结撒花。
参考地址:
1、 @Transaction 源码解析:https://blog.csdn.net/shandian534/article/details/124085838;
2、 @Transactional(rollbackFor):https://blog.csdn.net/qq_43900956/article/details/120993392;
3、 @Transactional 注解的 rollbackFor 属性:https://www.jianshu.com/p/c5988db897fc;
4、 啪!啪!@Transactional 注解的 12 种失效场景,这坑我踩个遍:https://blog.51cto.com/u_14787961/4833414;