Spring事务最佳应用指南(包含:事务传播类型、事务失效场景、使用建议、事务源码分析)

前言

本文主要介绍的是在Spring框架中有关事务的应用方式,以及一些生产中常见的与事务相关的问题、使用建议等。同时,为了让读者能够更容易理解,本文在讲解过程中也会通过源码以及案例等方式进行辅助说明,通过阅读本文不但能够解决在实际生产环境中遇到的与Spring事务相关的问题,也能让你掌握更好的应用方式。

事务的传播类型

我们首先从事务的传播类型开始说起,在Spring中事务传播指的是:一个事务方法调用了另一个事务方法,比如事务方法A在执行过程中又调用了事务方法B,那么对于事务方法B来说,应该如何如何运行?于是,针对这个问题,Spring就定义了七种数据传播类型,基本上就是罗列了所有可能遇到的场景,然后供使用者选择:

  • REQUIRED:支持当前事务,如果当前不存在则新开启一个事务(默认配置)

  • SUPPORTS:支持当前事务,如果当前不存在事务则以非事务方式执行

  • MANDATORY:支持当前事务,如果当前不存在事务则抛出异常

  • REQUIRES_NEW:创建一个新事务,如果当前已存在事务则挂起当前事务

  • NOT_SUPPORTED:以非事务方式执行,如果当前已存在事务则挂起当前事务

  • NEVER:以非事务方式执行,如果当前已存在事务则抛出异常

  • NESTED:如果当前存在事务,则在嵌套事务中执行,否则开启一个新事务

场景分类

事务传播类型 前一个方法存在事务 前一个方法不存在事务 当前一个方法存在事务时,是否与其是同一个事务
REQUIRED 使用前一个方法的事务 创建一个新的事务
SUPPORTS 使用前一个方法的事务 按非事务方式执行
MANDATORY 使用前一个方法的事务 抛出异常
REQUIRES_NEW 创建一个新的事务 创建一个新的事务
NOT_SUPPORTED 以非事务方式执行 按非事务方式执行 -
NEVER 抛出异常 按非事务方式执行 -
NESTED 嵌套方式使用前一个方法的事务 创建一个新的事务

如果你还不清楚这些传播类型具体有什么区别也没关系,接下来本节会针对每一种类型分别进行效果演示,以此来帮助你进行理解。

在此之前,先准备一下演示的数据

sql 复制代码
CREATE TABLE `t1` (
  `id` int(11) NOT NULL,
  `c` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `idx_c` (`c`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE TABLE `t2` (
  `id` int(11) NOT NULL,
  `c` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `idx` (`c`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

两条更新语句,分别对t1t2两张表进行更新。

xml 复制代码
<update id="updateT1">
    update t1 set c = 2 where id = 1
</update>
<update id="updateT2">
    update t2 set c = 2 where id = 1
</update>

REQUIRED

含义:支持当前事务,如果当前不存在则新开启一个事务(默认配置)。

下面这块代码逻辑为:当调用T1Service中的func方法时,除了要更新t1表数据之外,还会调用t2Service中的func方法,更新t2表,不过,在执行t2Service中的方法时会遇到异常。

java 复制代码
@Service
public class T1Service {

    @Resource
    private TestMapper testMapper;

    @Resource
    private T2Service t2Service;
    
    @Transactional
    public void func() {
        testMapper.updateT1();
        t2Service.func();
    }
}

@Service
public class T2Service {

    @Resource
    private TestMapper testMapper;

    @Transactional
    public void func() {
        testMapper.updateT2();
        int i = 1 / 0;
    }
}

@Transactional默认的传播方式就是REQUIRED,当方法执行到int i = 1 / 0时会抛出异常,此时t1、t2表中的数据都不会被修改,因为这两个方法使用的是同一个事务,所以只要有一个遇到异常,两个更新就都不会成功。

SUPPORTS

含义:支持当前事务,如果当前不存在事务则以非事务方式执行。

t2Servicefunc方法现在没有事务了,t2Servicefunc方法配置上@Transactional(propagation = Propagation.SUPPORTS),当执行int i = 1 / 0时,t1、t2两张表数据都不会回滚。

java 复制代码
@Service
public class T1Service {

    @Resource
    private TestMapper testMapper;

    @Resource
    private T2Service t2Service;

    // 去掉事务 @Transactional
    public void func() {
        testMapper.updateT1();
        t2Service.func();
    }
}
java 复制代码
@Service
public class T2Service {

    @Resource
    private TestMapper testMapper;
	
   /**
    * 数据不会回滚,因为当前没有事务,SUPPORTS会以非事务方式执行
    * 如果配置成 @Transactional(propagation = Propagation.REQUIRED),则事务会生效 
    */
    @Transactional(propagation = Propagation.SUPPORTS)
    public void func() {
        testMapper.updateT2();
        int i = 1 / 0;
    }
}

MANDATORY

含义:支持当前事务,如果当前不存在事务则抛出异常。

t1Service没有事务时,把t2Servicefunc方法配置为@Transactional(propagation = Propagation.MANDATORY)

java 复制代码
// t1Service
public void func() {
    testMapper.updateT1();
    t2Service.func();
}

// t2Service
@Transactional(propagation = Propagation.MANDATORY)
public void func() {
    testMapper.updateT2();
    int i = 1 / 0;
}

异常信息

java 复制代码
org.springframework.transaction.IllegalTransactionStateException: No existing transaction found for transaction marked with propagation 'mandatory'
	at org.springframework.transaction.support.AbstractPlatformTransactionManager.getTransaction(AbstractPlatformTransactionManager.java:362) ~[spring-tx-5.3.14.jar:5.3.14]
	at org.springframework.transaction.interceptor.TransactionAspectSupport.createTransactionIfNecessary(TransactionAspectSupport.java:595) ~[spring-tx-5.3.14.jar:5.3.14]
	at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:382) ~[spring-tx-5.3.14.jar:5.3.14]
	at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:119) ~[spring-tx-5.3.14.jar:5.3.14]
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.3.14.jar:5.3.14]
	at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:753) ~[spring-aop-5.3.14.jar:5.3.14]
	at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:698) ~[spring-aop-5.3.14.jar:5.3.14]

