事务传播行为详解

一、事务传播行为的基本概念

事务传播行为是Spring 框架中事务管理的核心概念,用于定义当一个事务方法被另一个事务方法调用时,事务应如何传播。通俗地说,它解决了 “多个事务方法嵌套调用时,新方法是加入现有事务还是创建新事务” 的问题。

事务传播行为详解

在 Spring 中,事务传播行为通过@Transactional注解的propagation属性设置,或在TransactionDefinition接口中定义。

二、Spring 事务传播行为的类型及详解

Spring 定义了 7 种事务传播行为,以下是详细说明:

传播行为类型英文名称核心定义典型应用场景

PROPAGATION_REQUIRED

(propagation_required)

REQUIRED(默认)若当前存在事务,则加入该事务;若不存在,则创建新事务核心业务逻辑(如订单创建、支付流程),确保操作在统一事务中。

PROPAGATION_REQUIRES_NEW

(propagation_requires_new)

REQUIRES_NEW无论当前是否存在事务,都创建新事务,原事务会被挂起。异步任务、日志记录(不希望主事务失败影响子任务),或需要独立回滚的操作。

PROPAGATION_NESTED

(propagation_nested)

NESTED若当前存在事务,则创建嵌套事务(通过数据库 Savepoint 实现);若不存在,则创建新事务。MySQL 等支持 Savepoint 的数据库中,需要部分回滚的场景(如订单部分退款)。

PROPAGATION_SUPPORTS

(propagation_supports)

SUPPORTS若当前存在事务,则加入事务;若不存在,则以非事务方式执行。只读查询方法,可复用外层事务,也可独立执行。

PROPAGATION_NOT_SUPPORTED

(propagation_not_supported)

NOT_SUPPORTED以非事务方式执行,若当前存在事务,则挂起该事务。明确不需要事务的操作(如缓存更新),避免事务开销。

PROPAGATION_NEVER

(propagation_never)

NEVER强制以非事务方式执行,若当前存在事务,则抛出异常。确保方法绝对不运行在事务中(如只读缓存服务)。

PROPAGATION_MANDATORY

(propagation_mandatory)

MANDATORY强制要求当前存在事务,否则抛出异常。子方法必须依赖外层事务(如财务系统中的分账操作)。

三、(@Transactional注解的propagation属性)的生效条件触发时机

Spring事务传播行为(@Transactional注解的propagation属性)的生效条件和触发时机需要结合以下几点来理解:

1. 注解何时起作用?

Spring事务传播行为的注解(如@Transactional(propagation = Propagation.REQUIRED))会在以下场景中生效:

  • 方法被调用时:当一个事务方法(带有@Transactional注解)被另一个方法调用时,事务传播行为会根据当前事务上下文动态决定如何处理事务。
  • 代理机制触发:Spring通过动态代理(JDK动态代理或CGLIB代理)管理事务。只有当方法是通过代理对象调用时,事务传播行为才会生效。

2.事务传播行为的触发流程

事务传播行为的触发流程可以简化为以下步骤:

方法调用时检查事务上下文:Spring会检查当前是否存在事务(例如,调用方是否已经开启事务)。
根据传播行为决定事务处理方式:

  • 新建事务(如REQUIRES_NEW):挂起当前事务(如果有),开启新事务。
  • 加入事务(如REQUIRED):直接使用当前事务。
  • 强制要求/禁止事务(如MANDATORY/NEVER):根据规则抛出异常或挂起事务。

执行方法逻辑:在确定事务上下文后,执行方法体内的业务逻辑。
事务提交或回滚:根据方法执行结果和传播行为规则提交或回滚事务。

3.示例代码

REQUIRED 传播行为的核心原则 “如果存在事务则加入,不存在则创建新事务” 是针对每个被调用的方法而言的。具体逻辑如下:

⑴. 核心判断时机

当一个被 @Transactional(propagation = Propagation.REQUIRED) 注解的方法被调用时,Spring 会检查当前调用环境是否存在活跃事务:

  • 如果存在事务:方法会加入该事务,共享同一事务上下文。
  • 如果不存在事务:方法会创建一个新事务。

⑵. 示例说明

场景一:调用链中已有事务

