记一次Spring事务失效的发现与解决过程

一、事情起因是这样的

        首先,我们是使用Spring + mybatis 进行开发。

        某功能在测试环境看到报错日志, 但是数据库里面的数据发生了变化,没有回滚。

        执行数据库update 操作的方法上明确有 

@Transactional(rollbackFor = Exception.class)的注解。 

        假设,异常日志之前执行的sql是

update t1 set c1= 10 where id = 10 //原来 c1 = 5

        方法抛出异常后, 数据库里面确实被修改成了10 。 说明事务没有回滚,或者事务没有生效。

二、验证是事务失效还是回滚失败

        1、本地启动服务, 在update 执行的结束的地方和异常抛出之前,打断点, 执行玩update。使用Navicat查看, 数据已经被修改。 说明事务没生效或者数据库隔离级别为读未提交。

        2、检查数据库隔离级别:可重复读

        在可重复度的隔离级别下, 事务之间是是无法看到相互之间没有提交的数据的。所以确定当前问题是,事务失效。@Transactional(rollbackFor = Exception.class) , 没有生效。

        下面就开始了查看@Transactional在什么情况下会失效。

三、Spring Transactional 失效的可能原因

1、、失效场景集一:代理不生效

(1)将注解标注在接口方法上

@Transactional是支持标注在方法与类上的。一旦标注在接口上,对应接口实现类的代理方式如果是CGLIB,将通过生成子类的方式生成目标类的代理,将无法解析到@Transactional,从而事务失效。

这种错误我们还是犯得比较少的,基本上我们都会将注解标注在接口的实现类方法上,官方也不推荐这种。

(2)被final、static关键字修饰的类或方法

CGLIB是通过生成目标类子类的方式生成代理类的,被final、static修饰后,无法继承父类与父类的方法。

(3)类方法内部调用

事务的管理是通过代理执行的方式生效的,如果是方法内部调用,将不会走代理逻辑,也就调用不到了。

2、失效场景集二:框架或底层不支持的功能

(1)非public修饰的方法

(2)多线程调用

(3)数据库本身不支持事务

比如Mysql的Myisam存储引擎是不支持事务的,只有innodb存储引擎才支持。

这个问题出现的概率极其小,因为Mysql5之后默认情况下是使用innodb存储引擎了。

但如果配置错误或者是历史项目,发现事务怎么配都不生效的时候,记得看看存储引擎本身是否支持事务。

(4)未开启事务

这个也是一个比较麻瓜的问题,在Springboot项目中已经不存在了,已经有DataSourceTransactionManagerAutoConfiguration默认开启了事务管理。

但是在MVC项目中还需要在applicationContext.xml文件中,手动配置事务相关参数。如果忘了配置,事务肯定是不会生效的。

3、失效场景集三:错误使用@Transactional

(1)错误的传播机制

(2)rollbackFor属性设置错误

默认情况下事务仅回滚运行时异常和Error,不回滚受检异常(例如IOException)。

因此如果方法中抛出了IO异常,默认情况下事务也会回滚失败。

我们可以通过指定@Transactional(rollbackFor = Exception.class)的方式进行全异常捕获。

(3)异常被内部catch

(4)嵌套事务

检查了以上问题, 在我们系统中不存在这些问题。 所以很苦恼。

四、查找事务失效的真正原因

首先捋一下思路:

在应用系统调用声明@Transactional 的目标方法时,Spring Framework 默认使用 AOP 代理,在代码运行时生成一个代理对象,根据@Transactional 的属性配置信息,这个代理对象决定该声明@Transactional 的目标方法是否由拦截器 TransactionInterceptor 来使用拦截,在 TransactionInterceptor 拦截时,会在在目标方法开始执行之前创建并加入事务,并执行目标方法的逻辑, 最后根据执行情况是否出现异常,利用抽象事务管理器AbstractPlatformTransactionManager 操作数据源 DataSource 提交或回滚事务.

