【Spring】事务传播机制

news2024/11/27 12:12:30

事务传播机制

  • 一. 事务传播机制是什么
  • 二. 为什么需要事务传播机制
  • 三. 事务传播机制有哪些
  • 四. Spring 事务传播机制演示
    • 1. ⽀持当前事务(REQUIRED)
    • 2. 不支持当前事务(REQUIRES_NEW)
    • 3. 不⽀持当前事务,NEVER 抛异常
    • 4. NESTED 嵌套事务

一. 事务传播机制是什么

Spring 事务传播机制定义了多个包含了事务的⽅法,相互调⽤时,事务是如何在这些⽅法间进⾏传递的。

二. 为什么需要事务传播机制

事务隔离级别是保证多个并发事务执⾏的可控性的(稳定性的),⽽事务传播机制是保证⼀个事务在多个调⽤⽅法间的可控性的(稳定性的)。

举个栗子:

比如新冠病毒,它有不同的隔离⽅式(酒店隔离还是居家隔离),是为了保证疫情可控,然⽽在每个⼈的隔离过程中,会有很多个执⾏的环节,⽐如酒店隔离,需要负责⼈员运送、物品运送、消杀原⽣活区域、定时核算检查和定时送餐等很多环节,⽽事务传播机制就是保证⼀个事务在传递过程中是可靠性的,回到本身案例中就是保证每个⼈在隔离的过程中可控的。

事务隔离级别解决的是多个事务同时调⽤⼀个数据库的问题,如下图:

在这里插入图片描述

⽽事务传播机制解决的是⼀个事务在多个节点(⽅法)中传递的问题,如下图所示:
方法 1 调用 方法 2, 方法 2 调用方法 3:

在这里插入图片描述

三. 事务传播机制有哪些

Spring 事务传播机制包含以下 7 种:

  1. Propagation.REQUIRED:默认的事务传播级别,它表示如果当前存在事务,则加⼊该事务;如果当前没有事务,则创建⼀个新的事务。
  2. Propagation.SUPPORTS:如果当前存在事务,则加⼊该事务;如果当前没有事务,则以⾮事务的⽅式继续运⾏。
  3. Propagation.MANDATORY:(mandatory:强制性)如果当前存在事务,则加⼊该事务;如果当前没有事务,则抛出异常。
  4. Propagation.REQUIRES_NEW:表示创建⼀个新的事务,如果当前存在事务,则把当前事务挂起。也就是说不管外部⽅法是否开启事务,Propagation.REQUIRES_NEW 修饰的内部⽅法会新开启⾃⼰的事务,且开启的事务相互独⽴,互不⼲扰。
  5. Propagation.NOT_SUPPORTED:以⾮事务⽅式运⾏,如果当前存在事务,则把当前事务挂起。
  6. Propagation.NEVER:以⾮事务⽅式运⾏,如果当前存在事务,则抛出异常。
  7. Propagation.NESTED:如果当前存在事务,则创建⼀个事务作为当前事务的嵌套事务来运⾏;如果当前没有事务,则该取值等价于 PROPAGATION_REQUIRED。

以上 7 种传播⾏为,可以根据是否⽀持当前事务分为以下 3 类:

在这里插入图片描述

以情侣关系为例来理解以上分类:

在这里插入图片描述

四. Spring 事务传播机制演示

1. ⽀持当前事务(REQUIRED)

先开启事务先成功插⼊⼀条⽤户数据,然后再执⾏⽇志报错,⽽在⽇志报错是发⽣了异常,观察 propagation = Propagation.REQUIRED 的执⾏结果。

controller 层,传播机制为 REQUIRED,先调用保存用户,再调用保存 日志

@RestController
public class UserController {
    @Resource
    private UserService userService;

    @Resource
    private LogService logService;

    @RequestMapping("/save")
    @Transactional(propagation = Propagation.REQUIRED)
    public Object save(User user) {
        // 插⼊⽤户操作
        userService.save(user);

        // 插⼊⽇志
        logService.saveLog("⽤户插⼊:" + user.getUsername());
        return true;
    }
}

UserService ,传播机制为 REQUIRED,正常保存

@Service
public class UserService {
    @Resource
    private UserMapper userMapper;
    @Transactional(propagation = Propagation.REQUIRED)
    public int save(User user) {
        System.out.println("执⾏ save ⽅法.");
        return userMapper.save(user);
    }
}

LogService ,传播机制为 REQUIRED,抛异常,注意异常一定要捕获

