Spring Boot 事务失效问题详解:原因、场景与解决方案

在 Spring Boot 开发中,事务管理是保证数据一致性和完整性的核心机制。然而,许多开发者在使用 @Transactional 注解时,可能会遇到事务失效的问题,导致数据异常或业务逻辑错误。本文将深入分析 Spring Boot 中事务失效的常见原因,并结合实际场景给出解决方案,帮助大家更好地掌握事务的使用。

一、事务失效的常见场景

1.1 同类中方法直接调用导致事务失效

原因分析

Spring 的事务是通过 AOP 代理实现的,只有通过代理对象调用的方法,事务才会生效。如果在同一个类中,一个方法直接调用另一个带有 @Transactional 注解的方法(即 this.method() 方式),则事务不会生效。

示例代码

@Service
public class UserService {@Transactionalpublic void createUser(User user) {// 保存用户}public void createUserAndLog(User user) {this.createUser(user); // 事务失效log.info("用户创建成功");}
}

解决方案

  • 将事务方法提取到另一个类中,通过 Spring 注入调用。
  • 使用 AopContext.currentProxy() 获取当前代理对象调用方法。
  • 自我注入:将当前 Service 注入到自身,通过注入的对象调用方法。

推荐方式(自我注入)

@Service
public class UserService {@Autowiredprivate UserService self; // 自我注入@Transactionalpublic void createUser(User user) {// 保存用户}public void createUserAndLog(User user) {self.createUser(user); // 事务生效log.info("用户创建成功");}
}

1.2 异常未被正确捕获或抛出

原因分析

默认情况下,Spring 只对 RuntimeException 和 Error 进行回滚。如果捕获了异常但未抛出,或抛出了非运行时异常,事务不会回滚。

示例代码

@Transactional
public void updateUser(User user) {try {userRepository.save(user);} catch (Exception e) {log.error("更新失败", e);// 异常被吞掉,事务不会回滚}
}

解决方案

  • @Transactional 注解中指定回滚的异常类型。
  • 捕获异常后,重新抛出 RuntimeException。

推荐方式

@Transactional(rollbackFor = Exception.class)
public void updateUser(User user) {try {userRepository.save(user);} catch (Exception e) {log.error("更新失败", e);throw new RuntimeException("更新失败", e);}
}

1.3 事务传播行为配置不当

原因分析

在嵌套事务中,如果内层事务使用了 Propagation.REQUIRES_NEW,它会启动一个独立的新事务,外层事务的回滚不会影响内层事务,可能导致数据不一致。

示例代码

@Transactional(propagation = Propagation.REQUIRES_NEW)
public void innerMethod() {// 内层事务
}@Transactional
public void outerMethod() {innerMethod();throw new RuntimeException("外层异常");
}

问题

  • 外层事务回滚,但内层事务已提交,导致数据不一致。

解决方案

  • 根据业务需求选择合适的传播行为。
  • 如果希望内外事务一致,避免使用 REQUIRES_NEW,改用 REQUIRED

推荐方式

@Transactional(propagation = Propagation.REQUIRED)
public void innerMethod() {// 内层事务
}@Transactional
public void outerMethod() {innerMethod();throw new RuntimeException("外层异常");
}

1.4 数据库引擎不支持事务

原因分析

某些数据库引擎(如 MySQL 的 MyISAM)不支持事务,即使代码中配置了事务,也不会生效。

解决方案

  • 确保数据库使用支持事务的引擎,如 InnoDB

1.5 事务管理器未正确配置

原因分析

如果项目中没有正确配置事务管理器,@Transactional 注解不会生效。

解决方案

  • 确保在配置类中配置了事务管理器,例如:
@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource) {return new DataSourceTransactionManager(dataSource);
}

1.6 多数据源事务管理问题

原因分析

在多数据源场景下,如果没有为每个数据源配置独立的事务管理器,事务可能会失效。

解决方案

  • 为每个数据源配置独立的事务管理器。
  • 使用 @Transactional(value = "transactionManagerName") 指定事务管理器。

二、如何排查事务失效问题

2.1 启用事务日志

application.properties 中开启事务日志:

logging.level.org.springframework.transaction=DEBUG
logging.level.org.springframework.jdbc=DEBUG

2.2 检查代理对象

确保事务方法是通过 Spring 的代理对象调用的,而不是直接调用。

2.3 检查异常处理

确保异常被正确抛出,并符合事务回滚的条件。

2.4 检查数据库引擎

确保数据库引擎支持事务,例如使用 InnoDB。


三、事务传播行为(Propagation)的常用类型

传播行为类型说明
REQUIRED(默认)当前方法加入已有事务,若没有则创建新事务。
REQUIRES_NEW创建新事务,并挂起当前事务。
NOT_SUPPORTED不支持事务,挂起当前事务。
NEVER不允许事务,若当前有事务则抛出异常。
SUPPORTS当前方法可以在事务中执行,也可以不在事务中执行。
MANDATORY当前方法必须在事务中执行,若没有事务则抛出异常。

四、总结

Spring Boot 中的事务失效问题,通常是由于以下原因导致的:

  • 同类中方法直接调用
  • 异常未被正确抛出
  • 事务传播行为配置不当
  • 数据库引擎不支持事务
  • 事务管理器未正确配置
  • 多数据源事务管理问题

为了避免事务失效,建议遵循以下最佳实践:

  • 确保事务方法通过 Spring 代理调用
  • 正确处理异常,确保事务回滚
  • 合理配置事务传播行为
  • 使用支持事务的数据库引擎
  • 正确配置事务管理器

通过本文的分析和解决方案,相信大家对 Spring Boot 的事务管理有了更深入的理解。在实际开发中,合理使用事务,能够有效保证数据的一致性和完整性。

参考资料

  • Spring 官方文档 - 事务管理
  • CSDN - SpringBoot的事务失效

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

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

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

相关文章

Python-文件操作-StringIO和BytesIO-路径操作-shutil模块-csv,ini序列化和反序列化-argparse使用-学习笔记

序 欠4年前的一份学习笔记,献给今后的自己。 文件操作 冯诺依曼体系架构CPU由运算器和控制器组成 运算器,完成各种算数运算、逻辑运算、数据传输等数据加工处理 。 控制器,控制程序的执行 存储器,用于记忆程序和数据,例…

LLM的表征做减法的是什么,自然语言是一个矩阵,怎么进行减法的

LLM的表征做减法的是什么,自然语言是一个矩阵,怎么进行减法的 有个假设:就是最后一个词语融合了前面词语的信息 减法操作主要用于提取模型内部表征中的"诚实性"概念向量。具体来说,这是通过对比诚实和不诚实场景下的模型隐藏状态实现的。 import torch from t…

Java创建型模式---单例模式

单例模式基础概念单例模式是一种创建型设计模式,其核心思想是确保一个类仅有一个实例,并提供一个全局访问点来获取这个实例。在 Java 中实现单例模式主要有以下关键点:私有构造函数 - 防止外部通过new关键字创建实例静态实例变量 - 类内部持有…

详解Kafka重平衡机制详解

Kafka 的重平衡机制(Rebalance)是确保消费者组内成员动态变化(如新成员加入、现有成员退出或崩溃、订阅主题分区数变化)时,分区所有权能合理、公平地重新分配的核心机制。其目标是保证所有分区都有消费者处理&#xff…

代码详细注释:文件IO在用户管理系统中的应用实践:C语言实现用户名查重与密码确认与支持日志记录的终端用户认证解决方案的注册登录系统

代码/* 作业增强版注册登录系统 - 带日志和安全性增强功能 */ #include <stdio.h> // 标准输入输出函数(printf, scanf等) #include <stdlib.h> // 标准库函数(exit, malloc等) #include <string.h> // 字符串处理函数(strcmp, strcspn等) #inc…

Go与JS无缝协作:Goja引擎实战之错误处理最佳实践

引言&#xff1a;当Go邂逅JavaScript 在现代软件开发中&#xff0c;跨语言协作已成为提升效率的关键。想象一下&#xff1a;用Go的高性能处理核心逻辑&#xff0c;同时用JavaScript的灵活性实现动态规则——这不再是梦想。Goja&#xff0c;这个纯Go语言实现的JavaScript引擎&am…

继承与多态:面向对象编程的两大支柱

引言&#xff1a;为什么必须掌握继承与多态&#xff1f; 在Java开发中&#xff0c;继承与多态是构建可扩展、易维护系统的基石&#xff1a; 继承&#xff1a;实现代码复用&#xff0c;建立清晰的类层次结构多态&#xff1a;提升代码灵活性&#xff0c;实现"编写一次&#…

2025使用VM虚拟机安装配置Macos苹果系统下Flutter开发环境保姆级教程--上篇

前言 我们在学习Flutter开发的过程中&#xff0c;永远都跳不过去的一个问题就是如何在MAC下开发并打包Flutter工程项目&#xff0c;但MAC开发首先要解决的问题就是我们一般技术人员的电脑都是WINDOWS操作系统&#xff0c;专门配置一台MAC的话成本又是不得不考虑的因素&#xf…

250708-Svelte项目从Debian迁移到无法联网的RHEL全流程指南

&#x1f4cc; 背景 在 Debian 上使用以下命令创建了一个 Svelte 项目&#xff1a; npm install -g sv npx sv create my-svelte-demo cd my-svelte-demo npm install npm run dev现在需要将该项目迁移到一台 无法联网的 RHEL 9.4 服务器 上运行&#xff0c;出现如下报错&…

力扣 hot100 Day39

118. 杨辉三角 给定一个非负整数 numRows&#xff0c;生成「杨辉三角」的前 numRows 行。 class Solution { public:vector<vector<int>> generate(int numRows) {vector<vector<int>> res(numRows);for (int i 0; i < numRows; i) {res[i].resi…

HuggingFists: 无代码处理复杂PDF

有过使用LLM搭建RAG或其它类知识系统的朋友一定会对文档数据的复杂多样性有着深刻的理解。各行各业的磁盘中都沉睡了数年到数十年的各类文档信息&#xff0c;包括&#xff1a;Doc、Docx、PPT、PDF、XLS、PNG、JPEG等各类格式。利用LLM激活这些数据价值的首要工作就是能够正确的…

Vue 3 框架实现理念、架构与设计哲学深度解析

第一部分&#xff1a;Vue 3 的起源&#xff1a;架构演进与设计哲学 Vue 3 的诞生并非一次简单的版本迭代&#xff0c;而是一场深刻的架构革命。它的出现是前端技术演进、应用规模扩张以及对更高性能和可维护性追求的必然结果。要全面理解 Vue 3 的各项实现理念&#xff0c;必须…

SQL Server使用存储过程导出数据到Excel实现方式

在SQL Server数据库管理中,存储过程作为预编译的T-SQL语句集合,能显著提升数据操作效率与安全性。将数据导出到Excel的需求广泛存在于报表生成、数据迁移等场景。本文详细解析四种通过存储过程实现数据导出的技术方案,涵盖代码实现、适用场景及优化策略,为不同业务需求提供…

OpenGL 2. 着色器

#include <glad/glad.h> #include <GLFW/glfw3.h> #include <iostream> #include <stdexcept>// 函数声明 void framebuffer_size_callback(GLFWwindow* window, int width, int height); void processInput(GLFWwindow* window); void checkShaderCom…

【c++】容器扩容导致的类实例资源被错误释放

BUG记录 表现为新实例被存入前&#xff0c;容器内部的旧实例的析构被意外调用 因为 std::vector 在容量不足时&#xff0c;会自动扩容&#xff0c;把旧元素「搬」到新内存&#xff0c;然后析构旧内存上的那些对象。然后由于LKMotorController 类里没有正确处理移动语义&#xf…

TypeScript 集成

下面&#xff0c;我们来系统的梳理关于 Vue TypeScript 深度集成 的基本知识点&#xff1a;一、TypeScript 与 Vue 集成概述 1.1 为什么需要 TypeScript 类型安全&#xff1a;编译时类型检查&#xff0c;减少运行时错误代码智能&#xff1a;强大的IDE智能提示和自动补全可维护…

npm proxy

背景 前端项目下载依赖时经常会出现timeout的情况&#xff0c;此时有三种解决方案。 切换镜像源。 适用于对依赖版本要求不严格的情况。延长超时时间。设置npm proxy。一些生产环境对依赖版本有着严格要求&#xff0c;并且指定了依赖的下载地址&#xff08;如下图&#xff09;&…

TVS管工作原理是什么?主要的应用场景都有哪些?

什么是TVS管&#xff1f; TVS&#xff08;Transient Voltage Suppressors&#xff09;&#xff0c;即瞬态电压抑制器&#xff0c;也被称为雪崩击穿二极管&#xff0c;是一种二极管形式的高效能保护器件&#xff0c;常用来防止端口瞬间的电压冲击造成后级电路的损坏。 TVS 有单…

分布式微服务系统架构第156集:JavaPlus技术文档平台日更-Java线程池使用指南

title: java线程池使用 author: 哪吒 date: 2023-06-15点击勘误issues&#xff0c;哪吒感谢大家的阅读Java线程池使用指南1. 线程池基础使用1.1 创建线程池的方式方式一&#xff1a;使用Executors工具类&#xff08;不推荐&#xff09;// 1. 固定大小线程池 ExecutorService fi…

【最新版】点大全能版v2.6.7.1 含汇付斗拱插件+uniapp前端

一.介绍V2全能版本、独立版本全开源&#xff0c;含链动21&#xff0c;汇付斗拱​、排队免单、推三返1 &#xff0c;扶持金&#xff0c;平级奖&#xff0c;团队业绩奖&#xff0c;酒店管理&#xff0c;约车&#xff0c;餐饮等众多营销功能&#xff0c;商城系统版本号为2.6.7.1&a…