【Spring】Spring事务和事务传播机制

 🔥个人主页: 中草药

🔥专栏:【Java】登神长阶 史诗般的Java成神之路


一、Spring事务

我们在MySQL阶段已经学习了MySQL的事务相关知识,详情可见

【MySQL数据库】索引与事务-CSDN博客

1、概念 

我们在此做一个简单回顾 

事务(Transaction)是指一组操作的集合,这些操作作为一个整体,要么全部成功,要么全部失败,具有以下四大特性(ACID):

  1. 原子性(Atomicity):事务中的操作要么全部成功,要么全部失败,不可分割。
  2. 一致性(Consistency):事务执行前后,数据处于一致状态。
  3. 隔离性(Isolation):事务相互独立执行,不受其他事务的干扰。
  4. 持久性(Durability):事务完成后,数据的更改会被永久保存。

在分布式环境或复杂业务逻辑中,事务可以有效防止数据异常,提高系统的可靠性。

2、事务的实现

1、编程式事务

使用DataSourceTransactionManager和TransactionDefinition去进行事务的管理

DataSourceTransactionManager 事务管理器. ⽤来获取事务(开启事务), 提交或回滚事务,
TransactionDefinition 是事务的属性, 在获取事务的时候需要将 TransactionDefinition 传递进去从而获得⼀个事务 TransactionStatus
package org.example.controller;import org.example.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionManager;
import org.springframework.transaction.TransactionStatus;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RequestMapping("/user")
@RestController
public class UserController {@Autowiredprivate UserService userService;@Autowiredprivate DataSourceTransactionManager transactionManager;@Autowiredprivate TransactionDefinition transactionDefinition;@RequestMapping("/registry")public String registry(String name, String password) {//⽤⼾注册TransactionStatus status = transactionManager.getTransaction(transactionDefinition);userService.registryUser(name, password);//提交事务//transactionManager.commit(status);//回滚事务transactionManager.rollback(status);return "注册成功";}
}

2、声明式事务(推荐):

需要先在Maven中添加依赖

<dependency><groupId>org.springframework</groupId><artifactId>spring-tx</artifactId>
</dependency>
  • 通过 @Transactional 注解实现事务管理,配置简单,非侵入式。
  • 配合 AOP 切面拦截方法调用,统一控制事务逻辑。
@Transactional
public void someTransactionalMethod() {// 业务逻辑
}
        在需要事务的⽅法上添加 @Transactional 注解就可以实现了. ⽆需⼿动开启事务和提交事
务, 进⼊⽅法时⾃动开启事务, ⽅法执⾏完会⾃动提交事务, 如果中途发⽣了没有处理的异常会⾃动
回滚事务.

测试代码

package org.example.controller;import lombok.extern.slf4j.Slf4j;
import org.example.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import java.io.IOException;@RestController
@Slf4j
@RequestMapping("/trans")
public class TransactionalController {@Autowiredprivate UserService userService;/*** 不加事务的情况下, 数据插入成功* @param name* @param password* @return*/
//    @Transactional@RequestMapping("/r1")public String registry(String name,String password){//用户注册userService.registryUser(name,password);log.info("数据插入成功");int a = 10/0;return "注册成功";}/*** 添加事务的情况, 事务回滚*/@Transactional@RequestMapping("/r2")public String r2(String name,String password){//用户注册userService.registryUser(name,password);log.info("数据插入成功");int a = 10/0;return "注册成功";}/*** 异常被捕获: 事务提交*/@Transactional@RequestMapping("/r3")public String r3(String name,String password){//用户注册userService.registryUser(name,password);log.info("数据插入成功");try {int a = 10/0;}catch (Exception e){log.error("发生异常");}return "注册成功";}/*** 异常被捕获并且throw: 事务回滚*/@Transactional@RequestMapping("/r4")public String r4(String name,String password){//用户注册userService.registryUser(name,password);log.info("数据插入成功");try {int a = 10/0;}catch (Exception e){log.error("发生异常");throw e;}return "注册成功";}/*** 手动回滚事务*/@Transactional@RequestMapping("/r5")public String r5(String name,String password){//用户注册userService.registryUser(name,password);log.info("数据插入成功");try {int a = 10/0;}catch (Exception e){log.error("发生异常");TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();}return "注册成功";}/*** 发生非运行时异常, 事务提交*/@Transactional@RequestMapping("/r6")public String r6(String name,String password) throws IOException {//用户注册userService.registryUser(name,password);log.info("数据插入成功");if (true){throw new IOException();}return "注册成功";}/*** 设置rollbackFor, 发生非运行时异常, 事务回滚*/@Transactional(rollbackFor = Exception.class)@RequestMapping("/r7")public String r7(String name,String password) throws IOException {//用户注册userService.registryUser(name,password);log.info("数据插入成功");if (true){throw new IOException();}return "注册成功";}/*** 设置事务的隔离级别**/@Transactional(isolation = Isolation.DEFAULT)@RequestMapping("/r8")public String r8(String name,String password) throws IOException {//用户注册userService.registryUser(name,password);log.info("数据插入成功");if (true){throw new IOException();}return "注册成功";}
}
@Transactional 默认只在遇到运行时异常和Error时才会回滚, ⾮运行时异常不回滚. 即
Exception的⼦类中, 除了RuntimeException及其⼦类

3、Spring事务的隔离级别

事务隔离级别描述了并发事务之间数据可见性的限制程度,直接影响事务操作的正确性和系统性能。隔离级别越高,数据一致性越强,但性能越低。SQL 标准定义了以下四种隔离级别:

隔离级别问题性能说明
读未提交(Read Uncommitted)脏读、不可重复读、幻读事务可以读取其他事务未提交的数据,数据一致性最低。
读已提交(Read Committed)不可重复读、幻读较高事务只能读取其他事务已经提交的数据,避免了脏读,但仍存在不可重复读和幻读问题。
可重复读(Repeatable Read)幻读中等事务在读取数据时,保证同一事务内多次读取结果一致,避免了脏读和不可重复读,但可能出现幻读。
串行化(Serializable)无问题最高隔离级别,事务顺序执行,完全避免脏读、不可重复读和幻读,但性能较差,可能导致大量锁冲突。