@Service
public class LogService {
    @Resource
    private LogMapper logMapper;
    @Transactional(propagation = Propagation.REQUIRED)
    public int saveLog(String content) {
        int result = logMapper.saveLog(content);
        // 出现异常
        try {
            int i = 10 / 0;
        } catch (Exception e) {
            // 一定要捕捉异常
            // 然后手动回滚
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
        }
        return result;
    }
}

执⾏结果:用户表和日志表都没有插⼊任何数据。

  1. UserService 中的保存⽅法正常执⾏完成。
  2. LogService 保存⽇志程序报错。
  3. 因为三个方法的事务传播机制使⽤的都是 REQURIED, UserService、 LogService 和 Controller 使用同一个事务,所以整个事务回滚。数据库中没有插⼊任何数据,也就是步骤 1 中的⽤户插⼊⽅法也回滚了。

在这里插入图片描述

2. 不支持当前事务(REQUIRES_NEW)

UserController 类中的代码不变,将添加⽇志的⽅法修改为 REQUIRES_NEW 不⽀持当前事务,重新创建事务,观察执⾏结果:

controller 层,传播机制为 REQUIRED,先调用保存用户,再调用保存 日志

@RestController
public class UserController {
    @Resource
    private UserService userService;

    @Resource
    private LogService logService;

    @RequestMapping("/save")
    @Transactional(propagation = Propagation.REQUIRED)
    public Object save(User user) {
        // 插⼊⽤户操作
        userService.save(user);

        // 插⼊⽇志
        logService.saveLog("⽤户插⼊:" + user.getUsername());
        return true;
    }
}

UserService ,传播机制为 REQUIRES_NEW,正常保存

@Service
public class UserService {
    @Resource
    private UserMapper userMapper;
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public int save(User user) {
        System.out.println("执⾏ save ⽅法.");
        return userMapper.save(user);
    }
}

LogService ,传播机制为 REQUIRES_NEW,抛异常, 注意异常一定要捕获

@Service
public class LogService {
    @Resource
    private LogMapper logMapper;
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public int saveLog(String content) {
        int result = logMapper.saveLog(content);
        // 出现异常
        try {
            int i = 10 / 0;
        } catch (Exception e) {
            // 一定要捕捉异常
            // 然后手动回滚
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
        }
        return result;
    }
}

程序执⾏结果:User 表中成功插⼊了数据,Log 表执⾏失败,但没影响 UserController 中的事务。

在这里插入图片描述

3. 不⽀持当前事务,NEVER 抛异常

controller 层,传播机制为 REQUIRED,只调用保存用户

@RestController
public class UserController {
    @Resource
    private UserService userService;
    
    @RequestMapping("/save")
    @Transactional(propagation = Propagation.REQUIRED)
    public Object save(User user) {
        // 插⼊⽤户操作
        userService.save(user);
        return true;
    }
}

UserService ,传播机制为 NEVER,正常保存

@Service
public class UserService {
    @Resource
    private UserMapper userMapper;
    @Transactional(propagation = Propagation.NEVER)
    public int save(User user) {
        System.out.println("执⾏ save ⽅法.");
        return userMapper.save(user);
    }
}

程序执⾏报错,⽤户表未添加任何数据。

4. NESTED 嵌套事务

controller 层,传播机制为 REQUIRED/NESTED/或者不写参数都行,调用保存用户

@RestController
public class UserController {
    @Resource
    private UserService userService;


    @RequestMapping("/save")
    @Transactional(propagation = Propagation.REQUIRED)
    public Object save(User user) {
        // 插⼊⽤户操作
        userService.save(user);
        return true;
    }
}

UserService ,传播机制为 NESTED ,正常保存

@Service
public class UserService {
    @Resource
    private UserMapper userMapper;
    
    @Resource
    private LogService logService;
    
    @Transactional(propagation = Propagation.NESTED)
    public int save(User user) {
        int result = userMapper.save(user);
        System.out.println("执⾏ save ⽅法.");
        // 调用插入插⼊⽇志的方法
        logService.saveLog("⽤户插⼊:" + user.getUsername());
        return result;
    }
}

LogService ,传播机制为 NESTED ,抛异常, 注意异常一定要捕获

@Service
public class LogService {
    @Resource
    private LogMapper logMapper;

    @Transactional(propagation = Propagation.NESTED)
    public int saveLog(String content) {
        int result = logMapper.saveLog(content);
        // 出现异常
        try {
            int i = 10 / 0;
        } catch (Exception e) {
            // 一定要捕捉异常
            // 然后手动回滚
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
        }
        return result;
    }
}