REQUIRES_NEW

含义:创建一个新事务,如果当前已存在事务则挂起当前事务。

t2的数据不会被更新。

java 复制代码
// t1Service
public void func() {
    testMapper.updateT1();
    t2Service.func();
}

// t2Service
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void func() {
    testMapper.updateT2();
    int i = 1 / 0;
}

与REQUIRED有什么区别呢?

如果把抛出异常的地方放到t1Service中。

java 复制代码
// t1Service
@Transactional
public void func() {
    testMapper.updateT1();
    t2Service.func();
    int i = 1 / 0;
}

// t2Service
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void func() {
    testMapper.updateT2();
}

再次执行后,t2的数据不会回滚,t1的数据会回滚,因为t2t1不是一个事务。

NOT_SUPPORTED

含义:以非事务方式执行,如果当前已存在事务则挂起当前事务。

NOT_SUPPORTED的效果就是无论异常是在t1Service还是t2Service都不会回滚t2的数据。

java 复制代码
// t1Service
@Transactional
public void func() {
    testMapper.updateT1();
    t2Service.func();
    int i = 1 / 0;
}

// t2Service
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void func() {
    testMapper.updateT2();
    int i = 1 / 0;
}

NEVER

含义:以非事务方式执行,如果当前已存在事务则抛出异常。

如字面含义,直接抛出异常。

java 复制代码
// t1Service
@Transactional
public void func() {
    testMapper.updateT1();
    t2Service.func();
}

// t2Service
@Transactional(propagation = Propagation.NEVER)
public void func() {
    testMapper.updateT2();
}
java 复制代码
org.springframework.transaction.IllegalTransactionStateException: Existing transaction found for transaction marked with propagation 'never'
	at org.springframework.transaction.support.AbstractPlatformTransactionManager.handleExistingTransaction(AbstractPlatformTransactionManager.java:413) ~[spring-tx-5.3.14.jar:5.3.14]
	at org.springframework.transaction.support.AbstractPlatformTransactionManager.getTransaction(AbstractPlatformTransactionManager.java:352) ~[spring-tx-5.3.14.jar:5.3.14]
	at org.springframework.transaction.interceptor.TransactionAspectSupport.createTransactionIfNecessary(TransactionAspectSupport.java:595) ~[spring-tx-5.3.14.jar:5.3.14]
	at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:382) ~[spring-tx-5.3.14.jar:5.3.14]
	at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:119) ~[spring-tx-5.3.14.jar:5.3.14]
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.3.14.jar:5.3.14]
	at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:753) ~[spring-aop-5.3.14.jar:5.3.14]
	at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:698) ~[spring-aop-5.3.14.jar:5.3.14]