@Transactional(propagation = Propagation.REQUIRED)
public void methodA() {// 事务已创建methodB(); // 调用 methodB
}@Transactional(propagation = Propagation.REQUIRED)
public void methodB() {// methodB 加入 methodA 的事务
}
  • 分析methodB 被调用时,由于调用链中已存在 methodA 创建的事务,methodB 直接加入该事务,两者共享同一事务边界。

场景二:调用链中无事务

public void methodA() {// 无事务methodB(); // 调用 methodB
}@Transactional(propagation = Propagation.REQUIRED)
public void methodB() {// methodB 创建新事务
}
  • 分析methodB 被调用时,由于调用链中不存在事务,methodB 会创建一个新事务。

⑶. 关键点

  • 与方法层级无关:无论方法是被直接调用还是嵌套在多层调用链中,判断逻辑始终是当前调用环境是否存在事务
  • 事务上下文由 Spring 维护Spring 通过 TransactionSynchronizationManager 管理事务上下文,确保同一线程中共享事务状态。
  • 自调用问题:若方法在同一个类中被自调用(如 this.methodB()),@Transactional 注解会失效,因为 Spring 的 AOP 代理机制只拦截外部调用。

"在同一事务中" 的含义


四、核心传播行为的典型场景与示例

1.propagation_required(默认行为)

这是最常用的传播行为。如果当前存在事务,则加入该事务;如果不存在,则创建一个新事务。

应用场景:核心业务逻辑,确保多个操作在同一事务中。

示例:创建订单并扣减库存

