【源码】Spring事务之传播特性的详解

Spring事务系列

1、【源码】SpringBoot事务注册原理

2、【源码】Spring Data JPA原理解析之事务注册原理

3、【源码】Spring Data JPA原理解析之事务执行原理

4、【源码】SpringBoot编程式事务使用及执行原理

5、【源码】Spring事务之传播特性的详解

6、【源码】Spring事务之事务失效及原理

前言

《Spring事务系列》的前面4篇博文从源码的角度分析了Spring事务的注册以及执行原理,本篇分享一下事务的传播特性。

事务传播特性

Spring事务包含七种的传播特性,在Propagation枚举类中,分别如下:

1)REQUIRE:如果当前没有事务,则新创建一个事务;如果上下文存在事务,则加入到这个事务中。默认值;

2)SUPPORTS:如果当前上下文存在事务,则加入这个事务;如果上下文不存在事务,则使用无事务的方式执行。即:如果上下文有事务,则加入这个事务;否则直接执行,不会新创建事务;

3)MANDATORY:如果当前上下文存在事务,则加入这个事务;如果上下文不存在事务,则报错;

4)REQUIRES_NEW:每次都新创建一个事务。如果当前上下文有事务,则挂起上下文的事务,重新创建一个事务;如果当前上下文没有事务,新创建一个事务;

如:ServiceA.method()调用ServiceB.method(),在ServiceB.method()使用REQUIRES_NEW传播特性。如果ServiceB.method()提交后,ServiceA.method()报错,则ServiceB.method()不会回滚,ServiceA.method()会回滚;如果ServiceB.method()报错,则ServiceA.method()可以通过捕获异常,选择回滚或提交。

5)NOT_SUPPORTED:不支持事务执行。如果当前上下文存在事务,则挂起上下文事务;

如:ServiceA.method()调用ServiceB.method(),在ServiceB.method()使用NOT_SUPPORTED传播特性,则ServiceB.method()的执行不在事务范围内。如果ServiceB.method()报错,则ServiceA.method()可以通过捕获异常,选择回滚或提交。

6)NEVER:无事务执行。如果当前上下文存在事务,则抛异常;

如:ServiceA.method()调用ServiceB.method(),在ServiceB.method()使用NEVER传播特性,则ServiceB.method()的整条调用链中都不能创建事务,否则会报错。

7)NESTED:嵌套事务。如果上下文存在事务,则在嵌套的事务中执行;如果上下文没有事务,则新创建一个事务;

如:ServiceA.method()调用ServiceB.method(),在ServiceB.method()使用NESTED传播特性。如果ServiceB.method()报错,则只回滚ServiceB.method()中的信息,对ServiceA.method()没有影响。通过savepoint实现。

源码解析

事务的传播特性可以通过@Transactional注解的propagation属性进行设置,对于Propagation枚举类中每一种传播特性,在TransactionDefinition中都对应一个属性值。

3.1 Propagation

Propagation的源码如下:

