spring事务失效问题可以这样解决

news2024/12/28 2:57:16

今天咱们就来聊聊在Spring事务管理中你可能踩过的坑,并教你如何规避这些陷阱。

事务提交的陷阱

有时,事务方法在抛出异常后没有回滚,而是被提交了。

这通常是由于异常被捕获但没有显式抛出,导致Spring误以为事务正常完成。

错误示例
 

java

@Transactional
public void processTransaction() {
try {
// 执行一些数据库操作
userRepository.save(new User("John"));

// 模拟异常
if (true) {
throw new RuntimeException("模拟异常");
}
} catch (Exception e) {
// 捕获异常,但没有显式抛出,事务可能不会回滚
System.out.println("处理异常:" + e.getMessage());
}
}

图片

修正后的代码
 

java

@Transactional(rollbackFor = Exception.class) // 指定回滚的异常类型
public void processTransaction() throws Exception {
// 执行一些数据库操作
userRepository.save(new User("John"));

// 模拟异常
if (true) {
throw new Exception("强制回滚");
}
}

图片

事务不回滚

在事务方法中处理业务逻辑时,异常可能会被捕获和处理,导致事务没有正确回滚。

错误示例
 

java

@Transactional
public void processData() {
try {
// 进行数据库操作
userRepository.updateUser(new User("Jane"));

// 模拟一个业务异常
throw new CustomBusinessException("业务错误");
} catch (CustomBusinessException e) {
// 捕获异常,事务未回滚
System.out.println("捕获业务异常:" + e.getMessage());
}
}

问题分析

  • 在捕获异常后,事务没有被标记为回滚,因此 processData 方法中的数据库操作仍然会提交,导致数据状态不一致。

修正后的代码
 

java

@Transactional(rollbackFor = CustomBusinessException.class) // 指定业务异常时回滚
public void processData() throws CustomBusinessException {
// 进行数据库操作
userRepository.updateUser(new User("Jane"));

// 模拟一个业务异常
throw new CustomBusinessException("业务错误");
}

图片

死锁的困境与破解

死锁是数据库操作中常见的问题,尤其是在多个事务相互依赖资源时。

通常表现在两个或多个事务相互等待对方释放锁,结果大家都无法完成。

以下是一个可能发生死锁的简单示例:

 

java

@Transactional
public void updateUserBalances(Long user1Id, Long user2Id) {
User user1 = userRepository.findById(user1Id);
User user2 = userRepository.findById(user2Id);

user1.setBalance(user1.getBalance() - 100);
user2.setBalance(user2.getBalance() + 100);

userRepository.save(user1);
userRepository.save(user2);
}

图片

为了避免死锁,我们需要确保事务方法锁定资源的顺序一致,或者通过短小的事务方法减少锁持有时间。

此外,合理设计事务逻辑,避免循环依赖也很重要。

 

java

@Transactional
public void updateUserBalancesConsistently(Long user1Id, Long user2Id) {
User user1 = userRepository.findById(user1Id);
User user2 = userRepository.findById(user2Id);

// 按照固定的顺序处理用户更新,避免死锁
if (user1Id < user2Id) {
updateBalances(user1, user2);
} else {
updateBalances(user2, user1);
}
}

private void updateBalances(User user1, User user2) {
user1.setBalance(user1.getBalance() - 100);
user2.setBalance(user2.getBalance() + 100);

userRepository.save(user1);
userRepository.save(user2);
}

图片

事务提交后的回调陷阱

有时候,我们会在事务提交后触发一些回调事件,比如发送消息或者更新缓存。

但如果在这些回调中重新开启事务,可能会导致Spring的事务状态和数据库的事务状态不一致,进而引发意想不到的错误。

示例代码
 

java

@Transactional
public void processOrder() {
// 一些订单处理逻辑
transactionSynchronizationRegistry.registerSynchronization(new TransactionSynchronizationAdapter() {
@Override
public void afterCommit() {
// 回调事件中开启新事务
processNotification();
}
});
}

public void processNotification() {
// 这里我们可能再次使用事务来处理通知逻辑
// 但如果事务状态管理不当,可能导致问题
notificationService.sendNotification();
}

图片

修正后的代码

为了避免这种问题,可以使用 Propagation.REQUIRES_NEW 来确保在回调中开启的新事务与原事务分离,或在回调前清除事务状态。

 

java

@Transactional
public void processOrder() {
// 订单处理逻辑
transactionSynchronizationRegistry.registerSynchronization(new TransactionSynchronizationAdapter() {
@Override
public void afterCommit() {
// 使用新的事务传播行为
notificationService.sendNotificationInNewTransaction();
}
});
}

@Service
public class NotificationService {

@Transactional(propagation = Propagation.REQUIRES_NEW)
public void sendNotificationInNewTransaction() {
// 在新的事务中处理通知发送逻辑
sendNotification();
}

public void sendNotification() {
// 实际的通知发送逻辑
System.out.println("Notification sent.");
}
}

