Spring高手之路24——事务类型及传播行为实战指南

ops/2024/9/24 11:38:24/

文章目录

  • 1. 编程式事务(不推荐)
  • 2. 声明式事务(推荐)
  • 3. 事务的传播行为(复杂混合事务场景及时序图说明)
    • 3.1 NESTED和REQUIRES_NEW传播行为的区别

1. 编程式事务(不推荐)

定义:编程式事务是指通过显式的编程代码来管理事务的开始、提交和回滚。开发者需要手动控制事务的每个步骤。

优点

  • 更加灵活:开发者可以根据具体的业务逻辑细节对事务进行精细控制。
  • 适用于需要精细控制的事务逻辑:当事务行为需要根据特定条件进行复杂控制时,编程式事务更为合适。

缺点

  • 代码冗长:需要手动编写大量的事务管理代码,增加了代码复杂性。
  • 易出错:手动管理事务容易导致漏写提交或回滚的代码,增加了发生错误的风险。

示例(以Spring的编程式事务为例):

java">import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;public class TransactionService {private final PlatformTransactionManager transactionManager;public TransactionService(PlatformTransactionManager transactionManager) {this.transactionManager = transactionManager;}public void executeInTransaction() {TransactionDefinition def = new DefaultTransactionDefinition();TransactionStatus status = transactionManager.getTransaction(def);try {// 业务逻辑transactionManager.commit(status);} catch (Exception e) {transactionManager.rollback(status);throw e;}}
}

  这段代码展示了如何使用Spring进行编程式事务管理。executeInTransaction方法中,首先创建了一个事务定义和事务状态。然后,在try代码块中执行业务逻辑,并在成功时提交事务。如果发生异常,则回滚事务。通过这种方式,可以精细控制事务的开始、提交和回滚,适用于需要复杂事务控制的场景。

2. 声明式事务(推荐)

定义:声明式事务是通过配置或注解的方式来管理事务。开发者无需手动编写事务管理代码,事务的控制交由框架处理。

优点

  • 简洁明了:使用注解或配置文件即可实现事务管理,代码更加简洁。
  • 降低出错率:自动管理事务,减少手动管理带来的错误。

缺点

  • 灵活性较差:声明式事务在面对非常复杂的事务逻辑时,可能不够灵活,但可以通过结合使用不同的事务传播行为来部分解决这个问题。

示例(以Spring的声明式事务为例):

java">import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;@Service
public class TransactionService {@Transactionalpublic void executeInTransaction() {// 业务逻辑}
}

Spring中,通常推荐使用声明式事务,因为它更加简洁并且可以充分利用Spring框架的事务管理功能。在需要复杂事务控制时,可以考虑使用编程式事务

3. 事务的传播行为(复杂混合事务场景及时序图说明)

  • REQUIRED:如果当前没有事务,则创建一个新的事务;如果已经存在一个事务,则加入当前事务
  • REQUIRES_NEW:每次都创建一个新的事务,如果当前已经有一个事务,则挂起当前事务。挂起当前事务的意思是,当前事务的执行暂停,待新事务完成后再恢复执行。新事务提交后,主事务回滚不会影响这个新事务
  • NESTED:如果当前有事务,则在当前事务中嵌套一个事务,若外部事务回滚,则嵌套事务也会回滚;如果当前没有事务,则创建一个新的事务
  • MANDATORY:必须在一个现有事务中运行,如果当前没有事务,则抛出异常。
  • NEVER:不能在事务中运行,如果当前有事务,则抛出异常。
  • NOT_SUPPORTED:当前方法不支持事务,如果当前有事务,则将事务挂起。
  • SUPPORTS:如果当前有事务,则加入该事务;如果当前没有事务,也可以非事务方式运行。

  对于需要部分事务回滚的复杂场景,Spring中的声明式事务确实可以通过传播行为来实现一定的灵活控制。以下是一个详细的示例,展示如何使用常见的传播行为来实现部分事务回滚。

复杂混合事务场景示例:

假设有一个订单处理系统,包含以下步骤:

  1. 创建订单
  2. 扣减库存
  3. 发送确认邮件

我们希望在处理过程中:

  • 如果创建订单失败,整个事务回滚。
  • 如果扣减库存失败,只回滚扣减库存这部分,不影响订单创建。
  • 发送确认邮件可以在一个独立的事务中进行,即使失败也不影响前面的步骤。

代码如下:

java">import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;@Service
public class OrderService {@Autowiredprivate InventoryService inventoryService;@Autowiredprivate EmailService emailService;@Transactional(propagation = Propagation.REQUIRED)public void processOrder(Order order) {// Step 1: 创建订单(主事务createOrder(order);try {// Step 2: 扣减库存(嵌套事务inventoryService.deductInventory(order);} catch (Exception e) {// 仅回滚扣减库存这部分,不影响订单创建System.err.println("扣减库存失败: " + e.getMessage());}// Step 3: 发送确认邮件(新事务emailService.sendOrderConfirmation(order);}@Transactional(propagation = Propagation.REQUIRES_NEW)public void createOrder(Order order) {// 创建订单的逻辑// 如果失败,抛出异常,整个事务回滚if (order == null) {throw new RuntimeException("订单创建失败");}}
}

库存服务InventoryService

java">@Service
public class InventoryService {@Transactional(propagation = Propagation.NESTED)public void deductInventory(Order order) {// 扣减库存的逻辑// 如果失败,抛出异常,仅回滚此嵌套事务if (order.getItems().isEmpty()) {throw new RuntimeException("库存扣减失败");}}
}

邮件服务EmailService

java">@Service
public class EmailService {@Transactional(propagation = Propagation.REQUIRES_NEW)public void sendOrderConfirmation(Order order) {// 发送确认邮件的逻辑// 失败也不会影响主事务if (order.getEmail() == null) {throw new RuntimeException("邮件发送失败");}}
}

解释