事务管理的框架是由抽象事务管理器 AbstractPlatformTransactionManager 来提供的,而具体的底层事务处理实现,由 PlatformTransactionManager 的具体实现类来实现,如事务管理器 DataSourceTransactionManager。不同的事务管理器管理不同的数据资源 DataSource,比如 DataSourceTransactionManager 管理 JDBC 的 Connection。

1、进行一次Spring未生效事务管理的Debug

(1)、我们的测试代码

@Service
@Slf4j
public class TestSevice {@AutowiredXXXDao xxxDao;@Transactional(rollbackFor = Exception.class)public void updateAData() {System.out.println("updateAData 方法执行开始");//这里进行数据库操作xxxDao.updateOne(XXX.builder().id("0002C2B4-D5AA-4596-B592-510185E4B115").type("8888").build());//throw异常throw  new RuntimeException("error");System.out.println("updateAData 方法执行完成");}
}

 我们期待能够正常回滚!

(2)我们打开 

org.springframework.transaction.interceptor.
TransactionInterceptor

在如下位置打个断点

一步步跟下去会发现在 

org.springframework.transaction.interceptor.

TransactionAspectSupport

发现 

PlatformTransactionManager 是 KafkaTransactionManager ~ WTF!!

先往下走走看。

进入completeTransactionAfterThrowing, 我们会发现。 最终进入到AbstractPlatformTransactionManager.rollback()

最终我们会发现,调用了KafkaTransactionManager(实现了 AbstractPlatformTransactionManager)的rollback。并没有对mysql 数据源做任何的rollback。

2、为什么会是Kafka的事务管理器呢?

反过来想一下?为什么AbstractPlatformTransactionManager 的四个实现里面, 只有Kafka的注册进去了呢?

gpt 查了下这四种事务管理器。大概如下。

CciLocalTransactionManager
  • CciLocalTransactionManager 通常用于与企业信息系统 (EIS) 的交互,如数据库、消息队列等。
  • 它是针对 Common Client Interface (CCI) 标准的事务管理器,用于管理在单个资源管理器范围内的本地事务。
  • 主要用于与符合 CCI 标准的资源适配器一起使用,确保在事务上下文中适当地协调对这些资源的操作。
JtaTransactionManager
  • JtaTransactionManager 是用于管理分布式事务的事务管理器之一。
  • 它是在 Java EE 环境下使用的,通常与 JTA(Java Transaction API)一起使用。
  • JTA 是 Java 平台的事务管理 API,用于管理分布式事务,允许跨多个资源管理器的事务管理。
  • JtaTransactionManager 通过 JTA 实现分布式事务的管理,它协调不同资源管理器之间的事务操作,确保事务的一致性和隔离性。
DataSourceTransactionManager
  • DataSourceTransactionManager 通常用于与关系型数据库 (RDBMS) 的交互。
  • 它是 Spring 框架中用于管理数据库事务的事务管理器之一。
  • 主要用于管理与 DataSource 关联的事务,DataSource 是用于获取数据库连接的接口。
  • DataSourceTransactionManager 通过 JDBC 连接管理事务,它可以管理多个连接,并将它们与 Spring 中的事务进行关联。
KafkaTransactionManager
  • KafkaTransactionManager 是用于管理 Kafka 消息系统中事务的事务管理器。
  • 它是在 Spring Kafka 框架中使用的,用于管理 Kafka 生产者和消费者之间的事务。
  • KafkaTransactionManager 实现了 Kafka 的事务性 API,允许在生产者和消费者之间实现端到端的事务性保证。
  • 它管理 Kafka 生产者和消费者之间的事务性操作,以确保消息的可靠传递和处理。

结论是:CciLocalTransactionManager  JtaTransactionManager 我们并没有使用, 也没有配置。 所以不会被注册进去。

那么我们看下DataSourceTransactionManager 在没有Kafka 的情况下, 是怎么注入到SpringBean管理器的

看到了这个 @ConditionalOnMissingBean注解。 在(IOC容器内)不存在PlatformTransactionManager的实现类的时候,才加载并注入DataSourceTransactionManager(PlatformTransactionManager的实现)。所以。 在这里, Spring 容器先加载了KafkaTransactionManager。 就不会再加载DataSourceTransactionManager。

3、So!问题是由于数据源事务管理器没有注入。

下面我们尝试一下,手动写一个配置类,将DataSourceTransactionManager 注入到容器内。

@Configuration
public class DatasourceTransactionConfig {private final DataSource dataSource;private final TransactionManagerCustomizers transactionManagerCustomizers;DatasourceTransactionConfig(DataSource dataSource,ObjectProvider<TransactionManagerCustomizers> transactionManagerCustomizers) {this.dataSource = dataSource;this.transactionManagerCustomizers = transactionManagerCustomizers.getIfAvailable();}@Bean@Primary  //配置默认事务管理器public DataSourceTransactionManager transactionManager(DataSourceProperties properties) {DataSourceTransactionManager transactionManager = new DataSourceTransactionManager(this.dataSource);if (this.transactionManagerCustomizers != null) {this.transactionManagerCustomizers.customize(transactionManager);}return transactionManager;}}

我们尝试一下,事物是否生效?

生效!完美! 

但是Kafka的事务怎么办呢?

我们再次debug进去吧

这里的确是DatasourceTransactionManager了。Kafka的去哪里了呢?

我们跳到上图的上一步

final PlatformTransactionManager tm = determineTransactionManager(txAttr);

进入determineTransactionManager 发现, transactionManagerCache只有一个TransactionManager, 所以如果Kafka有事务需求, 一样会失效。

这可如何是好。

五、兼容Kafka和DataSource两个TransactionManager

可以尝试链式事务管理器。