如果把t1Service中的事务去掉,则不会抛出不支持事务的异常,同时t2Service自己抛出异常后,数据也是不会回滚的,完全是当做无事务处理。

java 复制代码
// t1Service
public void func() {
    testMapper.updateT1();
    t2Service.func();
}

// t2Service
@Transactional(propagation = Propagation.NEVER)
public void func() {
    testMapper.updateT2();
    int i = 1 / 0;
}

NESTED

含义:如果当前存在事务,则在嵌套事务中执行,否则开启一个新事务。

NESTED应该是几种事务传播方式中最难理解的,如果不注意,NESTEDREQUIRED功能看起来则差不多,都可以理解为有事务则加入,没有则新启一个,但实际上NESTEDREQUIRED要更加灵活。

先来看第一个案例,在t1Service中调用t2Service时,对t2Service抛出的异常进行了捕获,并且自己也没有再抛出。

java 复制代码
// t1Service
@Transactional
public void func() {
    testMapper.updateT1();
    // 以此catch异常的原因是想像你说明,在t1Service的func方法中,是不会因为调用t2Service遇到异常而被回滚的,因此异常已经被catch了。回滚主要是因为使用的是同一个事务。
    try {
        t2Service.func();
    } catch (Exception e) {
        e.printStackTrace();
    }
}

// t2Service
@Transactional(propagation = Propagation.REQUIRED)
public void func() {
    testMapper.updateT2();
    int i = 1 / 0;
}

t2Service配置为REQUIRED时,t1t2都进行了回滚,因为是同一个事务。

但如果t2Service配置为NESTED就不一样了,此时t1则不会回滚。

java 复制代码
// t1Service
@Transactional
public void func() {
    testMapper.updateT1();
    try {
        t2Service.func();
    } catch (Exception e) {
        e.printStackTrace();
    }
}

// t2Service
@Transactional(propagation = Propagation.NESTED)
public void func() {
    testMapper.updateT2();
    int i = 1 / 0;
}

NESTED与REQUIRES_NEW的区别

接下来再来看看NESTEDREQUIRES_NEW的区别。

我们分别给t1Servicet2Service加上一段System.out.println(TransactionSynchronizationManager.getCurrentTransactionName());用来查看一下当前执行的事务。

java 复制代码
// t1Service
@Transactional
public void func() {
    testMapper.updateT1();
    System.out.println(TransactionSynchronizationManager.getCurrentTransactionName());
    try {
        t2Service.func();
    } catch (Exception e) {
        e.printStackTrace();
    }
}
// t2Service
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void func() {
    testMapper.updateT2();
    System.out.println(TransactionSynchronizationManager.getCurrentTransactionName());
    int i = 1 / 0;
}

输出结果

java 复制代码
com.demo.transaction.service.T1Service.func
com.demo.transaction.service.T2Service.func

如果把REQUIRES_NEW替换为NESTED则可以看出实际上还是同一个事务。

java 复制代码
com.demo.transaction.service.T1Service.func
com.demo.transaction.service.T1Service.func

也就是说,使用NESTED时,虽然还是同一个事务,但却可以在多个方法中进行控制。

实现原理

两个方法同一个事务没有一起回滚,实际上利用的是savepoint功能,大概方式如下:

java 复制代码
-- 主事务
savepoint;
-- 执行主事务代码

-- 子事务
savepoint;

-- 执行子事务代码

-- 子事务提交
commit;

-- 执行主事务代码

-- 主事务提交
commit;

所以,搞清楚实现方式以后,你就会发现如果是在主事务中抛出异常,那么子事务也会被回滚,就像下面这样:t1t2都会回滚。

java 复制代码
// t1Service
@Transactional
public void func() {
    testMapper.updateT1();
    t2Service.func();
    int i = 1 / 0;
}
// t2Service
@Transactional(propagation = Propagation.NESTED)
public void func() {
    testMapper.updateT2();
}

事务的失效场景

关于事务未生效的问题,也是我们在日常开发中经常会遇到的,这一节总结了一些会导致事务失效的场景,你可以了解一下,以免遇到类似的问题。

异常未抛出

被捕获的异常一定要抛出,否则是不会回滚的。

