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

编程入门 行业动态 更新时间:2024-10-24 18:26:32

Spring<a href=https://www.elefans.com/category/jswz/34/1770772.html style=事务最佳应用指南(包含:事务传播类型、事务失效场景、使用建议、事务源码分析)"/>

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嵌套方式使用前一个方法的事务创建一个新的事务

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

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

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两张表进行更新。

<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中的方法时会遇到异常。


@Service
public class T1Service {@Resourceprivate TestMapper testMapper;@Resourceprivate T2Service t2Service;@Transactionalpublic void func() {testMapper.updateT1();t2Service.func();}
}@Service
public class T2Service {@Resourceprivate TestMapper testMapper;@Transactionalpublic 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两张表数据都不会回滚。

@Service
public class T1Service {@Resourceprivate TestMapper testMapper;@Resourceprivate T2Service t2Service;// 去掉事务 @Transactionalpublic void func() {testMapper.updateT1();t2Service.func();}
}
@Service
public class T2Service {@Resourceprivate 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)

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

异常信息

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的数据不会被更新。

// 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中。

// 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的数据。

// 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

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

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

// t1Service
@Transactional
public void func() {testMapper.updateT1();t2Service.func();
}// t2Service
@Transactional(propagation = Propagation.NEVER)
public void func() {testMapper.updateT2();
}
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自己抛出异常后,数据也是不会回滚的,完全是当做无事务处理。

// 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抛出的异常进行了捕获,并且自己也没有再抛出。


// 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则不会回滚。

// 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());用来查看一下当前执行的事务。

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

输出结果

com.demo.transaction.service.T1Service.func
com.demo.transaction.service.T2Service.func

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

com.demo.transaction.service.T1Service.func
com.demo.transaction.service.T1Service.func

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

实现原理

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

-- 主事务
savepoint;
-- 执行主事务代码-- 子事务
savepoint;-- 执行子事务代码-- 子事务提交
commit;-- 执行主事务代码-- 主事务提交
commit;

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

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

事务的失效场景

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

异常未抛出

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

// 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及其子类的异常,如果是受检异常或者其他自定义的不属于其子类的异常是不会回滚事务的。

@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即可

// 改成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()执行的方法并不会回滚。

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

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

@Service
public class T1Service {@Resourceprivate TestMapper testMapper;// 注入T1Service对象@Resourceprivate T1Service t1Service;public void func() {testMapper.updateT1();// 通过注入的方式调用t1Service.func2();}@Transactionalpublic void func2() {testMapper.updateT2();int i = 1 / 0;}
}

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

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

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

spring.main.allow-circular-references=true

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

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()方法操作的数据并不会被回滚。

@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无法为其生成代理。

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注解的,因为这样会导致这个类中的所有方法在执行时都带上了事务,这样原来根本就不需要事务的方法则多了一份负担。

@Service
@Transactional // 不要加在class级别
public class DemoService { 
}

长事务、过早起开事务

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

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

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

public void func() {select1();select2();manager.save();
}@Transactional
public void save() {save1();save2();
}

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

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

事务锁的问题

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

事务的整体架构展示

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

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

类的关系图

TransactionManager接口

标识性接口

public interface TransactionManager {}

PlatformTransactionManager接口

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

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也是一个标记性接口。

@FunctionalInterface
public interface TransactionCallback<T> {@NullableT doInTransaction(TransactionStatus status);
}
@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 -> rollbackrollbackOnException(status, ex);throw ex;}catch (Throwable ex) {// Transactional code threw unexpected exception -> rollbackrollbackOnException(status, ex);throw new UndeclaredThrowableException(ex, "TransactionCallback threw undeclared checked exception");}this.transactionManagermit(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这两个方法完成。

@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方法来实现。

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 doCommittriggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK);throw ex;}catch (TransactionException ex) {// can only be caused by doCommitif (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的使用标准。

@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 {conmit();}catch (SQLException ex) {throw translateException("JDBC commit", ex);}
}

processRollback

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

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 transactionif (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 earlyif (!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 markerif (unexpectedRollback) {throw new UnexpectedRollbackException("Transaction rolled back because it has been marked as rollback-only");}}finally {cleanupAfterCompletion(status);}

更多推荐

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

本文发布于:2023-11-16 08:25:09,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1614860.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:事务   源码   场景   类型   建议

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!