        ChainedTransactionManager 是 Spring Data 项目中的一个事务管理器实现。可以向 ChainedTransactionManager 指定事务管理器列表,它将按照列表中事务管理器的顺序启动事务。在退出时(即方法退出时),事务将按照事务管理器列表的相反顺序提交。

        虽然这听起来是个合理的策略,但需要注意的一点是,ChainedTransactionManager 目前已被弃用,不推荐使用。弃用的原因见 大意是,人们通常希望 ChainedTransactionManager 是一颗神奇的银弹,可以解决所有事务问题,包括具有两阶段提交的分布式事务和其他问题,而这与事实相去甚远。

        ChainedTransactionManager 只能确保事务按特定顺序启动和提交。它不能保证任何事务同步,更不用说任何分布式事务协调了。假设你可以接受 ChainedTransactionManager 的限制,并希望按照我们的用例所要求的特定顺序执行事务。在这种情况下,使用该事务管理器是合理的,只要你记住你使用的是框架中一个已废弃的类即可。

public class TransactionConfig {private final DataSource dataSource;private final TransactionManagerCustomizers transactionManagerCustomizers;TransactionConfig(DataSource dataSource,ObjectProvider<TransactionManagerCustomizers> transactionManagerCustomizers) {this.dataSource = dataSource;this.transactionManagerCustomizers = transactionManagerCustomizers.getIfAvailable();}@Bean@Primary  //配置默认事务管理器public DataSourceTransactionManager transactionManager(DataSourceProperties properties) {DataSourceTransactionManager transactionManager = new DataSourceTransactionManager(this.dataSource);if (this.transactionManagerCustomizers != null) {this.transactionManagerCustomizers.customize(transactionManager);}return transactionManager;}@Beanpublic ChainedKafkaTransactionManager chainedKafkaTransactionManager(DataSourceTransactionManager transactionManager,KafkaTransactionManager<?, ?> kafkaTransactionManager){return new ChainedKafkaTransactionManager<>(transactionManager, kafkaTransactionManager);}
}

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

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

相关文章

蓝桥杯单片机快速开发笔记——HC573/HC138

一、原理分析 二、思维导图 三、代码参考 #include "HC573.h" #include "reg52.h"void Set_HC573(unsigned char channel, unsigned char dat) {P2 (P2 & 0x1f) | 0x00; //赋值之前&#xff0c;关闭全部锁存器P0 dat; //保存待设置…

阿里云和腾讯云2核4G服务器租用价格对比,选哪个好?

租用2核4G服务器费用多少&#xff1f;2核4G云服务器多少钱一年&#xff1f;1个月费用多少&#xff1f;阿里云2核4G服务器30元3个月、轻量应用服务器2核4G4M带宽165元一年、企业用户2核4G5M带宽199元一年&#xff1b;腾讯云轻量2核4G服务器5M带宽165元一年、252元15个月、540元三…

长期护理保险可改善老年人心理健康 | CHARLS CLHLS CFPS 公共数据库周报(3.6)...

欢迎报名2024年“真实世界临床研究”课程&#xff01; 本周郑老师开讲&#xff1a;“真实世界临床研究”培训班&#xff0c;3月16-17日两天&#xff0c;欢迎报名&#xff01; CHARLS公共数据库‍ CHARLS数据库简介中国健康与养老追踪调查(China Health and Retirement Longitud…

多模态特征融合新突破!5大方法刷新顶会SOTA!

多模态融合可以应用于情感分析、身份验证、行为识别等多个领域&#xff0c;实现更精确的预测和判断。这其中&#xff0c;特征融合是多模态数据处理的核心环节&#xff0c;它不仅能够提高模型的性能&#xff0c;还能够增强数据的表达力&#xff0c;让模型能够更好地理解和处理现…

案例分析篇05:数据库设计相关28个考点(9~16)(2024年软考高级系统架构设计师冲刺知识点总结系列文章)

专栏系列文章推荐: 2024高级系统架构设计师备考资料(高频考点&真题&经验)https://blog.csdn.net/seeker1994/category_12593400.html 【历年案例分析真题考点汇总】与【专栏文章案例分析高频考点目录】(2024年软考高级系统架构设计师冲刺知识点总结-案例分析篇-…

白嫖AWS云服务器,验证、注册指南

背景 不知道你想不想拥有一台属于自己的云服务器呢&#xff0c;拥有一台自己的云服务器可以建站&#xff0c;可以在上面搭建个人博客&#xff0c;今天我就来教大家如何申请亚马逊 AWS 免费云服务器&#xff0c;这个云服务器可以长达12个月的免费。而且到期后可以继续换个账号继…

测试人员业务提升的几个方面

&#x1f4cb; 个人简介 作者简介&#xff1a;大家好&#xff0c;我是凝小飞&#xff0c;软件测试领域作者支持我&#xff1a;点赞&#x1f44d;收藏⭐️留言&#x1f4dd; 整个文章来源于我的xmind梳理和总结&#xff0c;接下来为各位一步步拆解。 一、测试流程遵守和推进 在…

离线安装数据库 mysql 5.7 linux

离线安装数据库 mysql 5.7 linux 方法一 参考链接Linux(Debian10.2)安装MySQL5.7.24环境 赋予文件执行权限chmod x 文件名 使用root用户sudo su解压文件tar xvf mysql-5.7.42-linux-glibc2.12-x86_64.tar.gz重命名mv mysql-5.7.42-linux-glibc2.12-x86_64 mysql将桌面的mys…

使用 URLDecoder 和 URLEncoder 对中文字符进行编码和解码

请直接看原文: 使用 URLDecoder 和 URLEncoder 对中文字符进行编码和解码_urldecoder.decode-CSDN博客 ------------------------------------------------------------------------------------------------------------------------------- 摘要&#xff1a; URLDecoder 和…

plotnine,一个非常实用的 Python 库!

大家好&#xff0c;今天为大家分享一个非常实用的 Python 库 - plotnine。 Github地址&#xff1a;https://github.com/has2k1/plotnine 在数据分析和可视化领域&#xff0c;Python 提供了许多强大的工具和库。其中&#xff0c;plotnine 是一个基于 Grammar of Graphics 理论的…

短剧在线搜索源码

一个非常哇塞的在线短剧搜索页面&#xff0c;接口已经对接好了&#xff0c;上传源码到服务器解压就能直接用&#xff0c;有能力的可以自己改接口自己写自己的接口 接口文档地址&#xff1a;doc.djcat.sbs 源码免费下载地址抄笔记

YOLOv8_pose-Openvino和ONNXRuntime推理【CPU】

纯检测系列&#xff1a; YOLOv5-Openvino和ONNXRuntime推理【CPU】 YOLOv6-Openvino和ONNXRuntime推理【CPU】 YOLOv8-Openvino和ONNXRuntime推理【CPU】 YOLOv7-Openvino和ONNXRuntime推理【CPU】 YOLOv9-Openvino和ONNXRuntime推理【CPU】 跟踪系列&#xff1a; YOLOv5/6/7-O…

rtsp流实现web端实时播放(海康+大华)

最近的电力项目需要嵌入海康摄像头画面&#xff0c;之前没有做过类似的流媒体播放&#xff0c;所以有些懵&#xff1b; 海康开放平台的webAPI&#xff0c;有插件还是无插件&#xff0c;都不适合自研web系统的嵌入&#xff0c;所以需要自己进行解流&#xff1b; 首先&#xff0c…

企业即时通讯工具,企业内部即时通讯系统推荐

企业需要私有化即时通讯系统主要出于以下几个方面的考虑&#xff0c; 数据安全和隐私保护&#xff1a;在企业日常办公中&#xff0c;涉及到大量的敏感信息和机密数据&#xff0c;这些信息如果通过公共即时通讯工具传输&#xff0c;存在被窃取或泄露的风险。通过私有化即时通讯…

如何将Git拉取项目后,将SSH验证方式修改为HTTPS?

首先在打开项目所在位置的Git BashGUI 查找当前的远程仓库URL&#xff1a; 打开终端或命令提示符&#xff0c;导航到你的项目目录&#xff0c;并使用以下命令查看当前配置的远程仓库URL&#xff1a; git remote -v这会显示如下格式的输出&#xff1a; origin gitgithub.com:用…

Linux中GPU相关命令

Linux查看显卡信息&#xff1a; lspci | grep -i vga 使用nvidia GPU可以&#xff1a; lspci | grep -i nvidia1 前边的序号 "00:0f.0"是显卡的代号(这里是用的虚拟机); 查看指定显卡的详细信息用以下指令&#xff1a; lspci -v -s 00:0f.01 Linux查看Nvidia显…

RedisTemplate实现锁超时时间延长(模仿Redisson看门狗机制)

业务场景&#xff1a; 在上一篇-Java业务功能并发问题处理的最后&#xff0c;我们用RedisTemplate实现了一个分布式锁&#xff0c;但是后面又有用户反馈同个单据出现了重复操作&#xff0c;让我们回忆下上次的加锁代码&#xff1a; 问题描述&#xff1a; 原因出现在我们锁住…

【开源工程】超经典实景三维数字孪生矿山~智慧矿山解决方案

飞渡科技数字孪生煤矿管理平台&#xff0c;以数字孪生技术为底座&#xff0c;融合图像识别、电磁感应、5G下井等技术&#xff0c;实现矿山环境、采煤装备、移动巡检等生产数据的全面采集&#xff0c;实时感知生产过程与关键装备的运行数据和状态&#xff0c;逐步推进矿山全流程…

Python爬虫从基础到入门:script标签中的数据

上一篇文章: Python爬虫从基础到入门:script标签中的数据 1. 分析需要抓取的数据的在哪?2. 获取数据、解析数据3. 下载视频、音频文件4. 参考代码1. 分析需要抓取的数据的在哪? 本篇博文以B站视频为例,B站视频在用户没有登录的状态下,只能观看视频尺寸为360流畅,在登录…

深度剖析Kafka中Coordinator的奥秘

欢迎来到我的博客&#xff0c;代码的世界里&#xff0c;每一行都是一个故事 深度剖析Kafka中Coordinator的奥秘 前言什么是Coordinator&#xff1f;Group Coordinator&#xff08;群组协调器&#xff09;&#xff1a;Transaction Coordinator&#xff08;事务协调器&#xff09…