java 复制代码
// t1Service
@Transactional
public void func() {
    try {
        testMapper.updateT1();
        t2Service.func();
        int i = 1 / 0;
    } catch (Exception e) {
    	// 异常捕获了,未抛出,导致异常事务不会回滚。
        e.printStackTrace();
    }
}

// t2Service
@Transactional
public void func() {
    testMapper.updateT2();
}

异常与rollback不匹配

@Transactional默认情况下,只会回滚RuntimeExceptionError及其子类的异常,如果是受检异常或者其他自定义的不属于其子类的异常是不会回滚事务的。

java 复制代码
@Transactional
public void func() throws Exception {
    try {
        testMapper.updateT1();
        t2Service.func();
        int i = 1 / 0;
    } catch (Exception e) {
    	// 默认情况下非运行时异常不会回滚
        throw new Exception();
    }
}

修改方式也很简单,@Transactional支持通过rollbackFor指定回滚异常类型,可以直接改成rollbackFor = Exception.class即可

java 复制代码
// 改成rollbackFor = Exception.class即可
@Transactional(rollbackFor = Exception.class)
public void func() throws Exception {
    try {
        testMapper.updateT1();
        t2Service.func();
        int i = 1 / 0;
    } catch (Exception e) {
        throw new Exception();
    }
}

方法内部直接调用

func2方法是由func调用,虽然func2方法上加了@Transactional注解,但事务不会生效,testMapper.updateT2()执行的方法并不会回滚。

java 复制代码
public void func() {
    testMapper.updateT1();
    func2();
}

@Transactional
public void func2() {
    testMapper.updateT2();
    int i = 1 / 0;
}

可以修改为直接通过注入的方式调用即可。

java 复制代码
@Service
public class T1Service {

    @Resource
    private TestMapper testMapper;
   
   // 注入T1Service对象
    @Resource
    private T1Service t1Service;

    public void func() {
        testMapper.updateT1();
        // 通过注入的方式调用
        t1Service.func2();
    }

    @Transactional
    public void func2() {
        testMapper.updateT2();
        int i = 1 / 0;
    }
}

注意:SpringBoot 2.6.0版本开始,默认禁止循环依赖,所以如果你使用的版本是2.6.0之后的,那么启动会遇到如下报错。

java 复制代码
As a last resort, it may be possible to break the cycle automatically by setting spring.main.allow-circular-references to true.

修改方式:在配置文件中把允许循环依赖打开即可。

java 复制代码
spring.main.allow-circular-references=true

当然,你也可以直接使用AopContext的方式,像下面这样:

java 复制代码
public void func() {
    testMapper.updateT1();
    T1Service t1Service = (T1Service) AopContext.currentProxy();
    t1Service.func2();
}
@Transactional
public void func2() {
    testMapper.updateT2();
    int i = 1 / 0;
}

在另一个线程中使用事务

Spring事务管理的方式就是通过ThreadLocal把数据库连接与当前线程绑定,如果新开启一个线程自然就不是一个数据库连接了,自然也就不是一个事务。

t2Service.func()方法操作的数据并不会被回滚。

java 复制代码
@Transactional
public void func() {
    testMapper.updateT1();
    new Thread(() -> t2Service.func()).start();
    int i = 1 / 0;
}

注解作用到private级别的方法上

当你写成如下这样时,IDEA直接会给出提示Methods annotated with '@Transactional' must be overridable 原因很简单,private修饰的方式,spring无法为其生成代理。

java 复制代码
public void func() {
    t1Service.func2();
}
@Transactional
private void func2() {
    testMapper.updateT1();
    int i = 1 / 0;
}

final类型的方法

这个与private道理是一样的,都是影响了Spring生成代理对象,同样IDEA也会有相关提示。

数据库存储引擎不支持事务

注意,如果你使用的是MySQL数据库,那么常用的存储引擎中只有InnoDB才支持事务,像MyISAM是不支持事务的,其他存储引擎都是针对特定场景下使用的,一般也不会用到,不做讨论。

事务的使用建议

Spring提供了两种使用事务的方式,一种是声明式事务、一种是编程式事务,无论是哪种方式使用起来都非常简单,无需过多介绍,本节主要是针对一些使用不当的场景进行说明。

声明式事务的应用级别

在实际生产应用中是不建议在类似Service这样的class类上直接加上@Transactional注解的,因为这样会导致这个类中的所有方法在执行时都带上了事务,这样原来根本就不需要事务的方法则多了一份负担。

