Spring对事务的支持

news2024/9/20 16:59:47

目录

事务的传播行为

一、什么是事务的传播行为

二、7种事务传播行为

1. REQUIRED

2. REQUIRES_NEW

3. NESTED

4. 总结

三、事务的传播行为(理解记忆)


转载:一个99%的人都说不清楚知识点——Spring 事务传播行为 - 哔哩哔哩

事务属性包括哪些?

  • 事务传播行为;--Propagation(本文重点解读事务传播行为)
  • 事务隔离级别;--Isolation
  • 事务超时;--timeout
  • 只读事务;--readOnly
  • 设置出现哪些异常回滚事务;--rollbackFor
  • 设置出现哪些异常不回滚事务;--noRollbackFor

事务的传播行为

一、什么是事务的传播行为

        在service类中有a()方法和b()方法,a()方法上有事务,b()方法上也有事务,当a()方法执行过程中调用了b()方法,事务是如何传递的?合并到一个事务里?还是开启一个新的事务?这就是事务传播行为。

注意:以下事务传播属性都是打在内部方法b()方法上的事务注解

二、7种事务传播行为

Spring 可以通过 @Transactional 注解的 propagation 属性来设置不同的传播行为策略。Spring 为此提供了一个枚举类 Propagation,源码如下:

package org.springframework.transaction.annotation;

import org.springframework.transaction.TransactionDefinition;

public enum Propagation {

   /**
    * 需要事务,它是默认传播行为,如果当前存在事务,就沿用当前事务,
    * 否则新建一个事务运行内部方法
    */
   REQUIRED(TransactionDefinition.PROPAGATION_REQUIRED),

   /**
    * 支持事务,如果当前存在事务,就沿用当前事务,
    * 如果不存在,则继续采用无事务的方式运行内部方法
    */
   SUPPORTS(TransactionDefinition.PROPAGATION_SUPPORTS),

   /**
    * 必须使用事务,如果当前没有事务,则会抛出异常,
    * 如果存在当前事务,则沿用当前事务
    */
   MANDATORY(TransactionDefinition.PROPAGATION_MANDATORY),

   /**
    * 无论当前事务是否存在,都会创建新事务运行方法,
    * 这样新事务就可以拥有新的锁和隔离级别等特性,与当前事务相互独立
    */
   REQUIRES_NEW(TransactionDefinition.PROPAGATION_REQUIRES_NEW),

   /**
    * 不支持事务,当前存在事务时,将挂起事务,运行方法
    */
   NOT_SUPPORTED(TransactionDefinition.PROPAGATION_NOT_SUPPORTED),

   /**
    * 不支持事务,如果当前方法存在事务,则抛出异常,否则继续使用无事务机制运行
    */
   NEVER(TransactionDefinition.PROPAGATION_NEVER),

   /**
    * 在当前方法调用内部方法时,如果内部方法发生异常,
    * 只回滚内部方法执行过的 SQL ,而不回滚当前方法的事务
    */
   NESTED(TransactionDefinition.PROPAGATION_NESTED);

......

}

接下来我们通过对其中三种最常用的(REQUIRED、REQUIRES_NEW、NESTED)策略进行对比来更深入的理解。以下测试均在外部方法开启事务的情况下进行,因为在外部没有事务的情况下,三者都会新建事务,效果一样。 

1. REQUIRED

当内部方法的事务传播行为设置为 REQUIRED 时,内部方法会加入外部方法的事务。我们在 UserServiceImpl 中添加如下方法:  

@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

   @Autowired
   private UserMapper mapper;

   @Override
   @Transactional(propagation = Propagation.REQUIRED)
   public void addWithRequired(User user) {
       mapper.insert(user);
  }

   @Override
   @Transactional(propagation = Propagation.REQUIRED)
   public void addWithRequiredAndException(User user) {
       mapper.insert(user);
       throw new RuntimeException();
  }
}