图片

事务状态管理工具类

在某些复杂的业务场景中,我们可能需要手动管理事务的状态。

此时,创建一个事务管理工具类可以帮助我们更加灵活地控制事务的开启、提交和回滚。

工具类实现

以下是一个简单的事务状态管理工具类的实现示例,在需要时手动管理事务的生命周期。

 

java

@Component
public class TransactionManager {

@Autowired
private PlatformTransactionManager transactionManager;

/**
* 在新事务中执行任务
* @param task 要执行的任务
*/
public void executeInNewTransaction(Runnable task) {
TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager);
// 设置事务传播行为为 PROPAGATION_REQUIRES_NEW
transactionTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
// 执行任务并在新事务中提交
transactionTemplate.execute(status -> {
task.run();
return null;
});
}
}

使用示例
 

java

@Service
public class OrderService {

@Autowired
private TransactionManager transactionManager;

public void placeOrder(Order order) {
// 在主事务中处理订单保存
saveOrder(order);

// 使用事务管理工具类在新事务中发送通知
transactionManager.executeInNewTransaction(() -> {
notificationService.sendOrderConfirmation(order);
});
}

@Transactional
public void saveOrder(Order order) {
// 保存订单逻辑
orderRepository.save(order);
}
}

图片

最后说一句(求关注!别白嫖!)

如果这篇文章对您有所帮助,或者有所启发的话,求一键三连:点赞、转发、在看。

关注公众号:woniuxgg,在公众号中回复:笔记  就可以获得蜗牛为你精心准备的java实战语雀笔记,回复面试、开发手册、有超赞的粉丝福利!

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

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

相关文章

Review Learning : 推进一体化超高清图像恢复训练方法

Review Learning: Advancing All-in-One Ultra-High-Definition Image Restoration Training Method 摘要 一体化图像恢复任务变得越来越重要&#xff0c;特别是对于超高清&#xff08;UHD&#xff09;图像。 现有的一体机UHD图像恢复方法通常通过引入针对不同退化类型的即时…

小区团购管理

TOC springboot254小区团购管理 第1章 绪论 1.1选题动因 当前的网络技术&#xff0c;软件技术等都具备成熟的理论基础&#xff0c;市场上也出现各种技术开发的软件&#xff0c;这些软件都被用于各个领域&#xff0c;包括生活和工作的领域。随着电脑和笔记本的广泛运用&…

一文搞懂后端面试之数据库综合应用【中间件 | 数据库 | MySQL | 高可用 | 高性能】

查询缓存 在MySQL里面&#xff0c;允许用户开启查询缓存。你可以理解这个缓存就是用SQL作为键&#xff0c;而对应的查询结果集就是值。如果下次过来的还是同一个查询&#xff0c;那么就直接返回缓存起来的查询结果集。 但是查询缓存不一定带来查询性能提升。如果你的查询每一…

接口隔离原则

接口隔离原则 接口隔离原则就是客户端不应该依赖它不需要的接口&#xff0c;或者说类间的依赖关系应该建立在最小的接口上。 我们以搜索美女为例&#xff0c;设计了如下的类图&#xff1a; 源代码如下。美女及其实现类&#xff1a; 搜索程序及其子类源代码如下&#xff1a; 最…

0101中文乱码-BufferedImage-图片处理

文章目录 1、问题描述2 、电子证书生成3、中文乱码原因及修复4、思考 结语 1、问题描述 接手维护一个休闲赛事类项目&#xff0c;因为服务器到期&#xff0c;项目从云服务器迁移到本地服务器。 项目生成比赛&#xff0c;分为二人组、三人组等等的团体&#xff1b;比赛设置几个…

判断矩阵中的一致性检验是指什么

判断矩阵中的一致性检验通常指的是在层次分析法&#xff08;Analytic Hierarchy Process, AHP&#xff09;中&#xff0c;用于评估决策者在构造判断矩阵时的逻辑一致性。 在AHP中&#xff0c;决策者需要比较成对的因素&#xff0c;并为每对因素的相对重要性赋予一个数值&#…

什么是haproxy七层代理

一.负载均衡 1.1.什么是负载均衡 负载均衡&#xff1a;Load Balance&#xff0c;简称LB&#xff0c;是一种服务或基于硬件设备等实现的高可用反向代理技术&#xff0c;负载均 衡将特定的业务(web服务、网络流量等)分担给指定的一个或多个后端特定的服务器或设备&#xff0c;从…

DriftingBlues2靶机渗透测试

DriftingBlues2靶机 文章目录 DriftingBlues2靶机信息收集FTP渗透web渗透权限提升靶机总结 信息收集 nmap扫描得到21,22和80端口&#xff0c;其中21ftp协议可以使用匿名用户登录 使用目录扫描一下网站&#xff0c;得到了blog目录 FTP渗透 匿名用户登录进去&#xff0c;发现…