java 复制代码
@Service
@Transactional // 不要加在class级别
public class DemoService { 
}

长事务、过早起开事务

简单来说,就是在整个方法的生命周期内,真正需要事务管理的方法可能只占用了20%的时间,而其他业务流程占用了80%的时间,但是由于事务是对整个方法生效,从而导致事务的整体执行时间与整个方法的执行时间一样。

java 复制代码
@Transactional
public void func() {
	// 两个select花费了2秒
	select1();
	select2();
	// 两个save只花费了200毫秒
	save1();
	save2();
}

解决方式也很简单,把长事务拆分为短事务即可。

java 复制代码
public void func() {
	select1();
	select2();
	manager.save();
}

@Transactional
public void save() {
	save1();
	save2();
}

也可以直接使用编程式事务,编程式事务可以更灵活的控制事务的范围。

java 复制代码
@Resource
private TransactionTemplate transactionTemplate;

public void func() {
    transactionTemplate.execute(new TransactionCallbackWithoutResult() {
        @Override
        protected void doInTransactionWithoutResult(TransactionStatus status) {
            testMapper.updateT1();
            t2Service.func();
            int i = 1 / 0;
        }
    });
}

事务锁的问题

这和前面的事务执行时间过长是有一定关系的,过长时间的事务,更容易因为锁资源的争抢而导致服务性能下降,同时还需要注意死锁的问题。

事务的整体架构展示

前面提到过,在Spring中,我们可以直接使用TransactionTemplate或者PlatformTransactionManager来操作事务,TransactionTemplate封装了事务处理的核心流程,使用者只需直接注入即可直接使用,而PlatformTransactionManager则是更底层的接口,虽然也可以直接通过它来控制事务,但它并不是主流的使用方式,一般还是建议优先使用TransactionTemplate

接下来,本小节主要会对Spring事务的整体架构进行梳理,通过源码的分析,让使用者能够更清楚事务的内部实现原理。

类的关系图

TransactionManager接口

标识性接口

java 复制代码
public interface TransactionManager {

}

PlatformTransactionManager接口

PlatformTransactionManager接口继承了TransactionManager,定义了事务的核心方法,提交和回滚,正如前面提到的,可以直接使用它来控制事务,但并不建议这样做,正确的做法应该是继承AbstractPlatformTransactionManager类,典型的样例就是JtaTransactionManagerDataSourceTransactionManager

java 复制代码
public interface PlatformTransactionManager extends TransactionManager {

	TransactionStatus getTransaction(@Nullable TransactionDefinition definition)
			throws TransactionException;

	void commit(TransactionStatus status) throws TransactionException;

	void rollback(TransactionStatus status) throws TransactionException;

}

AbstractPlatformTransactionManager抽象类

这是一个典型的采用模板方法设计的抽象类,定义了关于事务的核心处理流程。

下图展示了几个关键的抽象方法,其具体的逻辑处理都在子类中。

下图是DataSourceTransactionManager子类重写的doCommitdoRollback方法。

TransactionTemplate

TransactionTemplate是具体执行事务的入口,XXXTemplate结尾的类,通常目的都是为了简化使用者处理的流程,这种命名方式也是Spring的习惯。

TransactionTemplate中定义的入口方法就是execute

入参TransactionCallback也是一个标记性接口。

java 复制代码
@FunctionalInterface
public interface TransactionCallback<T> {
   @Nullable
   T doInTransaction(TransactionStatus status);
}
java 复制代码
@Override
@Nullable
public <T> T execute(TransactionCallback<T> action) throws TransactionException {
	// 方法执行的第一步就需要先确保transactionManager对象不为空,还记得transactionManager吗?就是前面分析的定义了事务处理的关键方法和核心流程的类。
	Assert.state(this.transactionManager != null, "No PlatformTransactionManager set");
	if (this.transactionManager instanceof CallbackPreferringPlatformTransactionManager) {
		return ((CallbackPreferringPlatformTransactionManager) this.transactionManager).execute(this, action);
	}
	else {
		TransactionStatus status = this.transactionManager.getTransaction(this);
		T result;
		try {
			result = action.doInTransaction(status);
		}
		catch (RuntimeException | Error ex) {
			// Transactional code threw application exception -> rollback
			rollbackOnException(status, ex);
			throw ex;
		}
		catch (Throwable ex) {
			// Transactional code threw unexpected exception -> rollback
			rollbackOnException(status, ex);
			throw new UndeclaredThrowableException(ex, "TransactionCallback threw undeclared checked exception");
		}
		this.transactionManager.commit(status);
		return result;
	}
}