public enum Propagation {// 需要事务。如果当前没有事务,则新创建一个事务;如果上下文存在事务,则加入到这个事务中。默认值;REQUIRED(TransactionDefinition.PROPAGATION_REQUIRED),// 如果当前上下文存在事务,则加入这个事务;如果上下文不存在事务,则使用无事务的方式执行。即:如果上下文有事务,则加入这个事务;否则直接执行,不会新创建事务;SUPPORTS(TransactionDefinition.PROPAGATION_SUPPORTS),// 如果当前上下文存在事务,则加入这个事务;如果上下文不存在事务,则报错;MANDATORY(TransactionDefinition.PROPAGATION_MANDATORY),// 每次都新创建一个事务。如果当前上下文有事务,则挂起上下文的事务,重新创建一个事务;如果当前上下文没有事务,新创建一个事务;REQUIRES_NEW(TransactionDefinition.PROPAGATION_REQUIRES_NEW),// 不支持事务执行。如果当前上下文存在事务,则挂起上下文事务;NOT_SUPPORTED(TransactionDefinition.PROPAGATION_NOT_SUPPORTED),// 无事务执行。如果当前上下文存在事务,则抛异常;NEVER(TransactionDefinition.PROPAGATION_NEVER),// 嵌套事务。如果上下文存在事务,则在嵌套的事务中执行;如果上下文没有事务,则新创建一个事务;NESTED(TransactionDefinition.PROPAGATION_NESTED);private final int value;Propagation(int value) {this.value = value;}public int value() {return this.value;}}

3.2 传播特性判断

Spring事务中的传播特性在AbstractPlatformTransactionManager类中进行判断,详见

【源码】Spring Data JPA原理解析之事务执行原理-CSDN博客

传播特性判断的核心代码如下:

public abstract class AbstractPlatformTransactionManager implements PlatformTransactionManager, Serializable {@Overridepublic final TransactionStatus getTransaction(@Nullable TransactionDefinition definition) throws TransactionException {// 抽象方法。如JpaTransactionManager.doGetTransaction(),创建一个JpaTransactionObject对象,开启一个新的ConnectionObject transaction = doGetTransaction();boolean debugEnabled = logger.isDebugEnabled();if (definition == null) {definition = new DefaultTransactionDefinition();}// 如果事务存在,则检测传播行为并返回if (isExistingTransaction(transaction)) {// 找到现有事务->检查传播行为以了解行为方式return handleExistingTransaction(definition, transaction, debugEnabled);}// 检查事务属性中的超时属性,默认为-1if (definition.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) {throw new InvalidTimeoutException("Invalid transaction timeout", definition.getTimeout());}// 如果事务的传播特性为PROPAGATION_MANDATORY,则抛异常if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {throw new IllegalTransactionStateException("No existing transaction found for transaction marked with propagation 'mandatory'");}// 如果事务的传播特性为PROPAGATION_REQUIRED、PROPAGATION_REQUIRES_NEW、PROPAGATION_NESTED,// 此时没有事务,所以需要创建新的事务else if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {// 挂起事务,此处返回的suspendedResources为nullSuspendedResourcesHolder suspendedResources = suspend(null);if (debugEnabled) {logger.debug("Creating new transaction with name [" + definition.getName() + "]: " + definition);}try {// 初始getTransactionSynchronization()为0,需要激活事务同步// SYNCHRONIZATION_NEVER为2,表示不激活事务同步boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);// 创建一个DefaultTransactionStatus对象,传入刚创建的transaction对象,新开启一个连接DefaultTransactionStatus status = newTransactionStatus(definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);// 开始事务doBegin(transaction, definition);// 准备同步,将事务相关信息保存到本地线程变量prepareSynchronization(status, definition);return status;}catch (RuntimeException | Error ex) {resume(null, suspendedResources);throw ex;}}else {// 传播特性为PROPAGATION_SUPPORTS、PROPAGATION_NOT_SUPPORTED、PROPAGATION_NEVER,此时没有事务,则不开启事务执行if (definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT && logger.isWarnEnabled()) {logger.warn("Custom isolation level specified but no actual transaction initiated; " +"isolation level will effectively be ignored: " + definition);}boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);// 创建一个DefaultTransactionStatus对象,此处的transaction为nullreturn prepareTransactionStatus(definition, null, true, newSynchronization, debugEnabled, null);}}private TransactionStatus handleExistingTransaction(TransactionDefinition definition, Object transaction, boolean debugEnabled)throws TransactionException {// 如果事务的传播特性为PROPAGATION_NEVER,因为上下文存在事务,则抛异常if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NEVER) {throw new IllegalTransactionStateException("Existing transaction found for transaction marked with propagation 'never'");}// 如果事务的传播特性为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);// 创建一个DefaultTransactionStatus对象,此处的transaction为nullreturn prepareTransactionStatus(definition, null, false, newSynchronization, debugEnabled, suspendedResources);}// 如果事务的传播特性为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对象,此处的transaction为新创建的事务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;}}// 如果事务的传播特性为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() + "]");}// 如果支持savepoint保存点,则创建保存点if (useSavepointForNestedTransaction()) {DefaultTransactionStatus status =prepareTransactionStatus(definition, transaction, false, false, debugEnabled, null);status.createAndHoldSavepoint();return status;}else { // 否则的话,创建新的事务boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);DefaultTransactionStatus status = newTransactionStatus(definition, transaction, true, newSynchronization, debugEnabled, null);doBegin(transaction, definition);prepareSynchronization(status, definition);return status;}}if (debugEnabled) {logger.debug("Participating in existing transaction");}// 判断是否需要验证现有的事务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);}}

事务的传播特性判断在AbstractPlatformTransactionManager的getTransaction()和handleExistingTransaction()方法。

3.2.1 getTransaction()

getTransaction()方法主要执行如下:

1)执行抽象方法doGetTransaction(),获取一个事务对象。如如JpaTransactionManager.doGetTransaction(),创建一个JpaTransactionObject对象,开启一个新的Connection;

2)判断上下文是否存在事务,如果有,则执行handleExistingTransaction()方法;

3)如果没有事务,执行如下:

3.1)如果事务的传播特性为PROPAGATION_MANDATORY,则抛异常;

3.2)如果事务的传播特性为PROPAGATION_REQUIRED或PROPAGATION_REQUIRES_NEW或PROPAGATION_NESTED,此时没有事务,所以需要创建新的事务;

3.3)否则,传播特性为PROPAGATION_SUPPORTS或PROPAGATION_NOT_SUPPORTED或PROPAGATION_NEVER,此时没有事务,则不开启事务,继续执行;