QT移除窗体的最大化和最小化按钮

效果 代码位置 代码 int main(int argc, char *argv[]) {QApplication a(argc, argv);Widget w;w.setWindowFlags(w.windowFlags() & ~Qt::WindowMaximizeButtonHint & ~Qt::WindowMinimizeButtonHint);w.show();return a.exec(); }

考研数学想考120,把李林880做到准确率80%以上够吗?

考研数学想考120&#xff0c;把880题做到正确率80%以上是不够的 因为最近几年的考研数学变化&#xff0c;很大&#xff0c;传统的背题型的备考方式已经没用了&#xff0c;而880题是这种模式的佼佼者&#xff0c;25版的880变动又很小&#xff0c;只加了40道比较综合的题目在每一…

python基础命令学习

1.Python基础知识 目录 1.Python基础知识1.1 变量及类型1.2 标识符与关键字1.3 输出与输入1.3.1格式化符号1.3.2转义字符1.3.3结束符1.3.4输入的特点 1.4 运算符1.4.1 算数运算符1.4.2 赋值运算符1.4.3 比较(即关系)运算符1.4.4 逻辑运算符 1.5 数据类型转换1.6 判断与循环语句…

【K8S系列】Kubernetes基础介绍

一、前言 搭建完k8s集群后&#xff0c;正式进入k8s相关知识点的理论了解。并结合官方文档逐步总结涉及k8s各类知识点&#xff0c;希望能对正在学习的或将要学习得到小伙伴有所帮助。 二、系统部署历程回顾 传统部署时代&#xff1a; 早期&#xff0c;各个组织是在物理服务器…

[python]uiautomation.WindowControl函数用法

Python UIAutomation 窗口控件 介绍 在本文中&#xff0c;我们将探讨Python UIAutomation库以及如何使用它来控制和自动化Windows应用程序。我们将介绍UIAutomation的基础知识及其功能&#xff0c;并提供代码示例来演示其用法。 什么是UI自动化&#xff1f; UIAutomation是一个…

Java中常用的设计模式

一、什么是设计模式 设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。 毫无疑问,设计模式于己于他人于系统都是多赢的,设计模式使代码编制真正工程…

【机器学习】探索数据矿藏:Python中的AI大模型与数据挖掘创新实践

&#x1f496; 前言&#xff1a;探索数据矿藏1. &#x1f4ca;数据获取与预处理&#xff1a;AI大模型的燃料1.1 &#x1f310;数据获取&#xff1a;多样性与规模并重1.2 &#x1f9f9;数据清洗与处理&#xff1a;提升数据质量1.3 &#x1f50d;特征工程&#xff1a;挖掘数据的深…

稠密检索的规模艺术:模型、数据与性能的精准匹配

论文&#xff1a;https://arxiv.org/pdf/2403.18684代码&#xff1a;GitHub - jingtaozhan/DRScale机构&#xff1a;清华大学领域&#xff1a;稠密检索、Scaling Laws发表&#xff1a;SIGIR2024最佳论文 Abstract Scaling Laws已经在广泛的任务中被观察到&#xff0c;特别是在…

JavaScript基础——闭包

闭包简介 闭包的作用 闭包可以保留变量的状态 闭包可以让变量私有化 闭包的缺点 闭包简介 在JavaScript中&#xff0c;重复声明同一个变量会导致变量冲突&#xff0c;在这个时候可以使用闭包创建独立的执行环境。 在JavaScript中&#xff0c;闭包是指封闭的执行环境&#xff…

“论软件开发过程RUP及其应用”写作框架,软考高级,系统架构设计师

论文真题 RUP&#xff08;Rational Unified Process&#xff09;是IBM公司的一款软件开发过程产品&#xff0c;它提出了一整套以UML为基础的开发准则&#xff0c;用以指导软件开发人员以UML为基础进行软件开发。RUP汲取了各种面向对象分析与设计方法的精华&#xff0c;提供了一…

Wireshark显示过滤器常用关键字及过滤表达式

Wireshark显示过滤器常用关键字及过滤表达式 1. 过滤器类型 Wireshark抓包工具提供了两种类型过滤器&#xff1a;抓包过滤器 和 显示过滤器。 抓包过滤器&#xff1a; 抓取满足过滤条件的数据包&#xff0c;不满足过滤条件的数据包不会被抓取。 显示过滤器&#xff1a; 包已…

C语言基础⑩——构造类型(结构体)

一、数据类型分类 1、基本类型 整数型 短整型&#xff1a;short&#xff08;2个字节&#xff09;&#xff1b;整型&#xff08;默认&#xff09;&#xff1a;int&#xff08;4个字节&#xff09;&#xff1b;长整型&#xff1a;long&#xff08;8个字节&#xff09;&#xf…