@Service
public class OrderService {@Autowiredprivate OrderRepository orderRepository;@Autowiredprivate InventoryService inventoryService;@Transactional(propagation = Propagation.REQUIRED)public Order createOrder(Order order) {// 保存订单Order savedOrder = orderRepository.save(order);// 扣减库存 (假设该方法也使用 REQUIRED 传播行为)inventoryService.reduceStock(order.getProductId(), order.getQuantity());return savedOrder;}
}

在这个例子中,如果 createOrder 方法被一个事务调用,reduceStock 方法会加入这个事务;如果没有事务,这两个操作会在一个新事务中执行。

2.propagation_requires_new

无论当前是否存在事务,都创建一个新事务,原事务会被挂起。

应用场景:异步任务、日志记录,不希望主事务失败影响子任务。

示例:订单支付与日志记录

@Service
public class PaymentService {@Autowiredprivate LogService logService;@Transactional(propagation = Propagation.REQUIRED)public void processPayment(Payment payment) {// 处理支付try {// 支付逻辑...// 记录支付日志 (使用 REQUIRES_NEW 确保即使主事务回滚,日志也会记录)logService.recordPaymentLog(payment);// 可能抛出异常的操作if (payment.getAmount() > 10000) {throw new PaymentException("金额过大需要审核");}} catch (Exception e) {// 异常处理}}
}@Service
public class LogService {@Transactional(propagation = Propagation.REQUIRES_NEW)public void recordPaymentLog(Payment payment) {// 记录日志逻辑}
}

在这个例子中,即使 processPayment 方法因异常回滚,recordPaymentLog 方法创建的日志记录也会被持久化,因为它在独立的事务中执行。

3.propagation_nested

如果当前存在事务,则创建一个嵌套事务(通过数据库 Savepoint 实现);如果不存在,则创建一个新事务。

应用场景:需要部分回滚的场景,如订单部分退款。

示例:订单部分退款

@Service
public class RefundService {@Autowiredprivate OrderRepository orderRepository;@Autowiredprivate PaymentRepository paymentRepository;@Transactional(propagation = Propagation.REQUIRED)public void processPartialRefund(Long orderId, BigDecimal refundAmount) {Order order = orderRepository.findById(orderId).orElseThrow();// 创建嵌套事务处理退款try {refundPayment(order.getPaymentId(), refundAmount);// 更新订单状态order.setStatus(OrderStatus.PARTIALLY_REFUNDED);orderRepository.save(order);} catch (RefundException e) {// 退款失败,但订单状态更新不会受影响// 嵌套事务的回滚不会影响外层事务order.setStatus(OrderStatus.REFUND_FAILED);orderRepository.save(order);}}@Transactional(propagation = Propagation.NESTED)public void refundPayment(Long paymentId, BigDecimal amount) {// 退款逻辑if (amount > 1000) {throw new RefundException("退款金额超过限制");}// 执行退款...}
}

在这个例子中,如果 refundPayment 方法抛出异常,只会回滚嵌套事务中的操作,而外层事务中的订单状态更新仍然会提交。

4.propagation_supports

如果当前存在事务,则加入事务;如果不存在,则以非事务方式执行。

应用场景:只读查询方法,可以复用外层事务,也可以独立执行。

示例:查询订单详情

@Service
public class OrderQueryService {@Autowiredprivate OrderRepository orderRepository;@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)public Order getOrderDetails(Long orderId) {// 查询订单详情return orderRepository.findById(orderId).orElseThrow();}
}

在这个例子中,如果 getOrderDetails 方法在事务中被调用,它会加入该事务;如果不在事务中,它会以非事务方式执行。这对于只读操作很有用,可以减少事务开销。

5.propagation_not_supported

以非事务方式执行,如果当前存在事务,则挂起该事务。

应用场景:明确不需要事务的操作,如缓存更新。

示例:更新缓存

@Service
public class CacheService {@Transactional(propagation = Propagation.NOT_SUPPORTED)public void updateCache(String key, Object value) {// 更新缓存逻辑// 这里不需要事务,因为缓存操作通常不需要回滚}
}

在这个例子中,如果 updateCache 方法在事务中被调用,当前事务会被挂起,方法执行完毕后再恢复。

6.propagation_never

强制以非事务方式执行,如果当前存在事务,则抛出异常。

应用场景:确保方法绝对不运行在事务中,如只读缓存服务。

示例:从缓存获取数据

@Service
public class CacheQueryService {@Transactional(propagation = Propagation.NEVER)public Object getFromCache(String key) {// 从缓存获取数据// 确保此方法不会在事务中执行return cache.get(key);}
}

在这个例子中,如果 getFromCache 方法在事务中被调用,会抛出异常,确保缓存操作不会在事务上下文中执行。

7.propagation_mandatory

强制要求当前存在事务,否则抛出异常。

应用场景:子方法必须依赖外层事务,如财务系统中的分账操作。

示例:财务分账

@Service
public class AccountingService {@Transactional(propagation = Propagation.MANDATORY)public void distributeFunds(Payment payment) {// 分账逻辑// 必须在事务中执行,确保数据一致性}
}@Service
public class PaymentProcessingService {@Autowiredprivate AccountingService accountingService;@Transactional(propagation = Propagation.REQUIRED)public void processPayment(Payment payment) {// 处理支付// ...// 分账 (依赖外层事务)accountingService.distributeFunds(payment);}
}

在这个例子中,distributeFunds 方法必须在事务中调用,否则会抛出异常。这确保了分账操作不会在没有事务保护的情况下执行。

五、事务传播行为的底层实现原理

  1. 数据库事务与 Spring 事务的映射

    • Spring 通过PlatformTransactionManager抽象层管理事务,不同数据库(如 MySQL、Oracle)的事务机制会影响传播行为的实现。
    • 例如:PROPAGATION_NESTED依赖数据库的SAVEPOINT机制,MySQL InnoDB 引擎支持,而 MyISAM 不支持。
  2. 事务挂起与恢复

    • 当使用REQUIRES_NEWNESTED时,Spring 会将当前事务状态(如连接、隔离级别)保存到TransactionStatus对象中,新事务完成后恢复原事务。

六、实战最佳实践与注意事项

  1. 优先使用默认传播行为(REQUIRED)

    • 核心业务逻辑通常需要事务一致性,默认值可避免遗漏配置。
  2. REQUIRES_NEW 的性能开销

    • 新事务会导致数据库连接切换和事务日志开销,非必要场景避免滥用。
  3. NESTED 的数据库兼容性

    • 若系统需跨数据库部署,谨慎使用NESTED,可考虑用REQUIRES_NEW替代。
  4. 事务边界控制

    • 避免在循环中调用REQUIRES_NEW方法(如批量插入),可通过@Transactional包裹整个循环以减少事务开销。
  5. 异常处理与事务回滚