3.2.2 handleExistingTransaction()

handleExistingTransaction()执行时,说明当前上下文存在事务,方法主要执行如下:

1)如果事务的传播特性为PROPAGATION_NEVER,因为上下文存在事务,则抛异常;

2)如果事务的传播特性为PROPAGATION_NOT_SUPPORTED,因为上下文存在事务,则挂起事务,继续执行;

3)如果事务的传播特性为PROPAGATION_REQUIRES_NEW,因为上下文存在事务,则挂起事务,新创建一个事务;

4)如果事务的传播特性为PROPAGATION_NESTED,因为上下文存在事务,则使用嵌套事务;

4.1)如果支持savepoint保存点,则创建保存点。如果保存点中的报异常,则只回滚保存点的事务;

4.2)如果不支持savepoint,则创建新事务;

小结

在实际的项目中,需要根据实际的业务需要,设置合理的传播特性,使用不当,不仅会导致事务失效,还可能导致数据不一致问题。

以上为本篇分享的全部内容,关于本篇内容你有什么自己的想法或独到见解,欢迎在评论区一起交流探讨下吧。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/28994.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

简说Navicat

Navicat 是一款功能强大的数据库管理工具&#xff0c;广泛应用于管理和开发各种数据库&#xff0c;如 MySQL、MariaDB、SQL Server、SQLite、Oracle 和 PostgreSQL。Navicat 提供了一系列直观的图形界面和丰富的功能&#xff0c;极大地简化了数据库管理和开发的复杂性。 Navic…

Zato库(续)

Zato库高级教程&#xff08;续&#xff09; 在前面的教程中&#xff0c;我们介绍了Zato的基础功能和一些进阶功能。现在&#xff0c;我们将进一步探讨一些高级功能和实际应用中的使用场景&#xff0c;以帮助开发者更好地掌握Zato的强大功能。 负载均衡和高可用配置 配置多服…

配置文件-基础配置,applicationproperties.yml

黑马程序员Spring Boot2 文章目录 1、属性配置2、配置文件分类3、yaml文件4、yaml数据读取4.1 读取单个数据4.2 读取全部属性数据4.3 读取引用类型属性数据 1、属性配置 SpringBoot默认配置文件application.properties&#xff0c;通过键值对配置对应属性修改配置 修改服务器端…

浏览器必装插件推荐:最新版Simple Allow Copy,解除网页复制限制!

经常在网上找资料的朋友&#xff0c;尤其是学生党&#xff0c;总会遇到一个问题&#xff1a;很多资料网站的文字是禁止复制的。于是大家通常会使用各种文字识别软件来图文转换&#xff0c;或者直接手打。 今天这款小工具&#xff0c;可以轻松复制各种氪金网站上的任何文字&…

文件查看相关的命令(Linux篇)

1.cat&#xff1a;从文件上到下查看文件内容的命令 [rootlocalhost ~]# cd Desktop/ [rootlocalhost Desktop]# ls xxx.txt [rootlocalhost Desktop]# cat xxx.txt 1.shdadjaksdj 2.bbbbbbbbbbb 3.wewqhwiehqi gggggggggggggggg hhhhhhhhhhhhhh zzzzzzzzzzzz 2.tac&#xff1…

视频监控平台:通过网络SDK对TCL网络摄像机进行PTZ控制 的源代码介绍及分享

目录 一、视频监控平台介绍 &#xff08;一&#xff09;概述 &#xff08;二&#xff09;视频接入能力介绍 &#xff08;三&#xff09;功能介绍 二、TCL网络摄像机 &#xff08;一&#xff09;360度全景自动旋转&#xff1a; &#xff08;二&#xff09;高清夜视和全彩…

Tustin变换,连续传递函数离散化

Tustin变换&#xff0c;连续传递函数离散化 举例 上述说明了&#xff0c;不要盲目相信ChatGPT f_vehspd 12; phase_vehspd 120; f_res f_vehspd / tan((360-phase_vehspd) * pi/(4*180) );%连续传递函数 num [1 -2*f_res f_res^2]; den [1 2*f_res f_res^2]; sys tf(num…

示例:WPF中绑定枚举到ComboBox的方式

一、目的&#xff1a;在开发过程中&#xff0c;经常会需要把枚举绑定到ComboxBox下拉列表中&#xff0c;其实方法有很多&#xff0c;这里面通过MarkupExtension扩展GetEnumSourceExtension去绑定到列表 二、实现 定义GetEnumSourceExtension类 public class GetEnumSourceExte…

Part 4.2 背包动态规划

