Spring5源码(48)-@Transactional声明式事物(六)嵌套事物处理

简介: Spring5源码(48)-@Transactional声明式事物(六)嵌套事物处理


1.引

上一节简单介绍了Spring中的嵌套事物,接下来针对每一种传播特性,单独分析,当然前提是已经存在一个事物

2.PROPAGATION_NEVER

该特性规定了以非事物方式执行,如果当前存在事物,则抛出异常。

// 1.PROPAGATION_NEVER --> 以非事物方式执行,如果当前存在事物,则抛出异常。
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NEVER) {
    throw new IllegalTransactionStateException("Existing transaction found for transaction marked with propagation 'never'");
}
3.PROPAGATION_NOT_SUPPORTED

该特性规定了以非事物方式执行,如果当前存在事物,则挂起当前事物。

if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NOT_SUPPORTED) {
    if (debugEnabled) { logger.debug("Suspending current transaction");}
    // 重点:挂起已有事物
    Object suspendedResources = suspend(transaction);
    boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
    // 创建新事物,注意:transaction参数为null,所以这里创建的不是一个真正的事物
    return prepareTransactionStatus(
            definition,
            null,
            false,
            newSynchronization,
            debugEnabled,
            suspendedResources);
}

这里又涉及到一个概念,事物挂起(这个前面没有介绍,因为前面都是单个事物)。下面分析一下事物挂起的流程:

protected final SuspendedResourcesHolder suspend(@Nullable Object transaction) throws TransactionException {
    // 1.如果存在事物同步回调接口
    if (TransactionSynchronizationManager.isSynchronizationActive()) {
        // 1.1 挂起事务同步回调接口
        List<TransactionSynchronization> suspendedSynchronizations = doSuspendSynchronization();
        try {
            // 挂起事物
            Object suspendedResources = null;
            if (transaction != null) {
                suspendedResources = doSuspend(transaction);
            }
            // 获取已有事物名称
            String name = TransactionSynchronizationManager.getCurrentTransactionName();
            // 清空已有事物名称
            TransactionSynchronizationManager.setCurrentTransactionName(null);
            // 获取已有事物的readOnly属性值
            boolean readOnly = TransactionSynchronizationManager.isCurrentTransactionReadOnly();
            // 将已有事物的readOnly属性值设置为false
            TransactionSynchronizationManager.setCurrentTransactionReadOnly(false);
            // 获取已有事物数据库事物隔离级别
            Integer isolationLevel = TransactionSynchronizationManager.getCurrentTransactionIsolationLevel();
            // 清空已有事物数据库事物隔离级别
            TransactionSynchronizationManager.setCurrentTransactionIsolationLevel(null);
            // 获取已有事物激活标识
            boolean wasActive = TransactionSynchronizationManager.isActualTransactionActive();
            // 将当前事物激活标识设置为false
            TransactionSynchronizationManager.setActualTransactionActive(false);
            // 返回SuspendedResourcesHolder
            /**
             * 将上面获取到的一系列事物属性,重新封装至SuspendedResourcesHolder对象,并返回
             */
            return new SuspendedResourcesHolder(suspendedResources, suspendedSynchronizations, name, readOnly, isolationLevel, wasActive);
        }
        catch (RuntimeException | Error ex) {
            // doSuspend failed - original transaction is still active...
            doResumeSynchronization(suspendedSynchronizations);
            throw ex;
        }
    }
    // 不存在事物同步回调接口,且当前事物不为空
    else if (transaction != null) {
        // Transaction active but no synchronization active.
        // 事物已经被激活,但是没有事物同步回调,则直接挂起当前事物即可
        Object suspendedResources = doSuspend(transaction);
        // 返回挂起的事物资源
        return new SuspendedResourcesHolder(suspendedResources);
    }
    // 处理没有事物的情况...
    else {
        // Neither transaction nor synchronization active.
        return null;
    }
}

首先来看事物同步回调接口,该接口会在prepareSynchronization方法中设置,即使我们没有自定义事物同步回调接口,Spring默认也会为当前事物创建一个空的事物同步回调接口。关于该接口的使用,可以参考上一节中的例子。(注意:事物同步回调接口是与当前线程绑定的)。下面看事物同步接口是如何挂起的:

private List<TransactionSynchronization> doSuspendSynchronization() {
    // 1.获取当前线程的所有事物同步回调
    List<TransactionSynchronization> suspendedSynchronizations = TransactionSynchronizationManager.getSynchronizations();
    // 2.循环并挂起所有同步回调接口
    for (TransactionSynchronization synchronization : suspendedSynchronizations) {
        synchronization.suspend();
    }
    // 3.清除资源
    TransactionSynchronizationManager.clearSynchronization();
    return suspendedSynchronizations;
}

其次来看挂起事物的流程,注释里已经写了很清楚了,最后会将已有的事物属性封装到SuspendedResourcesHolder对象中,该类就持有了被挂起事物的属性。接下来调用prepareTransactionStatus方法并将suspendedResources入参,这样一来,新创建的事物就持有了被挂起事物的的属性,就会形成一个事物链。而且新创建的事物transaction参数为null,所以PROPAGATION_NOT_SUPPORTED特性是不会真正开启事物的。

return prepareTransactionStatus(
                    definition,
                    null,
                    false,
                    newSynchronization,
                    debugEnabled,
                    suspendedResources);
4.PROPAGATION_REQUIRES_NEW

if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW) {
    if (debugEnabled) {
        logger.debug("Suspending current transaction, creating new transaction with name [" + definition.getName() + "]");
    }
    // 挂起已有事物
    SuspendedResourcesHolder suspendedResources = suspend(transaction);
    try {
        boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
        // 创建事物
        DefaultTransactionStatus status = newTransactionStatus(
                definition,
                transaction,
                true,
                newSynchronization,
                debugEnabled,
                suspendedResources);
        // 开启事物
        doBegin(transaction, definition);
        // 初始化事物同步属性
        prepareSynchronization(status, definition);
        return status;
    }
    catch (RuntimeException | Error beginEx) {
        resumeAfterBeginException(transaction, suspendedResources, beginEx);
        throw beginEx;
    }
}

该特性下会挂起已有事物并新建一个事物。

5.PROPAGATION_NESTED

if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
    // 如果不允许嵌套事物,则抛出异常
    if (!isNestedTransactionAllowed()) {
        throw new NestedTransactionNotSupportedException("Transaction manager does not allow nested transactions by default - " +
                "specify 'nestedTransactionAllowed' property with value 'true'");
    }
    if (debugEnabled) {
        logger.debug("Creating nested transaction with name [" + definition.getName() + "]");
    }
    /**
     * 下面对JtaTransactionManager和AbstractPlatformTransactionManager分别进行处理
     */
    // useSavepointForNestedTransaction(),是否为嵌套事务使用保存点
    // 1.对于JtaTransactionManager-->返回false
    // 2.对于AbstractPlatformTransactionManager-->返回true
    if (useSavepointForNestedTransaction()) {
        // Create savepoint within existing Spring-managed transaction,
        // through the SavepointManager API implemented by TransactionStatus.
        // Usually uses JDBC 3.0 savepoints. Never activates Spring synchronization.
        // 创建保存点在现有spring管理事务,通过TransactionStatus SavepointManager API实现。
        // 通常使用JDBC 3.0保存点。永远不要激活Spring同步。
        DefaultTransactionStatus status = prepareTransactionStatus(definition,transaction,
                false,
                false,
                debugEnabled,
                null);
        // 创建保存点
        status.createAndHoldSavepoint();
        return status;
    }
    else {
        // Nested transaction through nested begin and commit/rollback calls.
        // Usually only for JTA: Spring synchronization might get activated here
        // in case of a pre-existing JTA transaction.
        // 通过嵌套的开始,提交调用,及回滚调用进行嵌套事务。
        // 只对JTA有效,如果已经存在JTA事务,这里可能会激活Spring同步。
        boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
        DefaultTransactionStatus status = newTransactionStatus(
                definition,
                transaction,
                true,
                newSynchronization,
                debugEnabled,
                null);
        doBegin(transaction, definition);
        prepareSynchronization(status, definition);
        return status;
    }
}

这里对AbstractPlatformTransactionManager和JtaTransactionManager分别做了不同的处理,重点看前者。AbstractPlatformTransactionManager是允许使用保存点的。接下来看保存点的创建过程:

// 创建保存点
public void createAndHoldSavepoint() throws TransactionException {
    setSavepoint(getSavepointManager().createSavepoint());
}