    • 传播行为会影响异常传播,例如REQUIRES_NEW的子事务异常不会影响外层事务,需显式处理异常或配置rollbackFor

七、总结

事务传播行为是 Spring 事务管理的核心机制,合理选择传播行为可解决以下问题:

  • 多个服务方法间的事务边界划分
  • 核心业务与辅助操作的事务隔离
  • 复杂业务流程中的部分回滚需求

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

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

相关文章

Java八股文——Spring「SpringMVC 篇」

MVC分层介绍一下 面试官您好,MVC是一种非常经典、影响深远的软件设计模式,它的全称是Model-View-Controller。在我看来,它的核心目标就是解决早期Web开发中,业务逻辑、数据和界面显示高度耦合的问题,从而实现“各司其…

FreeSWITCH mod_curl 和 mod_xml_rpc 测试

编辑 /usr/local/freeswitch/conf/autoload_configs/xml_rpc.conf.xml <configuration name"xml_rpc.conf" description"XML RPC"> <settings> <param name"http-port" value"8889"/> <param name&quo…

实时监控、秒级决策:镜舟科技如何重塑融资融券业务数据处理模式

融资融券业务作为证券市场的重要组成部分&#xff0c;已成为金融机构核心业务增长点和利润来源。截至 2023 年底&#xff0c;我国融资融券余额已突破 1.8 万亿元&#xff0c;业务量呈现爆发式增长。然而&#xff0c;在业务高速发展的同时&#xff0c;金融机构面临着数据处理效率…

Linux与量子计算:面向未来的架构演进

Linux与量子计算&#xff1a;面向未来的架构演进 当经典计算遇上量子革命 引言&#xff1a;量子计算时代的黎明 量子计算正从理论走向工程实践&#xff0c;Linux作为现代计算的基石&#xff0c;正在量子革命中扮演关键角色。据IBM预测&#xff0c;到2027年&#xff0c;量子优势…

Java中wait()为何必须同步调用?

在 Java 中&#xff0c;wait() 方法必须在 synchronized 方法或代码块中调用&#xff0c;主要原因如下&#xff1a; 1. 监视器锁&#xff08;Monitor&#xff09;机制 依赖对象锁&#xff1a;wait() 方法需要操作对象的监视器锁&#xff08;Monitor&#xff09;&#xff0c;调…

前端面试专栏-基础篇:4. 页面渲染流程与性能优化

页面渲染流程与性能优化详解&#xff08;完整版&#xff09; 一、现代浏览器渲染流程&#xff08;详细说明&#xff09; 1. 构建DOM树 浏览器接收到HTML文档后&#xff0c;会逐步解析并构建DOM&#xff08;Document Object Model&#xff09;树。具体过程如下&#xff1a; (…

涨薪技术|Docker端口映射与容器互联技术

前面的推文我们学了Docker操作的常用命令,今天开始给大家分享Docker端口映射与容器互联,欢迎关注。Docker不管是程序员,架构师或者测试工程师都必须要掌握的一门主流技术。 Docker除了通过网络访问外,还提供了两个很方便的功能来满足服务访问的基本需求,一个是允许映射容…

Roboguide工作站机器人重新安装软件包

1、点击菜单栏“机器人-属性”&#xff1b; 2、点击“重新生成”&#xff1b; 3、点击“确定”&#xff1b; 4、点击“6&#xff1a;机器人选项” 5、在搜索框搜索软件包&#xff0c;或在软件包列表选择&#xff0c;勾选软件包后点击“下一步”&#xff1b; 6、点击“完成”&am…

预训练CNN网络的迁移学习(MATLAB例)

从基于大型数据集训练的神经网络中提取层&#xff0c;并基于新数据集进行微调。本例使用ImageNet中的子集进行微调。 This example retrains a SqueezeNet neural network using transfer learning. This network has been trained on over a million images, and can classif…

kali系统 windows Linux靶机入侵演练

Kali系统与Windows/Linux靶机入侵演练简介 演练概述 Kali Linux是一款专为渗透测试和网络安全评估设计的操作系统,常被安全专业人员用于合法的安全测试。入侵演练是网络安全训练的重要组成部分,旨在帮助安全人员了解攻击手法并提升防御能力。 基本组件 1. **攻击机**:通常…

手搓transformer

思路是这样子的&#xff1a;从手搓代码的角度去学习transformer&#xff0c;代码会一个一个模块地从头到尾添加&#xff0c;以便学习者跟着敲&#xff0c;到最后再手搓一个基于tansformer的机器翻译实战项目。 transformer整体架构 一、输入部分 词向量 import torch import t…

网络层协议:IP

目录 1、概念 2、关键组成部分 2.1 IP地址 2.1.1 概念 2.1.2 主要版本 2.1.3 IP地址分类 2.2 IP数据报&#xff08;IP协议传输的基本数据单元&#xff09; 3、工作原理 3.1 路由 3.2 分片与重组 4、相关协议 1、概念 目的&#xff1a;负责在复杂的网络环境中将数据…

Fastadmin报错Unknown column ‘xxx.deletetime‘ in ‘where clause

报错原因 在开启软删除后&#xff0c;设置了表别名&#xff0c;软删除字段依旧使用原表名。 解决方法 原代码 $list $this->model->with([admin, product])->where($where)->order($sort, $order)->paginate($limit);foreach ($list as $row) {$row->ge…

TCN+Transformer+SE注意力机制多分类模型 + SHAP特征重要性分析,pytorch框架

效果一览 TCNTransformerSE注意力机制多分类模型 SHAP特征重要性分析 TCN&#xff08;时序卷积网络&#xff09;的原理与应用 1. 核心机制 因果卷积&#xff1a;确保时刻 t t t 的输出仅依赖 t − 1 t-1 t−1 及之前的数据&#xff0c;避免未来信息泄露&#xff0c;严格保…

Elasticsearch的数据同步

elasticsearch中的数据多是来自数据库&#xff0c;当数据库发生改变时&#xff0c;elasticsearch也必须跟着改变&#xff0c;这个就叫做数据同步。 当我们是进行微服务的时候&#xff0c;同时两个服务不能进行相互调用的时候。就会需要进行数据同步。 方法一&#xff1a;同步…

uniapp 时钟

<template><view class"clock-view"><view class"clock-container u-m-b-66"><!-- 表盘背景 --><view class"clock-face"></view><!-- 时针 --><view class"hand hour-hand" :style&quo…

【大模型】实践之1:macOS一键部署本地大模型

Ollama + Open WebUI 自动部署脚本解析说明文档 先看下效果 一、脚本内容 #!/bin/bash set -eMODEL_NAME="qwen:1.8b" LOG_FILE="ollama_run.log" WEBUI_PORT=3000 WEBUI_CONTAINER_PORT=8080 WEBUI_URL="http://localhost:$WEBUI_PORT" DOC…

相机Camera日志实例分析之三:相机Camx【视频光斑人像录制】单帧流程日志详解

【关注我&#xff0c;后续持续新增专题博文&#xff0c;谢谢&#xff01;&#xff01;&#xff01;】 上一篇我们讲了&#xff1a; 这一篇我们开始讲&#xff1a; 目录 一、场景操作步骤 二、日志基础关键字分级如下 三、场景日志如下&#xff1a; 一、场景操作步骤 操作步…

介绍一下 TCP方式程序的通讯,服务器机与客户机

TCP通信方式&#xff1a;服务器与客户机通信详解 TCP(传输控制协议)是一种面向连接的、可靠的、基于字节流的传输层通信协议。下面我将详细介绍TCP方式下服务器与客户机的通信过程。 基本概念 TCP特点&#xff1a; 面向连接&#xff1a;通信前需建立连接可靠传输&#xff1a;…

Ubuntu系统复制(U盘-电脑硬盘)

所需环境 电脑自带硬盘&#xff1a;1块 (1T) U盘1&#xff1a;Ubuntu系统引导盘&#xff08;用于“U盘2”复制到“电脑自带硬盘”&#xff09; U盘2&#xff1a;Ubuntu系统盘&#xff08;1T&#xff0c;用于被复制&#xff09; &#xff01;&#xff01;&#xff01;建议“电脑…