2023.12.9 关于 Spring Boot 事务传播机制详解

目录

事务传播机制

七大事务传播机制 

支持当前调用链上的事务

Propagation.REQUIRED

Propagation.SUPPORTS

Propagation.MANDATORY

不支持当前调用链上的事务

Propagation.REQUIRES_NEW

Propagation.NOT_SUPPORTED

Propagation.NEVER

嵌套事务

Propagation.NESTED

前置工作

初始化数据库

初始化 实体类

初始化 Mapper 接口

初始化 XML 文件

 重点理解部分

NESTED 和 REQUIRED_NEW 的区别

NESTED 和 REQUIRED 的区别


事务传播机制

  • 事务的传播机制是指在多个事务方法之间调用时,事务如何在这些方法之间传播

七大事务传播机制 

支持当前调用链上的事务

Propagation.REQUIRED

  • 默认的事务传播级别
  • 如果当前没有事务,则新建一个事务,如果当前存在事务,则加入该事务

实例理解


Propagation.SUPPORTS

  • 如果当前方法没有事务,就以非事务方式执行,如果已经存在一个事务中,则加入到这个事务中


Propagation.MANDATORY

  • 如果当前方法没有事务,则抛出异常,如果已经存在一个事务中,则加入到这个事务中

不支持当前调用链上的事务

Propagation.REQUIRES_NEW

  • 创建一个新事务,如果存在当前事务,则挂起当前事务

Propagation.NOT_SUPPORTED

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

Propagation.NEVER

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

嵌套事务

Propagation.NESTED

  • 如果当前存在事务,则在嵌套事务中执行,否则与 REQUIRED 的操作一样

前置工作

  • 此处为了方便下文进行代码测试理解
  • 我们先将准备工作做好

初始化数据库

  • 创建一个 user 表,并添加几条用户信息


初始化 实体类

  • 创建 User 实体类 与 数据库的 user 表字段名相对应
import lombok.Data;

@Data
public class User {
    private int id;
    private String name;
    private int age;
    private String password;
    private int state;
}

初始化 Mapper 接口

  • 初始化 UserMapper 接口,此处我们添加一个 add 方法

import com.example.demo.entity.User;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;

@Mapper
public interface UserMapper {
//    新增用户信息
    Integer add(User user);
}