TransactionManager会作为TransactionTemplate中的一个属性来使用。

整个execute中几个关键的方法实际上都是在调用TransactionManager中提供的方法,doInTransaction则是执行业务代码的地方。

TransactionDefinition

TransactionDefinition这个接口主要用来定义事务的传播行为、隔离级别、超时时间、是否只读等属性。

总结

Spring为我们提供的TransactionTemplate类,定义了事务处理的基本流程,对外暴露一个execute方法,并通过传入一个标记性接口TransactionCallback来实现使用者的业务逻辑处理,大大简化了使用方式。

PlatformTransactionManager则更加的灵活,它的目的主要是为了能够让使用者更加方便的在事务流程前后进行业务扩展,比如它的实现类有:HibernateTransactionManager、JpaTransactionManager、JtaTransactionManager,很明显就是针对不同的ORM框架而定制的。

所以定义TransactionTemplate是为了让事务使用更加方便,定义PlatformTransactionManager是为了让扩展更加的方便。

事务的提交与回滚

最后,我们再来看看事务的提交与回滚的实现逻辑。

commit方法

commit方法是在AbstractPlatformTransactionManager类中定义的,是事务的提交的入口方法,具体的处理逻辑主要又由processCommitprocessRollback这两个方法完成。

java 复制代码
@Override
public final void commit(TransactionStatus status) throws TransactionException {
	// 判断事务是否已经完成
	if (status.isCompleted()) {
		throw new IllegalTransactionStateException(
				"Transaction is already completed - do not call commit or rollback more than once per transaction");
	}
	DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;
	if (defStatus.isLocalRollbackOnly()) {
		if (defStatus.isDebug()) {
			logger.debug("Transactional code has requested rollback");
		}
		processRollback(defStatus, false);
		return;
	}
	if (!shouldCommitOnGlobalRollbackOnly() && defStatus.isGlobalRollbackOnly()) {
		if (defStatus.isDebug()) {
			logger.debug("Global transaction is marked as rollback-only but transactional code requested commit");
		}
		processRollback(defStatus, true);
		return;
	}
	processCommit(defStatus);
}

processCommit

该方法执行了很多扩展方法,以及一些与事务传播类型有关的逻辑处理,最终的事务处理又由doCommit方法来实现。

java 复制代码
private void processCommit(DefaultTransactionStatus status) throws TransactionException {
	try {
		boolean beforeCompletionInvoked = false;
		try {
			boolean unexpectedRollback = false;
			// 这是一个空方法,主要是交由具体的事务处理器来实现
			prepareForCommit(status);
			// 下面两个方法都是Spring留出的扩展点,通过TransactionSynchronizationManager提供的registerSynchronization方法,可以注册TransactionSynchronization实例,从而调用TransactionSynchronization提供的相关方法
			triggerBeforeCommit(status);
			triggerBeforeCompletion(status);
			beforeCompletionInvoked = true;
			// 嵌套事务的处理分支
			if (status.hasSavepoint()) {
				if (status.isDebug()) {
					logger.debug("Releasing transaction savepoint");
				}
				unexpectedRollback = status.isGlobalRollbackOnly();
				status.releaseHeldSavepoint();
			}
			// 大多数都是一个新的事务
			else if (status.isNewTransaction()) {
				if (status.isDebug()) {
					logger.debug("Initiating transaction commit");
				}
				unexpectedRollback = status.isGlobalRollbackOnly();
				// 同样的具体交给事务处理器来完成
				doCommit(status);
			}
			else if (isFailEarlyOnGlobalRollbackOnly()) {
				unexpectedRollback = status.isGlobalRollbackOnly();
			}
			// Throw UnexpectedRollbackException if we have a global rollback-only
			// marker but still didn't get a corresponding exception from commit.
			if (unexpectedRollback) {
				throw new UnexpectedRollbackException(
						"Transaction silently rolled back because it has been marked as rollback-only");
			}
		}
		catch (UnexpectedRollbackException ex) {
			// can only be caused by doCommit
			triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK);
			throw ex;
		}
		catch (TransactionException ex) {
			// can only be caused by doCommit
			if (isRollbackOnCommitFailure()) {
				doRollbackOnCommitException(status, ex);
			}
			else {
				triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN);
			}
			throw ex;
		}
		catch (RuntimeException | Error ex) {
			if (!beforeCompletionInvoked) {
				triggerBeforeCompletion(status);
			}
			doRollbackOnCommitException(status, ex);
			throw ex;
		}
		// Trigger afterCommit callbacks, with an exception thrown there
		// propagated to callers but the transaction still considered as committed.
		try {
			// triggerAfterCommit和triggerAfterCompletion,和前面的triggerBeforeCommit、triggerBeforeCompletion两个方法一样,都是Spring留下的扩展点
			triggerAfterCommit(status);
		}
		finally {
			triggerAfterCompletion(status, TransactionSynchronization.STATUS_COMMITTED);
		}
	}
	finally {
		cleanupAfterCompletion(status);
	}
}