创建 TransactionServiceImpl 类,并添加如下方法:

@Slf4j
@Service
public class TransactionServiceImpl implements TransactionService {

    @Autowired
    private UserService userService;

    @Override
    public void noTransaction_required_required_externalException() {

        User xiaoShui = new User().setName("小水");
        User xiaoJing = new User().setName("小镜");
        userService.addWithRequired(xiaoShui);
        userService.addWithRequired(xiaoJing);
        throw new RuntimeException();

    }

    @Override
    public void noTransaction_required_requiredException() {

        User xiaoShui = new User().setName("小水");
        User xiaoJing = new User().setName("小镜");
        userService.addWithRequired(xiaoShui);
        userService.addWithRequiredAndException(xiaoJing);

    }

    @Override
    @Transactional
    public void transaction_required_required_externalException() {

        User xiaoShui = new User().setName("小水");
        User xiaoJing = new User().setName("小镜");
        userService.addWithRequired(xiaoShui);
        userService.addWithRequired(xiaoJing);
        throw new RuntimeException();

    }

    @Override
    @Transactional
    public void transaction_required_requiredException() {

        User xiaoShui = new User().setName("小水");
        User xiaoJing = new User().setName("小镜");
        userService.addWithRequired(xiaoShui);
        userService.addWithRequiredAndException(xiaoJing);

    }


    @Override
    @Transactional
    public void transaction_required_requiredException_try() {

        User xiaoShui = new User().setName("小水");
        User xiaoJing = new User().setName("小镜");
        userService.addWithRequired(xiaoShui);
        try {
            userService.addWithRequiredAndException(xiaoJing);
        } catch (Exception e) {
            log.error("发生异常,事务回滚!");
        }

    }
}

结果分析如下表所示:

前面四种情况都比较好理解,很多人不能理解最后一种情况:我都 try-catch 了你还想怎样?这里的关键点在于所有方法都处于同一个事务中,此时「小镜」的插入方法发生异常,那么这个方法所在的事务就会被 Spring 设置为 rollback 状态。因为异常被 catch 了,所以外部方法执行完要进行 commit 操作,这时却发现当前事务已经处于 rollback 状态了,虽然它不知道哪里出了问题,但也只能听从指挥,回滚所有操作了。

PS:由于外部方法不开启事务的情况,在每种传播行为下结果都是类似的,所以后面不再给出示例。

2. REQUIRES_NEW

当内部方法的传播行为设置为 REQUIRES_NEW 时,内部方法会先将外部方法的事务挂起,然后开启一个新的事务 。在 UserServiceImpl 中添加如下方法(UserServiceImpl 类中上面的方法还在哦):

@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

    ...

    @Override
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void addWithRequiredNew(User user) {

        mapper.insert(user);

    }

    @Override
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void addWithRequiredNewAndException(User user) {

        mapper.insert(user);
        throw new RuntimeException();

    }
}

在 TransactionServiceImpl 中添加如下方法:

@Slf4j
@Service
public class TransactionServiceImpl implements TransactionService {

    ...

    @Override
    @Transactional
    public void transaction_required_requiredNew_externalException() {

        User xiaoShui = new User().setName("小水");
        User xiaoJing = new User().setName("小镜");
        userService.addWithRequired(xiaoShui);
        userService.addWithRequiredNew(xiaoJing);
        throw new RuntimeException();

    }

    @Override
    @Transactional
    public void transaction_required_requiredNew_requiredNewException() {

        User xiaoShui = new User().setName("小水");
        User xiaoJing = new User().setName("小镜");
        User shuiJing = new User().setName("水镜");
        userService.addWithRequired(xiaoShui);
        userService.addWithRequiredNew(xiaoJing);
        userService.addWithRequiredNewAndException(shuiJing);

    }

