跳至主要內容

5、Spring 中的事务@Trasactional

安图新大约 4 分钟

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);
	}
}

调用TransactionAspectSupportinvokeWithinTransaction 分三种情况:

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;