doCommit

方法执行到了这一步,实际上就是获取Connection,然后调用commit即可,这是JDBC的使用标准。

java 复制代码
@Override
protected void doCommit(DefaultTransactionStatus status) {
	DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();
	Connection con = txObject.getConnectionHolder().getConnection();
	if (status.isDebug()) {
		logger.debug("Committing JDBC transaction on Connection [" + con + "]");
	}
	try {
		con.commit();
	}
	catch (SQLException ex) {
		throw translateException("JDBC commit", ex);
	}
}

processRollback

接下来是回滚,与事务的提交处理其实差不多,可以自行阅读。

java 复制代码
private void processRollback(DefaultTransactionStatus status, boolean unexpected) {
	try {
		boolean unexpectedRollback = unexpected;
		try {
			triggerBeforeCompletion(status);
			if (status.hasSavepoint()) {
				if (status.isDebug()) {
					logger.debug("Rolling back transaction to savepoint");
				}
				status.rollbackToHeldSavepoint();
			}
			else if (status.isNewTransaction()) {
				if (status.isDebug()) {
					logger.debug("Initiating transaction rollback");
				}
				doRollback(status);
			}
			else {
				// Participating in larger transaction
				if (status.hasTransaction()) {
					if (status.isLocalRollbackOnly() || isGlobalRollbackOnParticipationFailure()) {
						if (status.isDebug()) {
							logger.debug("Participating transaction failed - marking existing transaction as rollback-only");
						}
						doSetRollbackOnly(status);
					}
					else {
						if (status.isDebug()) {
							logger.debug("Participating transaction failed - letting transaction originator decide on rollback");
						}
					}
				}
				else {
					logger.debug("Should roll back transaction but cannot - no transaction available");
				}
				// Unexpected rollback only matters here if we're asked to fail early
				if (!isFailEarlyOnGlobalRollbackOnly()) {
					unexpectedRollback = false;
				}
			}
		}
		catch (RuntimeException | Error ex) {
			triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN);
			throw ex;
		}
		triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK);
		// Raise UnexpectedRollbackException if we had a global rollback-only marker
		if (unexpectedRollback) {
			throw new UnexpectedRollbackException(
					"Transaction rolled back because it has been marked as rollback-only");
		}
	}
	finally {
		cleanupAfterCompletion(status);
	}
相关推荐
lzb_kkk3 分钟前
【JavaEE】JUC的常见类
java·开发语言·java-ee
爬山算法27 分钟前
Maven(28)如何使用Maven进行依赖解析?
java·maven
hlsd#34 分钟前
go mod 依赖管理
开发语言·后端·golang
陈大爷(有低保)39 分钟前
三层架构和MVC以及它们的融合
后端·mvc
亦世凡华、39 分钟前
【启程Golang之旅】从零开始构建可扩展的微服务架构
开发语言·经验分享·后端·golang
河西石头40 分钟前
一步一步从asp.net core mvc中访问asp.net core WebApi
后端·asp.net·mvc·.net core访问api·httpclient的使用
2401_857439691 小时前
SpringBoot框架在资产管理中的应用
java·spring boot·后端
怀旧6661 小时前
spring boot 项目配置https服务
java·spring boot·后端·学习·个人开发·1024程序员节
李老头探索1 小时前
Java面试之Java中实现多线程有几种方法
java·开发语言·面试
芒果披萨1 小时前
Filter和Listener
java·filter