    @Override
    @Transactional
    public void transaction_required_requiredNewException_try() {

        User xiaoShui = new User().setName("小水");
        User xiaoJing = new User().setName("小镜");
        User shuiJing = new User().setName("水镜");
        userService.addWithRequired(xiaoShui);
        userService.addWithRequiredNew(xiaoJing);
        try {
            userService.addWithRequiredNewAndException(shuiJing);
        } catch (Exception e) {
            log.error("发生异常,事务回滚!");
        }

    }
}

结果分析如下表所示:


3. NESTED

当内部方法的传播行为设置为 NESTED 时,内部方法会开启一个新的嵌套事务(子事务)。在 UserServiceImpl 中添加如下方法:

@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

    ...

    @Override
    @Transactional(propagation = Propagation.NESTED)
    public void addWithNested(User user) {

        mapper.insert(user);

    }

    @Override
    @Transactional(propagation = Propagation.NESTED)
    public void addWithNestedAndException(User user) {

        mapper.insert(user);
        throw new RuntimeException();

    }
}

在 TransactionServiceImpl 中添加如下方法:  

@Slf4j
@Service
public class TransactionServiceImpl implements TransactionService {

    ...

    @Override
    @Transactional
    public void transaction_nested_nested_externalException() {

        User xiaoShui = new User().setName("小水");
        User xiaoJing = new User().setName("小镜");
        userService.addWithNested(xiaoShui);
        userService.addWithNested(xiaoJing);
        throw new RuntimeException();

    }

    @Override
    @Transactional
    public void transaction_nested_nestedException() {

        User xiaoShui = new User().setName("小水");
        User xiaoJing = new User().setName("小镜");
        userService.addWithNested(xiaoShui);
        userService.addWithNestedAndException(xiaoJing);

    }

    @Override
    @Transactional
    public void transaction_nested_nestedException_try() {

        User xiaoShui = new User().setName("小水");
        User xiaoJing = new User().setName("小镜");
        User shuiJing = new User().setName("水镜");
        userService.addWithRequired(xiaoShui);
        userService.addWithNested(xiaoJing);
        try {
            userService.addWithNestedAndException(shuiJing);
        } catch (Exception e) {
            log.error("发生异常,事务回滚!",e);
        }

    }
}

结果分析如下表所示:


每个 NESTED 事务执行前会将当前操作保存下来,叫做 savepoint (保存点),如果当前 NESTED 事务执行失败,则回滚到之前的保存点,保存点使得子事务的回滚不对主事务造成影响。NESTED 事务在外部事务提交以后自己才会提交。

4. 总结


REQUIRES_NEW 最为简单,不管当前有无事务,它都会开启一个全新事务,既不影响外部事务,也不会影响其他内部事务,真正的井水不犯河水,坚定而独立。

REQUIRED 在没有外部事务的情况下,会开启一个独立的新事务,且不会对其他同级事务造成影响;而当存在外部事务的情况下,则会与外部事务同生共死。

NESTED 在没有外部事务的情况下与 REQUIRED 效果相同;而当存在外部事务的情况下,当外部事务回滚时,它会创建一个嵌套事务(子事务)。外部事务回滚时,子事务会跟着回滚;但子事务的回滚不会对外部事务和其他同级事务造成影响。

三、事务的传播行为(理解记忆)

 

另可以学习:三、事务的传播行为 - 简书

聊聊spring七种事务传播行为? - 知乎

老杜:输入密码 · 语雀

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

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

相关文章

2023.7.7HCIA中静态路由

一、题目要求 需求&#xff1a; 1.所有节点的IP地址均属于192.168.1.0 24--子网划分 2.全网可达--静态 3.加快收敛&#xff08;尽量汇总&#xff09; 5.R6存在环回接口--6.6.6.6 24&#xff0c;不能直接写去往6.6.6.0网段的路由--缺省 6.没有环路--NUll 0 7.正常流量走100M&am…

什么是ERC20?