// 获取SavepointManager
protected SavepointManager getSavepointManager() {
    Object transaction = this.transaction;
    if (!(transaction instanceof SavepointManager)) {
        throw new NestedTransactionNotSupportedException(
            "Transaction object [" + this.transaction + "] does not support savepoints");
    }
    // SavepointManager就是当前事物
    return (SavepointManager) transaction;
}

// 创建保存点
public Object createSavepoint() throws TransactionException {
    // 获取ConnectionHolder
    ConnectionHolder conHolder = getConnectionHolderForSavepoint();
    try {
        // 如果当前连接不支持保存点,抛出异常
        if (!conHolder.supportsSavepoints()) {
            throw new NestedTransactionNotSupportedException(
                    "Cannot create a nested transaction because savepoints are not supported by your JDBC driver");
        }
        // 如果当前连接的RollbackOnly已经被标记为true,抛出异常
        if (conHolder.isRollbackOnly()) {
            throw new CannotCreateTransactionException(
                    "Cannot create savepoint for transaction which is already marked as rollback-only");
        }
        // 创建保存点
        return conHolder.createSavepoint();
    }
    catch (SQLException ex) {
        throw new CannotCreateTransactionException("Could not create JDBC savepoint", ex);
    }
}

总体来讲创建保存点的过程比较简单,最终会调用数据库驱动的底层方法去创建保存点,感兴趣的可以自己跟踪查看。创建完保存点之后将结果设置到TransactionStatus对象中。

该特性下会将新事物在原有事物内,以嵌套的方式运行,该特性也不会开启一个新的事物

6. PROPAGATION_SUPPORTS和PROPAGATION_REQUIRED

假如事物传播特性是PROPAGATION_SUPPORTS或PROPAGATION_REQUIRED的话,将会进行如下处理:

if (isValidateExistingTransaction()) {
    // 验证事物隔离级别
    // 如果当前事物的隔离级别不为默认隔离级别,则比较当前事物隔离级别与已有事物隔离级别,
    // 如不同,则抛出事物隔离级别不兼容异常
    if (definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT) {
        Integer currentIsolationLevel = TransactionSynchronizationManager.getCurrentTransactionIsolationLevel();
        if (currentIsolationLevel == null || currentIsolationLevel != definition.getIsolationLevel()) {
            Constants isoConstants = DefaultTransactionDefinition.constants;
            throw new IllegalTransactionStateException("Participating transaction with definition [" +
                    definition + "] specifies isolation level which is incompatible with existing transaction: " +
                    (currentIsolationLevel != null ?
                            isoConstants.toCode(currentIsolationLevel, DefaultTransactionDefinition.PREFIX_ISOLATION) :
                            "(unknown)"));
        }
    }
    // 验证事物只读属性
    // 如果当前事物可写,但是已有的事物是只读,则抛出异常
    if (!definition.isReadOnly()) {
        if (TransactionSynchronizationManager.isCurrentTransactionReadOnly()) {
            throw new IllegalTransactionStateException("Participating transaction with definition [" +
                    definition + "] is not marked as read-only but existing transaction is");
        }
    }
}
boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
return prepareTransactionStatus(definition, transaction, false, newSynchronization, debugEnabled, null);

isValidateExistingTransaction()该函数的作用是指定新事物参与已有事物时,新旧两个事物的验证级别。该属性值默认为false,宽松范围的验证,也就是不验证。如果将该属性值改为true的话,那么将会验证新旧两个事物的数据库事物隔离级别、事物只读属性是否相同。对于PROPAGATION_MANDATORY,从Spring的注释上没有看到对改属性的处理,但是该属性也会走这个分支,即使用已有事物,由于到这里肯定是已经存在事物的,该特性在这里不会抛出异常。

该特性也不会开启一个新的事物,而是继续在原有事物中运行或者以非事物方式运行