最终程序的执⾏结果:
⽤户表中数据添加成功,⽇志表中没有添加任何数据,Log 中的事务已经回滚,但是嵌套事务不会回滚嵌套之前的事务,也就是说嵌套事务可以实现部分事务回滚。

嵌套事务之所以能够实现部分事务的回滚,是因为事务中有⼀个保存点(savepoint)的概念,嵌套事务进⼊之后相当于新建了⼀个保存点,⽽回滚时只回滚到当前保存点,因此之前的事务是不受影响的。

在这里插入图片描述

嵌套事务(NESTED)和加⼊事务(REQUIRED )的区别:

  • 整个事务如果全部执⾏成功,⼆者的结果是⼀样的。
  • 如果事务执⾏到⼀半失败了,那么加⼊事务整个事务会全部回滚;⽽嵌套事务会局部回滚,不会影响上⼀个⽅法中执⾏的结果。

嵌套事务(NESTED)和新建事务(REQUIRE_NEW )的区别:

  • 对于 NESTED,当外部事务回滚时,内部事务一定回滚。
  • 对于 REQUIRE_NEW,外部事务与内部事务之间是相互独立的,所以外部事务回滚时,内部事务不会回滚。

注意:

  1. 当一个事务中出现异常时一定要将它 catch 然后手动回滚事务,如果不 catch 那么异常就会沿着调用链一直往上抛,这样只要是涉及到这个异常的所有事务都会回滚。
  2. 只有 NESTED 嵌套事务是类似于能同时存在多个事务,其他六种传播机制都是同一时刻只存在一个事务。
  3. 对于 NESTED,外部事务提交之前,内部事务无法提交,即外部事务回滚,内部事务一定回滚。

好啦! 以上就是对 Spring 事务传播机制 的讲解,希望能帮到你 !
评论区欢迎指正 !

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

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

相关文章

订单型企业经营分析指标大全(ODOO15/16)

ODOO-ERP搭建完成之后,我们重点是帮客户建立经营分析能力,以下是针对订单型企业的经营分析指标,涵盖业务运营的监控、资产构成、利润、盈亏点计算、资产运营效率等各方面,并且持续完善。 有些企业不重视,觉得自己企业小…

WebAPI+EF连接SQL Server数据库

右击解决方案-添加-新建项目-选择“类库(.NET Framework)”,新建的项目取名叫WebApi1.EF 添加EF: 新建一个ADO实体数据模型 选择DBFirst 数据源选择MySql 填写数据库地址及账号密码 选择实体框架版本 选择在数据库中的表User 到此配置完成&am…

对称加密和非对称加密以及CA证书

对称加密 对称加密只用到了公匙,这个公匙是消息的发送方和消息的接收方共享的,也就是消息发送方使用这个公匙对消息加密,然后接收方使用公匙对消息解密,最典型的例子比如像 encrypt 加密,比如我们有个 springboot 应用,需要对 application.yml 文件里的密码做加密,我们…

操作系统备考学习 day8 (2.4.1 ~ 2.4.4)

操作系统备考学习 day8 第二章 进程与线程2.4 死锁2.4.1 死锁的基本概念死锁产生的必要条件死锁什么时候会发生死锁的处理策略 2.4.2 死锁预防破坏互斥条件破坏不剥夺条件破坏请求和保持条件破坏循环等待条件 2.4.3 避免死锁银行家算法 2.4.4 死锁的检测和解除死锁的检测死锁的…

faster python之ctypes调用so/dll