ERC20 是以太坊区块链上最常见的代币标准之一。它是以太坊上智能合约的一种协议&#xff0c;定义了一套规则和接口&#xff0c;使得在以太坊网络上创建和管理代币变得更加简单和标准化。 ERC20 代币标准定义了一组功能和方法&#xff0c;以便代币合约可以与其他合约和钱包进行…

dxSpreadSheetReportDesigner使用笔记

通过该控件达到显示主从表,效果如下图所示. 在界面上放置以下控件 1.新建主从表主表为tab1,从表为tab2,二表通过设置从表的mastersource及MasterFields与从表联动. 2.设置dxspreadsheetreportdesigner与主从表关联 在DataBinding中的datasource关联主从. 在details中关联从表…

UE5、CesiumForUnreal接入XYZ格式地图瓦片如高德地图、OSM、ArcGIS等

文章目录 1.实现目标2.实现过程2.1 XYZ与TMS对比2.1 cesium-native改造2.3 CesiumForUnreal插件改造2.4 XYZ瓦片加载测试3.参考资料1.实现目标 通过改造cesium-native和cesiumforunreal插件,参考tms的栅格地图瓦片加载逻辑,实现在UE5、CesiumForUnreal中接入XYZ格式的地图瓦片…

-Xmx20m -Xms5m

-Xmsjava程序启动时初始堆的大小&#xff0c;默认是物理内存的1/64-Xmxjava程序能获得的最大堆的大小&#xff0c;默认为物理内存的1/4 验证默认值 winr → systeminfo 配置JVM启动参数&#xff1a;-XX:PrintCommandLineFlags-XX:ConcGCThreads3 -XX:G1ConcRefinementThreads…

UEditor v1.4.3.3 .net版本任意文件上传 漏洞复现

为方便您的阅读&#xff0c;可点击下方蓝色字体&#xff0c;进行跳转↓↓↓ 01 漏洞描述02 影响范围03 验证方式04 利用方式05 实战案例06 修复方案 01 漏洞描述 UEditor是一款所见即所得的开源富文本编辑器&#xff0c;具有轻量、可定制、用户体验优秀等特点&#xff0c;被广大…

flutter聊天界面-加号【➕】更多展开相机、相册等操作Panel

flutter聊天界面-加号【➕】更多展开相机、相册等操作Panel 在之前实现了flutter聊天界面的自定义表情的展示&#xff0c;这里记录一下更多操作展开的相机、相册等操作功能实现。 一、查看效果 更多操作展开的相机、相册等操作功能实现。 二、代码实现 展开的操作按钮可能比…

前端(四)——vue.js、vue、vue2、vue3

&#x1f60a;博主&#xff1a;小猫娃来啦 &#x1f60a;文章核心&#xff1a;vue.js、vue、vue2、vue3从全局到局部 文章目录 vue.js、vue、vue2、vue3是什么关系?Vue.js简介发展历程特点与优势生态系统Vue.js基础知识安装与配置 基本语法Vue.js主要版本解析Vue.js 2.x vue2…

python 第十一章 文件操作

系列文章目录 第一章 初识python 第二章 变量 第三章 基础语句 第四章 字符串str 第五章 列表list [] 第六章 元组tuple ( ) 第七章 字典dict {} 第八章 集合set {} 第九章 常用操作 第十章 函数 文章目录 系列文章目录11.1文件操作的作用11.2文件的基本操作打开打开文件模式 …

string底层是如何实现的

前言 在我们学习的时候总是会用到string&#xff0c;知道它具备各种功能&#xff0c;它也是一种很强大的模板&#xff0c;那么有没有想过&#xff0c;我们天天都在使用的它&#xff0c;它的底层又是怎么样的&#xff0c;它又是如何实现的呢。这里讲挑选几个比较常用的一个功能…

SGD原理及Pytorch实现