目录
相关文章
|
2月前
|
设计模式 Java 开发者
如何快速上手【Spring AOP】?从动态代理到源码剖析(下篇)
Spring AOP的实现本质上依赖于代理模式这一经典设计模式。代理模式通过引入代理对象作为目标对象的中间层,实现了对目标对象访问的控制与增强,其核心价值在于解耦核心业务逻辑与横切关注点。在框架设计中,这种模式广泛用于实现功能扩展(如远程调用、延迟加载)、行为拦截(如权限校验、异常处理)等场景,为系统提供了更高的灵活性和可维护性。
|
6月前
|
前端开发 Java 物联网
智慧班牌源码,采用Java + Spring Boot后端框架,搭配Vue2前端技术,支持SaaS云部署
智慧班牌系统是一款基于信息化与物联网技术的校园管理工具,集成电子屏显示、人脸识别及数据交互功能,实现班级信息展示、智能考勤与家校互通。系统采用Java + Spring Boot后端框架,搭配Vue2前端技术,支持SaaS云部署与私有化定制。核心功能涵盖信息发布、考勤管理、教务处理及数据分析,助力校园文化建设与教学优化。其综合性和可扩展性有效打破数据孤岛,提升交互体验并降低管理成本,适用于日常教学、考试管理和应急场景,为智慧校园建设提供全面解决方案。
416 70
|
6月前
|
Java 关系型数据库 MySQL
深入解析 @Transactional——Spring 事务管理的核心
本文深入解析了 Spring Boot 中 `@Transactional` 的工作机制、常见陷阱及最佳实践。作为事务管理的核心注解,`@Transactional` 确保数据库操作的原子性,避免数据不一致问题。文章通过示例讲解了其基本用法、默认回滚规则(仅未捕获的运行时异常触发回滚)、因 `try-catch` 或方法访问修饰符不当导致失效的情况,以及数据库引擎对事务的支持要求。最后总结了使用 `@Transactional` 的五大最佳实践,帮助开发者规避常见问题,提升项目稳定性与可靠性。
959 12
|
7月前
|
存储 监控 数据可视化
SaaS云计算技术的智慧工地源码,基于Java+Spring Cloud框架开发
智慧工地源码基于微服务+Java+Spring Cloud +UniApp +MySql架构,利用传感器、监控摄像头、AI、大数据等技术,实现施工现场的实时监测、数据分析与智能决策。平台涵盖人员、车辆、视频监控、施工质量、设备、环境和能耗管理七大维度,提供可视化管理、智能化报警、移动智能办公及分布计算存储等功能,全面提升工地的安全性、效率和质量。
142 0
|
9月前
|
监控 JavaScript 数据可视化
建筑施工一体化信息管理平台源码,支持微服务架构,采用Java、Spring Cloud、Vue等技术开发。
智慧工地云平台是专为建筑施工领域打造的一体化信息管理平台,利用大数据、云计算、物联网等技术,实现施工区域各系统数据汇总与可视化管理。平台涵盖人员、设备、物料、环境等关键因素的实时监控与数据分析,提供远程指挥、决策支持等功能,提升工作效率,促进产业信息化发展。系统由PC端、APP移动端及项目、监管、数据屏三大平台组成,支持微服务架构,采用Java、Spring Cloud、Vue等技术开发。
336 7
|
3月前
|
Java Spring 容器
SpringBoot自动配置的原理是什么?
Spring Boot自动配置核心在于@EnableAutoConfiguration注解,它通过@Import导入配置选择器,加载META-INF/spring.factories中定义的自动配置类。这些类根据@Conditional系列注解判断是否生效。但Spring Boot 3.0后已弃用spring.factories,改用新格式的.imports文件进行配置。
788 0
|
16天前
|
JavaScript Java Maven
【SpringBoot(二)】带你认识Yaml配置文件类型、SpringMVC的资源访问路径 和 静态资源配置的原理!
SpringBoot专栏第二章,从本章开始正式进入SpringBoot的WEB阶段开发,本章先带你认识yaml配置文件和资源的路径配置原理,以方便在后面的文章中打下基础
145 3
|
16天前
|
Java 测试技术 数据库连接
【SpringBoot(四)】还不懂文件上传?JUnit使用?本文带你了解SpringBoot的文件上传、异常处理、组件注入等知识!并且带你领悟JUnit单元测试的使用!
Spring专栏第四章,本文带你上手 SpringBoot 的文件上传、异常处理、组件注入等功能 并且为你演示Junit5的基础上手体验
468 2
|
4月前
|
人工智能 Java 测试技术
Spring Boot 集成 JUnit 单元测试
本文介绍了在Spring Boot中使用JUnit 5进行单元测试的常用方法与技巧,包括添加依赖、编写测试类、使用@SpringBootTest参数、自动装配测试模块(如JSON、MVC、WebFlux、JDBC等),以及@MockBean和@SpyBean的应用。内容实用,适合Java开发者参考学习。
492 0

热门文章

最新文章