初始化 XML 文件

  • 在与 UserMapper 接口相对应的 XML 文件中添加上与 add 方法 相对应的 sql 语句
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.demo.mapper.UserMapper">

    <insert id="add">
        insert into user(name,age,password) values(#{name},#{age},#{password})
    </insert>
</mapper>

 重点理解部分

NESTED 和 REQUIRED_NEW 的区别

  • REQUIRED_NEW 是新建一个事务并且新开始的这个事务与原有事务无关
  • 而 NESTED 是当前存在事务时会开启一个嵌套事务
  • 在 NESTED 情况下,父事务回滚时,子事务也会回滚
  • 而 REQUIRED_NEW 情况下,原有事务回滚,不会影响新开启的事务

实例理解

  • 我们创建一个 userController 类,并给 addUser 方法加上 REQUIRED 类型的事务
  •  此时的 addUser 方法,将自动创建一个事务A
  • 注意我们在 addUser 中加入了一个算数异常
import com.example.demo.entity.User;
import com.example.demo.service.LoggerService;
import com.example.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/user2")
public class UserController {
    @Autowired
    private UserService userService;

    @RequestMapping("/add")
    @Transactional(propagation = Propagation.REQUIRED)
    public String addUser(User user) {
//        调用 userService 的 add 方法
        int result = userService.add(user);
        int c = 10/0;
        return "add 方法:" + (result == 1 ? "新增用户成功": "新增用户失败");
    }
}
  • 可以看到 userController 类中调用了 add 方法,该方法在 userService 中
  • 此处我们给 add 方法加上 NESTED 类型的事务
  • 因为 addUser 已经创建了一个事务A,所以此处的 add 方法将在 事务A中创建一个嵌套事务
import com.example.demo.entity.User;
import com.example.demo.mapper.UserMapper;
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 UserService {

    @Autowired
    private UserMapper userMapper;

    //    新增用户信息
    @Transactional(propagation = Propagation.NESTED)
    public int add(User user) {
        int result = userMapper.add(user);
        return result;
    }
}

运行结果:

  • 在浏览器中输入对应的 URL 来访问 addUser 方法
  • 此时我们关注的是 在 NESTED 情况下,父事务回滚时,子事务也会回滚

  • 此处正好为 addUser 方法发生了 算数异常,从而进行了回滚操作
  • 且在抛出算数异常前,就已经调用了 userService.add 方法
  • 如果此处数据库中插入了新用户信息,则代表子事务未进行回滚操作

  • 此时我们查看 user 表,发现新用户未插入,即子事务进行了回滚操作,与预期结果一致

调整 add 方法的事务传播机制

  • 我们将 add 方法改为 REQUIRES_NEW 类型的事务
  • 因为 addUser 已经创建了一个事务A,所以此处的 add 方法将会把 事务A 挂起,另外创建一个 事务B
import com.example.demo.entity.User;
import com.example.demo.mapper.UserMapper;
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 UserService {

    @Autowired
    private UserMapper userMapper;

    //    新增用户信息
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public int add(User user) {
        int result = userMapper.add(user);
        return result;
    }
}

运行结果:

  • 在浏览器中输入对应的 URL 来访问 addUser 方法
  • 此时我们关注的是 REQUIRED_NEW 情况下,原有事务回滚,不会影响新开启的事务

  • 查看 user 表发现新增了一条用户信息,即新开启的事务B 未进行回滚,与预期结果一致


NESTED 和 REQUIRED 的区别

  • REQUIRED 的情况下,调用方存在事务时,则被调用发和调用方使用同一个事务
  • 那么被调用方出现异常时,由于共用同一个事务,所以无论是否 catch 异常,事务都会回滚
  • 而在 NESTTED 情况下,被调用方发生异常时,调用方可以 catch 其异常,这样只有子事务回滚,父事务不回滚

实例理解

  • 我们创建一个 userController 类,并给 addUser 方法加上 REQUIRED 类型的事务
  • 此时的 addUser 方法,将自动创建一个事务A
  • 我们将在 userService 的 add 方法中加入一个 算数异常
  • 为了验证 REQUIRED 的情况下,被调用方出现异常时,由于共用同一个事务,所以无论是否 catch 异常,事务都会回滚
  • 如果不捕获 add 方法抛出的异常 事务A 肯定会进行回滚操作
  • 所以我们此处对算数异常进行捕获,由此来看 事务A 是否还会进行回滚操作
import com.example.demo.entity.User;
import com.example.demo.service.LoggerService;
import com.example.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/user2")
public class UserController {
    @Autowired
    private UserService userService;

    @RequestMapping("/add")
    @Transactional(propagation = Propagation.REQUIRED)
    public String addUser(User user) {
        int result = 0;
        try {
//        调用 userService 的 add 方法
            result = userService.add(user);
        }catch (Exception e){
            e.printStackTrace();
        }
        return "add 方法:" + (result == 1 ? "新增用户成功": "新增用户失败");
    }
}
  • 此处我们给 add 方法加上 REQUIRED 类型的事务
  • 因为 addUser 已经创建了一个事务A,所以此处的 add 方法将会直接加入 事务A 中
  • 此处我们在 add 方法中加入了一个算数异常
import com.example.demo.entity.User;
import com.example.demo.mapper.UserMapper;
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 注解 代表该类会伴随着 项目的启动而注入到容器中
@Service
public class UserService {

    @Autowired
    private UserMapper userMapper;

    //    新增用户信息
    @Transactional(propagation = Propagation.REQUIRED)
    public int add(User user) {
        int result = userMapper.add(user);
        int a = 1/0;
        return result;
    }
}

运行结果:

  • 在浏览器中输入对应的 URL 来访问 addUser 方法

  • 查看 user 表发现没有新增新用户信息,即事务A进行了回滚操作,与预期结果一致

调整 add 方法的事务传播机制

  • 我们将 add 方法改为  NESTED 类型的事务
  • 因为 addUser 已经创建了一个事务A,所以此处的 add 方法将会在 事务A 中创建一个嵌套事务
import com.example.demo.entity.User;
import com.example.demo.mapper.UserMapper;
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 注解 代表该类会伴随着 项目的启动而注入到容器中
@Service
public class UserService {

    @Autowired
    private UserMapper userMapper;

    //    新增用户信息
    @Transactional(propagation = Propagation.NESTED)
    public int add(User user) {
        int result = userMapper.add(user);
        int a = 1/0;
        return result;
    }
}

运行结果:

  • 在浏览器中输入对应的 URL 来访问 addUser 方法
  • 此处为了验证 NESTTED 情况下,被调用方发生异常时,调用方可以 catch 其异常,这样只有子事务回滚,父事务不回滚
  • 在浏览器中输入对应的 URL 来访问 addUser 方法

  • 我们可以看到 事务A 未进行回滚操作

  • 查看 user 表发现没有新增新用户信息,即子事务进行了回滚操作,与预期结果一致

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

币圈新贵Blast,一条可以帮助用户赚钱的Layer2!

Blast在短短两天内实现了超过2亿美元的TVL飙升&#xff0c;这一迅猛增长让整个低迷已久的Layer2市场感到震惊。尽管Blast主网要到二月份才正式上线&#xff0c;但这并未能阻挡用户们热情地锁仓。截至目前&#xff0c;Blast已经吸引了超过75,000名用户&#xff0c;总锁定价值已经…

直流电和交流电

直流电&#xff08;Direct Current&#xff0c;简称DC&#xff09;和交流电&#xff08;Alternating Current&#xff0c;简称AC&#xff09;是电流的两种基本形式。 1. 直流电 直流电是指电流方向始终保持不变的电流。在直流电中&#xff0c;电子只能沿着一个方向移动。直流电…

UEFI下Windows10和Ubuntu22.04双系统安装图解

目录 简介制作U盘启动盘并从U盘启动电脑安装系统安装Windows系统安装Ubuntu 附录双系统时间不一致 简介 传统 Legacy BIOS主板下的操作系统安装可参考本人博客 U盘系统盘制作与系统安装&#xff08;详细图解&#xff09; &#xff0c;本文介绍UEFI主板下的双系统安装&#xff…

快手商品采集API商品列表API商品详情数据API接口获取快手商品信息API

背景介绍 快手商城是快手平台上的一个电商购物频道&#xff0c;类似于淘宝、京东等商城&#xff0c;用户可以通过搜索或者快手 App 首页的一级入口进入。目前&#xff0c;快手商城正在招商中&#xff0c;今年双 11 期间&#xff0c;快手将力推短视频、直播间、店铺、商城这一全…

基于AWS Serverless的Glue服务进行ETL(提取、转换和加载)数据分析(三)——serverless数据分析

3 serverless数据分析 大纲 3 serverless数据分析3.1 创建Lambda3.2 创建API Gateway3.3 结果3.4 总结 3.1 创建Lambda 在Lambda中&#xff0c;我们将使用python3作为代码语言。 步骤图例1、入口2、创建&#xff08;我们选择使用python3.7&#xff09;3、IAM权限&#xff08;…

C# 数据的保存和提取(.TXT格式)

红色部分的才是最终版 一、将页面内容保存到文件中 第一步 创建Visual的Windows窗体应用,使用的是 第二步 创建几个Label控件、TextBox控件、以及Button按钮,而TextBox控件放入Panel中 第三步 先对写法进行了解,了解保存的语句 StreamWriter sw= new StreamWriter(TXT…

自定义类型详解(1)

文章目录 目录1. 结构体1.1 结构的基础知识1.2 结构的声明1.3 特殊的声明1.4 结构的自引用1.5 结构体变量的定义和初始化1.6 结构体内存对齐1.7 修改默认对齐数1.8 结构体传参 2. 位段2.1 什么是位段2.2 位段的内存分配2.3 位段的跨平台问题2.4 位段的应用 3. 枚举3.1 枚举类型…

Appium python自动化测试系列之移动自动化测试!

1.1 移动自动化测试现状 因为软件行业越来越发达&#xff0c;用户的接受度也在不断提高&#xff0c;所以对软件质量的要求也随之提高&#xff0c;当然这个也要分行业&#xff0c;但这个还是包含了大部分。因为成本、质量的变化现在对自动化测试的重视度越来越高&#xff0c;在…

mmseg上手自己的数据集

制作自己的数据集&#xff0c;VOC格式为例。 这三个文件包括数据集的名称。可以使用labelme脚本自动生成。 跟据预测类别修改配置文件 D:\projects\mmsegmentation-main\mmseg\datasets\voc.py 因为是voc格式的数据集&#xff0c;在这个文件里进行配置&#xff0c;修改成自己数…

保研毕业论文查重率多少通过【保姆教程】

大家好&#xff0c;今天来聊聊保研毕业论文查重率多少通过&#xff0c;希望能给大家提供一点参考。 以下是针对论文重复率高的情况&#xff0c;提供一些修改建议和技巧&#xff1a; 保研毕业论文查重率多少通过 在保研过程中&#xff0c;毕业论文的查重率是衡量学术诚信和论文…

微信社群机器人开发

简要描述&#xff1a; 删除朋友圈 请求URL&#xff1a; http://域名地址/deleteSns 请求方式&#xff1a; POST 请求头Headers&#xff1a; Content-Type&#xff1a;application/jsonAuthorization&#xff1a;login接口返回 参数&#xff1a; 参数名必选类型说明wId…

AMEYA360--罗姆与Quanmatic公司利用量子技术优化制造工序并完成验证

全球知名半导体制造商罗姆(总部位于日本京都市)于2023年1月起与 Quanmatic Inc.(总部位于日本东京都新宿区&#xff0c;以下简称“Quanmatic”)展开合作&#xff0c;在半导体制造工序之一的EDS工序中测试并引入量子技术&#xff0c;以优化制造工序中的组合。目前&#xff0c;双…

ip ssl证书怎么更换ip地址

ip ssl证书是一种数字证书&#xff0c;为只有公网ip地址的站点建立安全、加密的通信通道。它通常由权威的证书颁发机构&#xff08;CA&#xff09;颁发&#xff0c;并用于验证网站的身份和安全性。ip ssl证书的主要目的是保护敏感信息&#xff0c;如信用卡号、用户名和密码等&a…

15:00面试,15:06就出来了,问的问题太变态了。。

刚从小厂出来&#xff0c;没想到在另一家公司我又寄了。 在这家公司上班&#xff0c;每天都要加班&#xff0c;但看在钱给的比较多的份上&#xff0c;也就不太计较了。但万万没想到5月一纸通知&#xff0c;所有人不准加班了&#xff0c;不仅加班费没有了&#xff0c;薪资还要降…

【Python网络爬虫入门教程3】成为“Spider Man”的第三课:从requests到scrapy、爬取目标网站

Python 网络爬虫入门&#xff1a;Spider man的第三课 写在最前面从requests到scrapy利用scrapy爬取目标网站更多内容 结语 写在最前面 有位粉丝希望学习网络爬虫的实战技巧&#xff0c;想尝试搭建自己的爬虫环境&#xff0c;从网上抓取数据。 前面有写一篇博客分享&#xff0…

如何使用iPhone15在办公室远程观看家里群晖nas上的4k电影?

文章目录 1.使用环境要求&#xff1a;2.下载群晖Video Station&#xff1a;3.公网访问本地群晖Video Station中的电影&#xff1a;4.公网条件下使用电脑浏览器访问本地群晖video station5.公网条件下使用移动端&#xff08;搭载安卓&#xff0c;ios&#xff0c;ipados等系统的设…

Java并发(十七)----变量的线程安全分析

1、成员变量和静态变量是否线程安全 如果它们没有共享&#xff0c;则线程安全 如果它们被共享了&#xff0c;根据它们的状态是否能够改变&#xff0c;又分两种情况 如果只有读操作&#xff0c;则线程安全 如果有读写操作&#xff0c;则这段代码是临界区&#xff0c;需要考虑线…

深信服AF设置安全防护策略

以百度为例&#xff0c;禁止内网用户访问www.baidu.com 1、对象→安全策略模板→新增 2、名称自定义&#xff0c;安全配置只选择url过滤 3、点击图标→新增→设置名称及url 勾选刚刚新增的url--deny→确定 4、高级选项→确定 5、策略→安全策略→安全防护策略→新增用户防护策略…

unity 3分钟 制作粒子爆炸效果 可以用在三消消除等

思路就是&#xff1a; 有一个对象池&#xff0c;管理各种特效。 当需要播放特效时&#xff0c;触发如下代码&#xff1a; blocker为粒子生成的位置 var particles gamePools.iceParticlesPool.GetObject(); if (particles ! null) {particles.transform.position blocker…

如何通过内网穿透工具实现任意浏览器远程访问Linux本地zabbix web管理界面

前言 Zabbix是一个基于WEB界面的提供分布式系统监视以及网络监视功能的企业级的开源解决方案。能监视各种网络参数&#xff0c;保证服务器系统的安全运营&#xff1b;并提供灵活的通知机制以让系统管理员快速定位/解决存在的各种问题。 本地zabbix web管理界面限制在只能局域…