 Spring中的事务隔离级别有5种:

1、Isolation.DEFAULT : 以连接的数据库的事务隔离级别为主.

2、Isolation.READ_UNCOMMITTED : 读未提交, 对应SQL标准中 READ UNCOMMITTED

3、Isolation.READ_COMMITTED : 读已提交,对应SQL标准中 READ COMMITTED

4、Isolation.REPEATABLE_READ : 可重复读, 对应SQL标准中 REPEATABLE READ

5、Isolation.SERIALIZABLE : 串⾏化, 对应SQL标准中 SERIALIZABLE

Spring 中事务隔离级别可以通过 @Transactional 中的 isolation 属性进行设置
    @Transactional(isolation = Isolation.READ_COMMITTED)@RequestMapping("/r3")public String r3(String name,String password){//用户注册userService.registryUser(name,password);log.info("数据插入成功");try {int a = 10/0;}catch (Exception e){log.error("发生异常");}return "注册成功";}

二、事务传播机制

1、概念

        事务传播机制定义了在一个事务方法被另一个事务方法调用时,事务如何在这些方法之间传播。例如,当一个事务方法A调用另一个事务方法B时,B方法是加入A方法的现有事务,还是开启一个新的事务,或者以其他方式处理事务,这就是由事务传播机制决定的。

Spring 定义了七种事务传播行为,通过@Transactional注解的propagation属性来指定。

事务隔离级别解决的是多个事务同时调用同一个数据库的问题

事务传播机制解决的是同一个事务在多个节点(方法)中传递的问题

 2、传播行为类型

@Transaction 注解支持事务传播机制的设置,通过propagation 属性来制定传播机制的设置,事务的传播机制有以下7种:

1、Propagation.REQUIRED : 默认的事务传播级别. 如果当前存在事务, 则加⼊该事务. 如果当前没 有事务, 则创建⼀个新的事务.

2、Propagation.SUPPORTS : 如果当前存在事务, 则加入该事务. 如果当前没有事务, 则以⾮事务的方式继续运行.

3、Propagation.MANDATORY :强制性. 如果当前存在事务, 则加⼊该事务. 如果当前没有事务,抛出异常.

4、Propagation.REQUIRES_NEW : 创建⼀个新的事务. 如果当前存在事务, 则把当前事务挂起. 也就是说不管外部⽅法是否开启事务, Propagation.REQUIRES_NEW 修饰的内部⽅法都会新开启⾃⼰的事务, 且开启的事务相互独⽴, 互不干扰。

5、Propagation.NOT_SUPPORTED : 以⾮事务⽅式运行, 如果当前存在事务, 则把当前事务挂起(不⽤).

6、 Propagation.NEVER 以非事务的方式运行,如果当前存在事务,则抛出异常。
7、Propagation.NESTED : 如果当前存在事务, 则创建⼀个事务作为当前事务的嵌套事务来运⾏.
如果当前没有事务, 则该取值等价于 PROPAGATION_REQUIRED
举例

REQUIRED(默认)
如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。这是最常用的事务传播行为。例如:

@Service
@Transactional
public class OuterService {@Autowiredprivate InnerService innerService;public void outerMethod() {// 执行一些业务操作//...innerService.innerMethod();// 继续执行其他业务操作//...}
}@Service
@Transactional(propagation = Propagation.REQUIRED)
public class InnerService {public void innerMethod() {// 执行内部业务操作//...}
}

在上述示例中,当outerMethod方法被调用时,如果当前存在事务(例如,在一个更大的业务流程中已经开启了事务),则innerMethod方法将加入该事务;如果outerMethod方法是在没有事务的环境下被调用,则会为outerMethodinnerMethod创建一个新的事务。

REQUIRES_NEW
总是创建一个新的事务。如果当前存在事务,则将当前事务挂起,新事务执行完毕后再恢复原事务。例如:

@Service
@Transactional
public class OuterService {@Autowiredprivate InnerService innerService;public void outerMethod() {// 执行一些业务操作//...try {innerService.innerMethod();} catch (Exception e) {// 处理innerMethod方法抛出的异常,但不影响outerMethod方法所在的事务//...}// 继续执行其他业务操作//...}
}@Service
@Transactional(propagation = Propagation.REQUIRES_NEW)
public class InnerService {public void innerMethod() {// 执行内部业务操作//...if (true) {throw new RuntimeException("Inner method error");}}
}

在这个例子中,innerMethod方法总是会开启一个新的事务,即使outerMethod方法已经在一个事务中。如果innerMethod方法抛出异常并回滚,outerMethod方法所在的事务不会受到影响,因为innerMethod的事务是独立的。

SUPPORTS
如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务方式执行。例如:

@Service
@Transactional
public class OuterService {@Autowiredprivate InnerService innerService;public void outerMethod() {// 执行一些业务操作//...innerService.innerMethod();// 继续执行其他业务操作//...}
}@Service
@Transactional(propagation = Propagation.SUPPORTS)
public class InnerService {public void innerMethod() {// 执行内部业务操作//...}
}

outerMethod在事务中调用innerMethod时,innerMethod会加入outerMethod的事务;如果outerMethod没有在事务中调用innerMethod,则innerMethod会以非事务方式执行。

NOT_SUPPORTED
总是以非事务方式执行,如果当前存在事务,则将当前事务挂起。例如:

@Service
@Transactional
public class OuterService {@Autowiredprivate InnerService innerService;public void outerMethod() {// 执行一些业务操作//...innerService.innerMethod();// 继续执行其他业务操作//...}
}@Service
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public class InnerService {public void innerMethod() {// 执行内部业务操作//...}
}

在这种情况下,innerMethod永远不会在事务中执行,即使outerMethod处于事务环境中。

MANDATORY
如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。例如:

@Service
@Transactional
public class OuterService {@Autowiredprivate InnerService innerService;public void outerMethod() {// 执行一些业务操作//...innerService.innerMethod();// 继续执行其他业务操作//...}
}@Service
@Transactional(propagation = Propagation.MANDATORY)
public class InnerService {public void innerMethod() {// 执行内部业务操作//...}
}

如果outerMethod没有在事务中调用innerMethod,将会抛出IllegalTransactionStateException异常,因为innerMethod要求必须在事务环境中执行。

NEVER
总是以非事务方式执行,如果当前存在事务,则抛出异常。例如:

@Service
@Transactional
public class OuterService {@Autowiredprivate InnerService innerService;public void outerMethod() {// 执行一些业务操作//...innerService.innerMethod();// 继续执行其他业务操作//...}
}@Service
@Transactional(propagation = Propagation.NEVER)
public class InnerService {public void innerMethod() {// 执行内部业务操作//...}
}

如果outerMethod在事务中调用innerMethod,将会抛出IllegalTransactionStateException异常。

NESTED
如果当前存在事务,则在嵌套事务内执行;如果当前没有事务,则创建一个新的事务。嵌套事务是外部事务的一个子事务,它有自己的保存点。如果子事务回滚,只会回滚到保存点,而不会影响外部事务;如果外部事务回滚,则子事务也会回滚。例如:

@Service
@Transactional
public class OuterService {@Autowiredprivate InnerService innerService;public void outerMethod() {// 执行一些业务操作//...try {innerService.innerMethod();} catch (Exception e) {// 处理innerMethod方法抛出的异常,但不影响outerMethod方法所在的事务//...}// 继续执行其他业务操作//...}
}@Service
@Transactional(propagation = Propagation.NESTED)
public class InnerService {public void innerMethod() {// 执行内部业务操作//...if (true) {throw new RuntimeException("Inner method error");}}
}

在上述示例中,如果innerMethod抛出异常并回滚,只会回滚innerMethod中的操作,outerMethod中的其他操作不受影响;如果outerMethod整体回滚,则innerMethod的操作也会回滚。


        Spring 事务管理及其事务传播机制为企业级应用开发提供了强大而灵活的事务处理能力。通过合理地配置事务和选择合适的事务传播行为,可以有效地保证数据的一致性、完整性和隔离性,同时满足复杂业务逻辑和分布式系统的需求。在实际开发中,我们需要深入理解事务传播机制的原理和应用场景,根据业务需求进行准确的配置,以构建健壮、可靠的应用程序。


梦想家命长,实干家寿短。——约.奥赖利

🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀

以上,就是本期的全部内容啦,若有错误疏忽希望各位大佬及时指出💐

  制作不易,希望能对各位提供微小的帮助,可否留下你免费的赞呢🌸

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

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

相关文章

Qt 小项目 学生管理信息系统

主要是对数据库的增删查改的操作 登录/注册界面&#xff1a; 主页面&#xff1a; 添加信息&#xff1a; 删除信息&#xff1a; 删除第一行&#xff08;支持多行删除&#xff09; 需求分析&#xff1a; 用QT实现一个学生管理信息系统&#xff0c;数据库为MySQL 要求&#xf…

核心网S6730-H48X6C-V2堆叠

核心网是电信网络的中枢,负责数据传输、服务提供和网络管理,对保障通信质量、支持新技术服务和维护网络安全至关重要。堆叠技术通过将多个网络设备逻辑上整合为一个单元,简化管理,提升网络可用性和性能,同时降低成本,增强网络扩展性。 堆叠在网络建设中至关重要,它通过…

教程: 5分钟部署 APIPark 开源 LLM Gateway 与 API 开放门户

极大简化了大语言模型调用的过程&#xff0c;无需复杂代码即可同时连接主流大语言模型&#xff0c;让企业更加快捷、安全地使用AI。喜欢或感兴趣的小伙伴们赶紧去体验吧&#xff01; &#x1f517;更详细使用教程可以查看&#xff1a;APIPark 产品使用文档 APIPark 提供出色的…

HTML5教程-表格宽度设置,最大宽度,自动宽度

HTML表格宽度 参考&#xff1a;html table width HTML表格是网页设计中常用的元素之一&#xff0c;可以用来展示数据、创建布局等。表格的宽度是一个重要的参数&#xff0c;可以通过不同的方式来设置表格的宽度&#xff0c;本文将详细介绍HTML表格宽度的不同设置方式和示例代…

RISC-V架构下OP-TEE 安全系统实践

安全之安全(security)博客目录导读 本篇博客&#xff0c;我们聚焦RISC-V 2024中国峰会上的RISC-V和OP-TEE结合的一个安全系统实践&#xff0c;来自芯来科技桂兵老师。 关于RISC-V TEE(可信执行环境)的相关方案&#xff0c;如感兴趣可参考RISC-V TEE(可信执行环境)方案初探 首…

RTK数据的采集方法

采集RTK&#xff08;实时动态定位&#xff09;数据通常涉及使用高精度的GNSS&#xff08;全球导航卫星系统&#xff09;接收器&#xff0c;并通过基站和流动站的配合来实现。本文给出RTK数据采集的基本步骤 文章目录 准备设备设置基站设置流动站数据采集数据存储与处理应用数据…

【银河麒麟操作系统真实案例分享】内存黑洞导致服务器卡死分析全过程

了解更多银河麒麟操作系统全新产品&#xff0c;请点击访问 麒麟软件产品专区&#xff1a;https://product.kylinos.cn 开发者专区&#xff1a;https://developer.kylinos.cn 文档中心&#xff1a;https://documentkylinos.cn 现象描述 机房显示器连接服务器后黑屏&#xff…

Mongodb副本集环境安全认证

我所配置的mongodb副本集群 step1启动 MongoDB 副本集的每一个节点 mongod --config=/usr/local/mongodb_wjx/wjx01/mongod.conf mongod --config=/usr/local/mongodb_wjx/wjx02/mongod.conf mongod --config=/usr/local/mongodb_wjx/wjx03/mongod.conf step2通过主节点添加管…

完美解决Qt Qml窗口全屏软键盘遮挡不显示

1、前提 说明&#xff1a;我使用的是第三方软键盘 QVirtualKeyboard QVirtualKeyboard: Qt5虚拟键盘支持中英文,仿qt官方的virtualkeyboard模块,但使用QWidget实现。 - Gitee.com 由于参考了几篇文章尝试但没有效果&#xff0c;链接如下&#xff1a; 文章一&#xff1a;可能…

在做题中学习(77):快排

解法&#xff1a;快排 思路&#xff1a; 1.快排排一趟&#xff0c;递归分出来的左区间和右区间&#xff08;一趟的思想&#xff0c;看我的前一个文章&#xff1a;颜色分类题解&#xff09; 2.递归&#xff1a;想清楚 函数头 和 返回条件怎么写 3.优化&#xff1a;等概率的取…

AUTO TECH China 2025 华南展:探索汽车技术的新纪元

AUTO TECH China 2025 华南展&#xff1a;探索汽车技术的新纪元 随着科技的日新月异&#xff0c;汽车行业正经历着前所未有的变革。从电动化、智能化到网联化&#xff0c;每一项新技术的应用都在重塑我们对汽车的认知。为了展示这些令人激动的创新成果&#xff0c;我们荣幸地宣…

C# RSA加密和解密,RSA生成私钥和公钥

C# RSA加密和解密&#xff0c;RSA生成私钥和公钥&#xff08;使用XML格式秘钥&#xff09; 目录 前言生成xml格式的公钥和私钥 PrivateKeyPublicKey测试加密、解密 方案1&#xff1a;RSA公钥加密&#xff0c;RSA私钥解密方案2&#xff1a;RSA私钥加密&#xff0c;RSA私钥解密…

指标加权评价方法

文章目录 层次分析法&#xff08;Analytic Hierarchy Process, AHP&#xff09;熵权法原理计算方法 Technique for Order Preference by Similarity to Ideal Solution(TOPSIS, 优劣解距离法)原理计算方法 层次分析法&#xff08;Analytic Hierarchy Process, AHP&#xff09; …

React第十七章(useRef)

useRef 当你在React中需要处理DOM元素或需要在组件渲染之间保持持久性数据时&#xff0c;便可以使用useRef。 import { useRef } from react; const refValue useRef(initialValue) refValue.current // 访问ref的值 类似于vue的ref,Vue的ref是.value&#xff0c;其次就是vu…

SpringBoot 赋能家乡特色推荐系统:高效架构与前沿技术集成

1 绪 论 1.1课题背景与意义 在Internet高速发展的今天&#xff0c;计算机的应用几乎完全覆盖我们生活的各个领域&#xff0c;互联网在经济&#xff0c;生活等方面有着举足轻重的地位&#xff0c;成为人们资源共享&#xff0c;信息快速传递的重要渠道。在中国&#xff0c;网上管…

国际知名会计事务所安永造访图为科技,探索财务管理全球化新路径

今日&#xff0c;全球领先的安永会计师事务所&#xff08;以下简称“安永”&#xff09;合伙人造访了图为信息科技&#xff08;深圳&#xff09;有限公司&#xff08;以下简称“图为科技”&#xff09;。 安永就财务管理工作的全球化战略提供专业指导意见&#xff0c;并为双方…

Java 实现手机号码归属地查询

1.pom坐标 <dependency><groupId>com.googlecode.libphonenumber</groupId><artifactId>geocoder</artifactId><version>2.205</version></dependency> 2.代码 package test;import com.alibaba.excel.util.StringUtils; im…

SIP系列七:ICE框架(P2P通话)

我的音视频/流媒体开源项目(github) SIP系列目录 目录 一、NAT 1、NAT介绍 2、NAT类型 2.1、 完全圆锥型NAT 2.2、受限圆锥型NAT 2.3、端口受限圆锥型NAT 2.4、对称NAT 3、NAT打洞 3.1、不同一NAT下 3.2、同一NAT下 二、ICE 三、ICE中的SDP 至此&#x…

python桌面工具

用处 使用该工具可以将excel内容转成SQL语句&#xff0c;可以使用到一些SQL的报表平台可以将json文件转成xlsx格式文件 前期准备 安装库 pip install pandas -i https://mirrors.aliyun.com/pypi/simplepip install wxpython -i https://mirrors.aliyun.com/pypi/simplepip i…

【Golang】Go语言编程思想(一):接口

接口 接口的概念 现在我们要实现一个函数&#xff0c;用于对给定的 url 进行解析&#xff0c;具体的代码实现如下&#xff1a; package mainimport ("fmt""io""net/http" )func retrieve(url string) string {resp, err : http.Get(url)if er…