关于Spring事务管理之默认事务间调用问题

由事务的传播行为我们知道, 如果将方法配置为默认事务REQUIRED在执行过程中Spring会为其新启事务REQUIRES_NEW, 作为一个独立事务来执行. 由此存在一个问题。

如果使用不慎, 会引发org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it

具体原因见以下demo简例:部分关键代码DemoService1

public class DemoService1Impl implements DemoService1 {private final Logger logger = LoggerFactory.getLogger(this.getClass());@Resourceprivate DemoDao demoDao;@Resourceprivate DemoService2 demoService2;/*** 业务逻辑 , 默认事务, 事务回滚异常 : Exception*/@Override@Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)public void doService() {HashMap<String, Integer> param = new HashMap<>(2);param.put("applId", 19);param.put("code", 19);demoDao.insert1(param);try {demoService2.doService();} catch (Exception e) {logger.error("业务2处理异常,{}", e.getMessage());}}
}

DemoService2

public class DemoService2Impl implements DemoService2 {@Resourceprivate DemoDao demoDao;/*** 业务逻辑, 默认事务, 事务回滚异常 : Exception*/@Override@Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)public void doService() {HashMap<String, Integer> param = new HashMap<>(2);param.put("applId", 10);param.put("code", 10);demoDao.insert2(param);throw new RuntimeException("因为一些原因,我处理失败了.");}
}

单元测试

public class DemoService1ImplTest extends BaseTest {@Resourceprivate DemoService1 demoService1;@Testpublic void doService() {demoService1.doService();}
}

说明: 这里用到的事务配置为注解方式, 目前我们项目开发过程中使用配置文件方式, 一般为以下方式。 这种方式的事务配置, 更容易引起问题

<tx:advice id="txAdvice" transaction-
manager="transactionManager">
<tx:attributes>
...
<tx:method name="do*" />
<tx:method name="doNew*" propaga-
tion="REQUIRES_NEW" />
...
</tx:attributes>
</tx:advice>

执行结果