  • 创建订单
    使用 REQUIRED 传播行为,作为主事务。如果失败,会抛出异常,导致整个事务回滚。

  • 扣减库存
    使用 NESTED 传播行为,在主事务内创建一个嵌套事务。如果扣减库存失败,只会回滚这个嵌套事务,不会影响到主事务

  • 发送确认邮件
    使用 REQUIRES_NEW 传播行为,总是创建一个新的事务。即使发送邮件失败,也不会影响前面的事务

通过结合使用事务不同的传播行为,可以在声明式事务中实现复杂的事务管理需求,如部分事务回滚。

整个事务处理过程时序图如下:

在这里插入图片描述

时序图解释

  1. 客户端调用 OrderServiceprocessOrder 方法开始主事务
  2. OrderService 中创建订单,使用 REQUIRED 传播行为。
  3. 如果订单创建成功,调用 InventoryService 扣减库存,使用 NESTED 传播行为。如果库存扣减失败,只回滚嵌套事务,不影响主事务
  4. 无论库存扣减是否成功,调用 EmailService 发送确认邮件,使用 REQUIRES_NEW 传播行为。即使邮件发送失败,也不影响主事务
  5. 如果订单创建失败,回滚主事务,并且所有嵌套事务也会回滚,但独立的新事务不会受到影响。

NESTEDREQUIRES_NEW_219">3.1 NESTED和REQUIRES_NEW传播行为的区别

有人可能疑惑了,这里NESTEDREQUIRES_NEW的传播行为看起来很像,这里详细对比一下。

  1. 事务关系:

REQUIRES_NEW

NESTED

  1. 回滚和提交行为

REQUIRES_NEW

  • 回滚行为:如果新事务失败,只回滚新事务,不影响外部事务
  • 提交行为:事务提交后,外部事务继续。即使外部事务回滚,新事务的提交也不会受到影响。

NESTED

  • 回滚行为:如果嵌套事务失败,只回滚嵌套事务,不影响外部事务
  • 提交行为:嵌套事务在外部事务提交时才真正提交。如果外部事务回滚,嵌套事务也会回滚。
  1. 使用场景

REQUIRES_NEW

  • 适用于需要独立事务的场景,例如记录日志或发送通知,这些操作应独立完成,不受主事务影响。

NESTED

  • 适用于部分独立处理但依赖于外部事务的场景,例如部分业务操作需要独立回滚,但整体上需要保持一致性。

欢迎一键三连~

有问题请留言,大家一起探讨学习

----------------------Talk is cheap, show me the code-----------------------

http://www.ppmy.cn/ops/115263.html

相关文章

科研小白入门工具

三、科研绘图 1.流程图绘制工具:powerpoint、亿图图示、visio、draw.io 2.绘制标准:布局合理、色彩鲜明、字体大小、矢量输出 矢量图绘制推荐流程:亿图图示绘制--visio--word--pdf无损放大 3.文章插图:excel、origin、matlab、…

【html】基础(一)

本专栏内容为:前端专栏 记录学习前端,分为若干个子专栏,html js css vue等 💓博主csdn个人主页:小小unicorn ⏩专栏分类:js专栏 🚚代码仓库:小小unicorn的代码仓库🚚 &am…

电源设计的艺术:从底层逻辑到工程实践

在电子工程的世界里,电源设计是核心中的核心。它不仅是电子设备的能量源泉,更是整个系统稳定运行的基石。随着科技的不断进步,电源设计的要求也越来越高,从效率、稳定性到体积、成本,每一个维度都是工程师们不断追求的…

如何在算家云搭建SDXL-Lightning(图像生成)

一、SDXL-Lightning简介 SDXL-Lightning 项目是由字节跳动开发的一个创新性的 AI 图像生成项目,该项目通过采用全新的蒸馏战略,优化了扩散模型,实现了从文字到高清图像的快速、高质量生成。以下是对 SDXL-Lightning 项目的详细介绍&#xff…

【第十六章:Sentosa_DSML社区版-机器学习之生存分析】

【第十六章:Sentosa_DSML社区版-机器学习之生存分析】 16.1 加速失效时间回归 1.算子介绍 加速失效时间回归模型Accelerated failure time (AFT)是一个监督型参数化的回归模型,它可以处理删失数据。它描述了一个生存时间的对数模型,所以它通…

nodejs之SSH

SSH(Secure Shell)是一个加密的网络协议,用于在不安全的网络上安全地执行远程命令和管理系统。SSH 最常见的用途是通过加密的连接远程登录计算机,但它也可以用于传输文件、隧道其他协议、以及执行其他网络服务。 而 node-ssh 是一…

JavaScript 网页设计案例详解( 最新技术趋势)

前言 随着 JavaScript 生态系统的不断发展和浏览器支持的不断完善,2024 年的前端开发技术已经变得更加现代化和高效。JavaScript 在网页设计中的应用不再局限于基础的交互,它与最新的 Web 标准、API 结合,为开发者带来了丰富的功能和出色的性…

多应用共享数据的两种方式 - Settings.Global.putXxx 和 ContentProvider

有需求: A 应用联网获取一时间戳,而B应用负责展示。为什么这么神奇,不直接在B应用中向服务端获取?别问,问就是业务导致。 既然存在这么个需求,解决数据存储以及数据获取问题就可以了。如题所说&#xff0c…