&#x1f38f;目录 &#x1f388;1 SGD       &#x1f384;1.1 原理       &#x1f384;1.2 构造       &#x1f384;1.3 参数详解——momentum ✨1 SGD &#x1f95a;2.1 原理 SGD为随机梯度下降&#xff0c;原理可看刘建平老师博客。 &#x1f383;…

Blender--原理化体积

“原理化体积 着色器将所有体积着色组件组合到一个易于使用的节点中。该节点含有散射&#xff0c;吸收和黑体辐射属性&#xff0c;因此&#xff0c;可以仅仅使用该着色器节点对烟雾和火焰等进行渲染。” 官方文档介绍&#xff1a;原理化体积 — Blender Manual 可以用于实现丁…

Rainbond开源

Rainbond的 Gateway API 插件制作实践 Gateway API 作为新一代的流量管理标准&#xff0c;对原有 Ingress 的扩展不规范、移植性差等问题做出了改进。从兼容K8s生态和优化网关体验出发&#xff0c;Rainbond 支持以插件的形式扩展平台网关能力&#xff0c;目前已经有多家社区提供…

启动项目报错,如何分析pom

报错信息如下 Caused by: java.lang.NoClassDefFoundError: org/springframework/core/metrics/ApplicationStartup 报错说找不到ApplicationStartup这个类&#xff0c;到项目中查看pom的dependency 我的项目引入了两个依赖 <parent><groupId>org.springframewor…

vue使用富文本编辑器 Wangeditor 可显示编辑新增回显禁用

1.效果图 2.安装依赖 npm install wangeditor 3.在main.js 全局引入 富文本组件 import editorBar from "/components/editor/editor.vue";Vue.component(editorBar, editorBar) 全局引入页面使用 <editor-bar v-model"form.nr" :flag"false&quo…

【高并发网络通信架构】1.Linux下实现单客户连接的tcp服务端

目录 一&#xff0c;函数清单 1.socket 方法 2.bind 方法 3.listen 方法 4.accept 方法&#xff08;阻塞函数&#xff09; 5.recv 方法&#xff08;阻塞函数&#xff09; 6.send 方法 7.close 方法 8.htonl 方法 9.htons 方法 10.fcntl 方法 二&#xff0c;代码实现…

0126 线性表

目录 2.线性表 2.1线性表的定义和基本概念 2.1部分习题 2.2线性表的顺序表示 2.2部分习题 2.3线性表的链式表示 2.3部分习题 2.线性表 2.1线性表的定义和基本概念 2.1部分习题 1.线性表是具有n个&#xff08;&#xff09;的有限序列 A.数据表 B.字符 C.…

FPGA纯verilog实现UDP协议栈,sgmii接口SFP光口收发,提供工程源码和技术支持

目录 1、前言2、我这里已有的UDP方案3、该UDP协议栈性能4、详细设计方案SFPGMII AXIS接口模块AXIS FIFOUDP协议栈1G/2.5G Ethernet PCS/PMA or SGMII 5、vivado工程1-->B50610 工程6、上板调试验证并演示准备工作查看ARPUDP数据回环测试 7、福利&#xff1a;工程代码的获取 …

c++日程管理系统

一.需求分析 多功能日历&#xff08;要求有简单ui&#xff09; 要求&#xff1a; 1.使用c代码用visual stdio运行 2.用户登录注册 3.登录后给用户三大功能选择&#xff1a; &#xff08;1&#xff09;基本日历&#xff1a;显示日历,默认六月&#xff0c;每日有对应的日程 &…

开源防病毒引擎ClamAV

本文软件是应网友 Windows 的要求折腾的&#xff1b; 什么是 ClamAV &#xff1f; ClamAV 是一个开源 ( GPLv2 ) 反病毒工具包&#xff0c;专为邮件网关上的电子邮件扫描而设计。它提供了许多实用程序&#xff0c;包括灵活且可扩展的多线程守护程序、命令行扫描程序和用于自动数…