27:38 [DEBUG] -
[com.erayt.cms.cms.dao.DemoDao.insert1] prepare
sql:[ insert into ...
27:38 [DEBUG] -
[com.erayt.cms.cms.dao.DemoDao.insert1] prepare
parameters:[19, 19] ...
27:38 [DEBUG] - {pstm-100001} Executing State-
ment: insert into ...
27:38 [DEBUG] - {pstm-100001} Types: [java.lang.Integer, java.lang.Integer] ...
27:38 [DEBUG] - [com.erayt.cms.cms.dao.DemoDao.insert2] prepare sql:[ insert into ...
27:38 [DEBUG] - [com.erayt.cms.cms.dao.DemoDao.insert2] prepare parameters:[10, 10] ...
27:38 [DEBUG] - {conn-100002} Preparing Statement: insert into ...
27:38 [DEBUG] - {pstm-100003} Types: [java.lang.Integer, java.lang.Integer] ...
27:38 [ERROR] - 业务2处理异常,因为一些原因,我处理失败了.
27:38 [WARN ] - Caught exception while allowing TestExecutionListener ...
org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been
marked as rollback-only
at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit ...
at org.springframework.test.context.transaction.TransactionContext.endTransaction ...
at org.springframework.test.context.transaction.TransactionalTestExecutionListener.afterTestMethod ...
at org.springframework.test.context.TestContextManager.afterTestMethod ...

问题分析: 问题出现的代码为

try {demoService2.doService();
} catch (Exception e) {logger.error("业务2处理异常,{}", e.getMessage());
}

问题原因是因为两个service中的方法doService均为默认事务REQUIRED, 默认事务再被调用时, 如外层方法无事务, 自身会新启事务。

此时#demoService1.doService() 的事务则为新启事务REQUIRES_NEW), 之后再被调用的方法#demoService2.doService() 会加入到调用者 #demoService1.doService() 事务中。

又由于spring的事务回滚依托在异常之上, 当demoService2.doService()出现异常后它将事务标记为回滚。异常抛出后被catch

demoService1.doService没有接受到里面抛出的异常, 方法继续执行, 执行结束后, 事务提交。

但当demoService1在做commit的时候检测到事务被标记为回滚, 与预期不符, 也就是Unexpected意想不到的UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only

在这里插入图片描述

事务的传播定义

下面列举了各公司框架使用到的亊务传播部分说明,还有些不常用传播行为,因为实际使用的少,大家在网上了解下就行了。

传播行为意义
PROPAGATION_REQUIRED表示当前方法必须运行在一个事务中,如果当前存在一个事务,那么该方法运行在这个事务中,否则,将创建一个新的事务
PROPAGATION_REQUIRES_NEW新建事务,表示当前方法必须运行在自己的事务中,如果当前存在一个事务,那么这个事务将在该方法运行期间被挂起
PROPAGATION_NESTED表示如果当前事务存在,则方法应该运行在一个嵌套事务中。否则,它看起来和PROPAGATION_REQUIRED看起来没什么俩样

主子事务存在嵌套行为,嵌套是子事务套在父事务的一部分,在进入事务之前,父事务建立一个回滚点,叫save point,然后执行子亊务,这个子亊务的执行也算是父亊务的一部分,然后子亊务执行结束,父亊务继续执行。重点就在二那个save point。下面癿几个问题加深下大家的理解,对二嵌套亊务问题说明:
【1】如果子亊务回滚,会发生什么? 父亊务会回滚到进入子亊务前建立的save point,然后尝试其它的亊务或者其他的业务逻辑,父事务之前的操作不会受到影响,更不会自动回滚。
【2】如果父亊务回滚,会収生什么? 父亊务回滚,子亊务也会跟着回滚!为什么呢,因为父亊务结束之前,子亊务是不会提交的,我们说子亊务是父亊务的一部分,正是这个道理。
【3】亊务癿提交癿顺序什么? 父亊务先提交,然后子亊务提交,还是子亊务先提交,父亊务再提交?还是那句话,子亊务是父亊务的一部分,由父亊务统一提交。

数据库的隔离级别有哪几种?
【1】读未提交(Read Uncommitted): 最低级别的隔离级别,允许一个事务读取另一个事务尚未提交的数据。这种隔离级别可能导致脏读(Dirty Read)问题。
【2】读已提交(Read Committed): 在一个事务读取数据时,只能读取已经提交的数据。这种隔离级别可以避免脏读,但可能会导致不可重复读Non-Repeatable Read问题。
【3】可重复读(Repeatable Read): 在一个事务读取数据时,保证多次读取同一数据时,读取的结果保持一致。这种隔离级别可以避免脏读和不可重复读,但可能会导致幻读Phantom Read问题。
【4】串行化(Serializable): 最高级别的隔离级别,通过对事务进行串行化执行,避免了脏读、不可重复读和幻读的问题。但这种隔离级别可能会导致并发性能下降。
这些隔离级别的选择取决于应用程序的需求和对数据一致性的要求。不同的数据库管理系统可能对隔离级别的实现有所不同。

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

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

相关文章

用于割草机器人,商用服务型机器人的陀螺仪

介绍一款EPSON推出适用于割草机器人&#xff0c;商用服务型机器人的高精度陀螺仪模组GGPM61&#xff0c;具体型号为GGPM61-C01。模组GGPM61是一款基于QMEMS传感器的低成本航向角输出的传感器模组&#xff0c;它可以输出加速度、角速度及姿态角等信息&#xff0c;为控制机器人运…

Git 保姆级教程(一):Git 基础

一、获取 Git 仓库 通常有两种获取 Git 项目仓库的方式&#xff1a; 1. 将尚未进行版本控制的本地目录转换为 Git 仓库&#xff1b; 2. 从其它服务器克隆 一个已存在的 Git 仓库。 两种方式都会在你的本地机器上得到一个工作就绪的 Git 仓库。 1.1 git init&#xff08;本地…

【InternLM】Lagent智能体应用搭建

1. Lagent和AgentLego 1.1 Lagent Lagent 是一个开源的 LLM 智能体框架&#xff0c;允许使用者快速将一个大语言模型转换成智能体&#xff0c;并提供一些典型工具来激发大语言模型的潜能。Lagent 框架图如下&#xff1a; Lagent 包含三个主要模块&#xff1a;agents&#xf…

【数据结构】二叉树(定义、性质、存储、遍历、构造)解析+完整代码

文章目录 1.树的基本概念2.二叉树的概念2.1 二叉树定义和特性2.2 二叉树性质2.3 二叉树的存储2.3.1 顺序存储2.3.2 链式存储 3.二叉树的遍历3.1 先序遍历3.2 中序遍历3.3 后序遍历3.4 层序遍历3.5 由遍历序列构造二叉树A.前序中序遍历序列B.后序中序遍历序列C.层序中序遍历序列…

G2024-04-24 开源项目日报 Top10

根据Github Trendings的统计,今日(2024-04-24统计)共有10个项目上榜。根据开发语言中项目的数量,汇总情况如下: 开发语言项目数量Python项目5TypeScript项目3PowerShell项目1C++项目1非开发语言项目1LLaMA模型的C/C++移植 创建周期:306 天开发语言:C, C++协议类型:MIT Li…

【嵌入式】Arduino IDE + ESP32开发环境配置

一 背景说明 最近想捣鼓一下ESP32的集成芯片&#xff0c;比较了一下&#xff0c;选择Arduino IDE并添加ESP32支持库的方式来开发&#xff0c;下面记录一下安装过程以及安装过程中遇到的坑。 二 下载准备 【1】Arduino IDE ESP32支持一键安装包&#xff08;非常推荐&#xff0…

消息队列 Kafka 入门篇(二) -- 安装启动与可视化工具

一、Windows 10 环境安装 1、下载与解压 首先&#xff0c;访问Apache Kafka的官方下载地址&#xff1a; https://kafka.apache.org/downloads 在本教程中&#xff0c;我们将使用kafka_2.13-2.8.1版本作为示例。下载完成后&#xff0c;解压到您的工作目录的合适位置&#xff…

【动态规划】C++ 子序列问题(递增子序列、数对链、定差子序列、斐波那契子序列...)

文章目录 1. 前言2. 例题最长递增子序列 3. 算法题3.1_摆动序列3.2_最长递增子序列的个数3.3_最长数对链[3.4_ 最长定差子序列](https://leetcode.cn/problems/longest-arithmetic-subsequence-of-given-difference/description/)3.5_最长的斐波那契子序列的长度3.6_最长等差数…

jvm知识点总结(一)

JVM的跨平台 java程序一次编写到处运行。java文件编译生成字节码&#xff0c;jvm将字节码翻译成不同平台的机器码。 JVM的语言无关性 JVM只是识别字节码&#xff0c;和语言是解耦的&#xff0c;很多语言只要编译成字节码&#xff0c;符合规范&#xff0c;就能在JVM里运行&am…

小程序AI智能名片商城系统直连:打造用户与企业无缝对接的新时代!

在高度不确定性的商业环境中&#xff0c;企业如何快速响应市场变化&#xff0c;实现与用户的零距离接触&#xff1f;答案就是——小程序AI智能名片商城系统直连&#xff01;这一创新工具不仅为企业打开了与用户直接连接的大门&#xff0c;更为企业提供了持续收集用户反馈、快速…

基于Python+Selenium+Pytest的Dockerfile如何写

使用 Dockerfile 部署 Python 应用程序与 Selenium 测试 在本文中&#xff0c;我们将介绍如何使用 Dockerfile 部署一个 Python 应用程序&#xff0c;同时利用 Selenium 进行自动化测试。我们将使用官方的 Python 运行时作为父镜像&#xff0c;并在其中安装所需的依赖项和工具…

3 命名实体识别调优化

能走到这里说明你对模型微调有了一个基本的认识。那么开始一段命名实体的任务过程&#xff0c;下面使用huggingface官网的数据。 1 准备模型 下面的模型自己选择一个吧&#xff0c;我的内存太第一个模型跑不了。 https://huggingface.co/ckiplab/bert-base-chinese-ner/tree…