目录 .so和.dll文件将go代码编译为动态链接库将C代码编译成动态链接库 ctypes库基础数据类型使用方法基本数据类型数组类型指针类型结构体类型嵌套结构体结构体数组结构体指针结构体指针数组 ctypes加载DLL的方式1. 使用 CDLL 类2. 使用 WinDLL 类(Windows特定&…

【LeetCode高频SQL50题-基础版】打卡第8天:第41~45题

文章目录 【LeetCode高频SQL50题-基础版】打卡第8天:第41~45题⛅前言好友申请II🔒题目🔑题解 2016年的投资🔒题目🔑题解 部门工资前三高的所有员工🔒题目🔑题解 修复表中的名字🔒题目…

深入JTS事务引擎:理论与实践相结合,掌握高效事务管理的秘诀

事务是可靠应用程序的构建块 如果您阅读过任何有关 J2EE 的介绍性文章或者书籍,那么就会发现,只有一小部分资料是专门针对 Java Transaction Service(JTS)或 Java Transaction API(JTA)的。这并不是因为 J…

[补题记录] Atcoder Beginner Contest 295(E)

URL:https://atcoder.jp/contests/abc295 目录 E Problem/题意 Thought/思路 Code/代码 E Problem/题意 给定长度为 N 的数组 A。进行如下操作: 若 Ai 0,将 Ai 等概率地变为 1 ~ M 中的任意一个数;对 A 排序; …

SQL及数据库基础知识点总结

一. SQL(Structured Query Language): 结构化查询语言。SQL语法不区分关键字的大小写,多条SQL语句必须以;分隔。 二. SQL的作用: SQL可以访问和处理数据库,包括数据的增删改查(插…

【实战项目之网页聊天室】

目录 项目背景 需求分析 1.用户管理模块 注册 登录 2.主界面 个人信息模块 会话列表模块 好友列表模块 消息区域模块 消息传输模块 添加好友模块 编写项目 1.创建项目添加依赖 2.配置项目信息 3.功能实现 用户管理模块 个人信息模块 好友列表模块 消息区…

FPmarkets:MT4中Renko图表工具有哪些?怎么用

以下FPmarkets总结的在MT4中使用Renko图表的最有趣的工具: 第一个是KT Renko实时图表指标,这是一个简单的指示器,仅显示砖块,未添加其他元素,因此与其他自定义指标和顾问兼容。 第二个是Renko Live Chart开发人员提供…

Kafka消费者使用案例

本文代码链接:https://download.csdn.net/download/shangjg03/88422633 1.消费者和消费者群组 在 Kafka 中,消费者通常是消费者群组的一部分,多个消费者群组共同读取同一个主题时,彼此之间互不影响。Kafka 之所以要引入消费者群组…

源码选择指南:比较流行的同城外卖跑腿系统解决方案

随着现代生活的快节奏和数字化转型的浪潮,外卖和跑腿服务成为了不可或缺的一部分。不仅在城市,就连小镇和乡村也可以享受到这些便捷的服务。如果您计划开发或改进一个同城外卖和跑腿系统,选择适合的源码解决方案是至关重要的。在本文中&#…

Jenkins 结合 ANT 发送测试报告

目录 全局变量配置 新建任务 插件安装 HTML 报告配置 邮件配置 全局变量配置 点击 ManageJenkins进入Jenkins 管理 点击 Global Tool Configuration 进入全局变量配置 配置 Ant ,Name 自己定义一个比较好理解的名称。 去掉 Install automatically 勾选&#x…

毕业设计选题Java+springboot校园新闻资讯系统源码 开题 lw 调试

💕💕作者:计算机源码社 💕💕个人简介:本人七年开发经验,擅长Java、Python、PHP、.NET、微信小程序、爬虫、大数据等,大家有这一块的问题可以一起交流! 💕&…

VBA技术资料MF69:添加和删除工作表中的分页符

我给VBA的定义:VBA是个人小型自动化处理的有效工具。利用好了,可以大大提高自己的工作效率,而且可以提高数据的准确度。我的教程一共九套,分为初级、中级、高级三大部分。是对VBA的系统讲解,从简单的入门,到…

黑马JVM总结(三十三)

(1)运行期优化-逃逸分析 在运行期间java虚拟机会对我们代码做一些优化,时间会变短: 字节码反复调用,到达一定的阈值,会启用编译器对自己饿吗编译执行,从0层上升为1层C1 C1和C2他俩的区别是解释…

C语言进阶第六课-----------字符分类函数和内存的开辟

作者前言 🎂 ✨✨✨✨✨✨🍧🍧🍧🍧🍧🍧🍧🎂 ​🎂 作者介绍: 🎂🎂 🎂 🎉🎉&#x1f389…

【Python从入门到进阶】39、使用Selenium自动验证滑块登录

接上篇《38、selenium关于Chrome handless的基本使用》 上一篇我们介绍了selenium中有关Chrome的无头版浏览器Chrome Handless的使用。本篇我们使用selenium做一些常见的复杂验证功能,首先我们来讲解如何进行滑块自动验证的操作。 一、测试用例介绍 我们要通过sel…

websocket逆向-protobuf序列化与反序列化

系列文章目录 训练地址:https://www.qiulianmao.com 基础-websocket逆向基础-http拦截基础-websocket拦截基础-base64编码与解码基础-protobuf序列化与反序列化视频号直播弹幕采集实战一:Http轮询更新中 websocket逆向-protobuf序列化与反序列化基础 系…