->背包模型模板(0/1,分组&#xff0c;完全&#xff0c;多重)<- [NOIP2018 提高组] 货币系统 题目背景 NOIP2018 提高组 D1T2 题目描述 在网友的国度中共有 n n n 种不同面额的货币&#xff0c;第 i i i 种货币的面额为 a [ i ] a[i] a[i]&#xff0c;你可以假设每…

算法03 二分查找算法【C++实现】

二分查找的概念 二分查找又称为折半查找&#xff0c;主要用于查找一个有序数组中某一个数的位置。 主要思想如下&#xff1a; 在一个有序数组中&#xff0c;取数组的中间值与要查找的数进行比较&#xff1b; 若要查找的数等于中间值&#xff0c;查找成功。 二分查找的步骤 …

Node.js安装扫盲

一、Node.js安装 在官网下载node.js安装包 双击打开node-v20.14.0-x64.ms文件&#xff0c;点击运行 进入安装Node.js的对话框&#xff0c;点击Next继续 勾选复选框后点击Next继续 默认安装路径 默认配置 这里不需要勾选&#xff0c;直接点击Next 点击Install 二、Node.js验…

【安装和引入 PyTorch 包,快来收藏】

在本文介绍 PyTorch 中一些最常用的命令和设置。 一个完成的 PyTorch 工作流程。 安装和引入 PyTorch 包 最好的安装教程就是去官方网站&#xff1a;https://pytorch.org/get-started/locally/ 安装结束之后&#xff0c;直接引入整个 torch 包&#xff1a; import torch或…

再也不怕离职同事泄露配置文件的密码了

Jasypt是一个功能强大、易于使用的Java加密库&#xff0c;适用于需要进行数据加密和安全保护的各类应用程序和系统。 实战 1、引入依赖 说明&#xff1a;JDK1.8 选择2.x版本&#xff0c;高一些版本的JDK就选择3.x的版本。 xml <!-- jasypt 加解密 --> <dependency&…

SQL注入-上篇

SQL注入 注入是web安全的头号大敌。注入攻击漏洞往往是应用程序缺少对输入进行安全性检查所引起的。攻击者把一些包含攻击代码当做命令或者查询语句发送给解释器&#xff0c;这些恶意数据可以欺骗解释器&#xff0c;从而执行计划外的命令或者未授权访问数据。注入漏洞通常能sq…

MySQL约束详解:构建数据完整性基石

目录 MySQL约束1.1 约束1.1 数据类型1.2 主键约束[重要]1.3 自增约束1.4 唯一约束1.5 非空约束1.6 默认值代码演示 1.7 外键约束[了解]思维导图最后 MySQL约束 MySQL作为广泛使用的开源关系型数据库管理系统&#xff0c;其强大的数据约束功能对于维护数据的一致性和准确性至关…

4.类,方法,对象

1.1.2. 面向对象程序设计的三大特征 1.1.2.1. 封装 面向对象编程核心思想之一就是将数据和对数据的操作封装在一起&#xff0c;形成一般的概念&#xff0c;比如类的概念。 1.1.2.2. 继承 继承体现了一种先进的编程模式。子类可以继承父类的属性和方法。 1.1.2.3. 多态 多…

Day 44 Ansible自动化运维

Ansible自动化运维 几种常用运维工具比较 ​ Puppet ​ —基于 Ruby 开发,采用 C/S 架构,扩展性强,基于 SSL,远程命令执行相对较弱ruby ​ SaltStack ​ —基于 Python 开发,采用 C/S 架构,相对 puppet 更轻量级,配置语法使用 YAML,使得配置脚本更简单 ​ Ansible ​ —基于 …

Python学习笔记12:进阶篇(一),类的相关知识

前言 在讲类之前&#xff0c;我们简单介绍一些Python的知识。这些知识在入门篇没讲&#xff0c;想学Python的&#xff0c;基本都对Python有基础的了解&#xff0c;但是今天开始的进阶知识&#xff0c;会涉及到一些Python的特性&#xff0c;所以在这里介绍一下。 Python是一种高…

C语言中的内存分配方式(静态分配、动态分配)定义以及区别

在C语言中&#xff0c;内存分配主要有两种方式&#xff1a;静态分配&#xff08;Static Allocation&#xff09;和动态分配&#xff08;Dynamic Allocation&#xff09;。这两种方式在程序运行时对内存的管理和使用有着不同的特点和用途。 1. 静态分配&#xff08;Static Allo…

Unity2D游戏制作入门 | 13 ( 之人物三段攻击 )

上期链接&#xff1a;Unity2D游戏制作入门 | 12(之人物受伤和死亡的逻辑动画)-CSDN博客 上期我们聊了人物的受伤和死亡的逻辑和动画&#xff0c;我们主要学习了事件的执行&#xff0c;即我们在人物受伤时可能会触发很多的事件&#xff0c;比如触发人物受伤的动画以及播放音乐等…