【实战】Spring Boot 嵌套事务REQUIRES_NEW与NESTED在项目中的运用

news2024/9/22 15:47:22

文章目录

    • 引言
    • 1. 什么是Nested Transactions?
    • 2. Spring Boot中的事务管理
      • 2.1 基本用法
      • 2.2 Nested Transactions的需求场景
    • 3. 实现Nested Transactions
      • 3.1 使用Propagation.REQUIRED)/Propagation.NESTED)
      • 3.2 嵌套事务REQUIRES_NEW与NESTED
      • 3.3 注意事项
    • 4. 测试Nested Transactions
    • 5. 总结

在这里插入图片描述

引言

在开发基于Spring Boot的应用程序时,事务管理是一项至关重要的任务。事务确保了数据的一致性和完整性,尤其是在执行一系列数据库操作时。当涉及到复杂的业务逻辑时,可能需要在单个事务中嵌套多个事务,即所谓的“Nested Transactions”。本文将深入探讨如何在Spring Boot项目中实现和管理Nested Transactions,并深入讨论REQUIRES_NEW与NESTED在项目中的运用。

1. 什么是Nested Transactions?

Nested Transactions指的是在一个外部事务中嵌套一个或多个内部事务。这种模式通常用于处理复杂的业务逻辑,其中某些部分需要在更细粒度上进行控制。例如,在一个转账操作中,可能需要先检查账户余额是否足够,然后再执行实际的转账操作。如果在这个过程中出现了任何错误,如余额不足,那么整个操作应该回滚,以保证数据的一致性。

2. Spring Boot中的事务管理

在Spring Boot中,事务管理主要通过@Transactional注解来实现。这个注解可以添加到类或方法级别,以指定事务的边界。Spring Boot还提供了TransactionManager接口,它负责事务的开始、提交和回滚。

2.1 基本用法

下面是一个简单的例子,展示了如何在Spring Boot中使用@Transactional注解:

/**
 * AccountServiceImpl
 * @author senfel
 * @version 1.0
 * @date 2024/8/27 14:30
 */
@Service
public class AccountServiceImpl implements AccountService {

    @Resource
    private AccountMapper accountMapper;
    /**
     * 转账
     * @param fromAccount
     * @param toAccount
     * @param amount
     * @author senfel
     * @date 2024/8/27 14:36
     * @return void
     */
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    @Override
    public void transfer(Long fromAccount, Long toAccount, BigDecimal amount) {
        Account from = accountMapper.selectById(fromAccount);
        Account to = accountMapper.selectById(toAccount);
        from.setBalance(from.getBalance().subtract(amount));
        to.setBalance(to.getBalance().add(amount));
        accountMapper.updateById(from);
        accountMapper.updateById(to);
    }
}

2.2 Nested Transactions的需求场景

在某些情况下,我们可能希望在转账的过程中加入一些额的步骤,就需要使用Nested Transactions来实现更细粒度的控制。

3. 实现Nested Transactions

3.1 使用Propagation.REQUIRED)/Propagation.NESTED)

默认情况下,@Transactional注解使用Propagation.REQUIRED传播行为。这意味着如果当前没有事务,就创建一个新的事务;如果已经存在一个事务,则加入到这个事务中;Propagation.NESTED则是开启一个当前事务的嵌套事务。但是,这并不能实现Nested Transactions。

3.2 嵌套事务REQUIRES_NEW与NESTED

使用@Transactional(propagation = Propagation.REQUIRES_NEW)
会强制在当前事务中创建一个新的事务,这个事务不影响外部事务,不受外部事务约束;
使用@Transactional(propagation = Propagation.NESTED)
会强制在当前事务中开启一个嵌套事务,这个事务生命周期依赖于外部事务,不影响外部事务,但受外部事务约束。
示例代码:

/**
 * AccountServiceImpl
 * @author senfel
 * @version 1.0
 * @date 2024/8/27 14:30
 */
@Service
public class AccountServiceImpl implements AccountService {

    @Resource
    private AccountMapper accountMapper;
    /**
     * transferByNew
     * @param fromAccount
     * @param toAccount
     * @param amount
     * @author senfel
     * @date 2024/8/27 14:36
     * @return void
     */
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    @Override
    public void transferByNew(Long fromAccount, Long toAccount, BigDecimal amount) {
        Account acc = accountMapper.selectById(fromAccount);
        if(acc.getBalance().compareTo(amount) < 0){
            throw new RuntimeException("转出账户余额不足");
        }
        //从service层获取bean调用方法开启事务,否则本类嵌套事务开启无效
        AccountServiceImpl bean = SpringUtil.getBean(AccountServiceImpl.class);
        bean.doTransferByNew(fromAccount, toAccount, amount);
        if(amount.compareTo(new BigDecimal("100")) == 0){
            //手动模拟异常
            throw new RuntimeException("转账金额不能为100");
        }
    }

    /**
     * transferByNested
     * @param fromAccount
     * @param toAccount
     * @param amount
     * @author senfel
     * @date 2024/8/27 15:19
     * @return void
     */
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    @Override
    public void transferByNested(Long fromAccount, Long toAccount, BigDecimal amount) {
        Account acc = accountMapper.selectById(fromAccount);
        if(acc.getBalance().compareTo(amount) < 0){
            throw new RuntimeException("转出账户余额不足");
        }
        //从service层获取bean调用方法开启事务,否则本类嵌套事务开启无效
        AccountServiceImpl bean = SpringUtil.getBean(AccountServiceImpl.class);
        bean.doTransferByNested(fromAccount, toAccount, amount);
        if(amount.compareTo(new BigDecimal("100")) == 0){
            //手动模拟异常
            throw new RuntimeException("转账金额不能为100");
        }
    }


    /**
     * doTransferByNested嵌套事务
     * @param fromAccount
     * @param toAccount
     * @param amount
     * @author senfel
     * @date 2024/8/27 14:42 
     * @return void
     */
    @Transactional(propagation = Propagation.NESTED)
    public void doTransferByNested(Long fromAccount, Long toAccount, BigDecimal amount) {
        Account from = accountMapper.selectById(fromAccount);
        Account to = accountMapper.selectById(toAccount);
        from.setBalance(from.getBalance().subtract(amount));
        to.setBalance(to.getBalance().add(amount));
        accountMapper.updateById(from);
        accountMapper.updateById(to);
    }

    /**
     * doTransferByNew开启一个新事物
     * @param fromAccount
     * @param toAccount
     * @param amount
     * @author senfel
     * @date 2024/8/27 15:15
     * @return void
     */
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void doTransferByNew(Long fromAccount, Long toAccount, BigDecimal amount) {
        Account from = accountMapper.selectById(fromAccount);
        Account to = accountMapper.selectById(toAccount);
        from.setBalance(from.getBalance().subtract(amount));
        to.setBalance(to.getBalance().add(amount));
        accountMapper.updateById(from);
        accountMapper.updateById(to);
    }
}

3.3 注意事项

嵌套事务的隔离性:REQUIRES_NEW不会受到外部事务的影响,但NESTED会受到外部事务影响。
回滚行为:如果嵌套事务失败并回滚,它不会影响外部事务的状态,前提是嵌套方法被catch住。
性能考虑:频繁地开启和关闭事务可能会导致性能下降。因此,在设计时应尽量减少嵌套事务的使用。

4. 测试Nested Transactions

为了验证Nested Transactions的正确性,我们可以编写单元测试来模拟不同的场景。
示例测试代码:

/**
 * NestedTransactionTest
 * @author senfel
 * @version 1.0
 * @date 2024/8/27 14:49
 */
@SpringBootTest
public class NestedTransactionTest {

    @Resource
    private AccountService accountService;
    @Resource
    private AccountMapper accountMapper;

    /**
     * testTransferNestedTransaction
     * @author senfel
     * @date 2024/8/27 15:36 
     * @return void
     */
    @Test
    public void testTransferNestedTransaction() {
        Long fromAccount = 1L;
        Long toAccount = 2L;
        BigDecimal amount = new BigDecimal("100");

        Account a1 = new Account(fromAccount, "A1",new BigDecimal("500"));
        Account b1 = new Account(toAccount,"B2", BigDecimal.ZERO);
        accountMapper.insert(a1);
        accountMapper.insert(b1);
        System.err.println("==========嵌套事务初始数据==============");
        System.err.println(JSONObject.toJSONString(a1));
        System.err.println(JSONObject.toJSONString(b1));
        System.err.println("==========嵌套事务REQUIRES_NEW==============");
        // 嵌套事务REQUIRES_NEW 测试
        try {
            accountService.transferByNew(fromAccount, toAccount, amount);
        }catch (Exception e){
            e.printStackTrace();
        }
        // 验证 嵌套事务REQUIRES_NEW 外部报错嵌套事务提交成功
        Account a2 = accountMapper.selectById(fromAccount);
        Account b2 = accountMapper.selectById(toAccount);
        System.err.println(JSONObject.toJSONString(a2));
        System.err.println(JSONObject.toJSONString(b2));

        System.err.println("==========嵌套事务NESTED==============");
        // 嵌套事务NESTED 测试
        try {
            accountService.transferByNested(fromAccount, toAccount, amount);
        }catch (Exception e){
            e.printStackTrace();
        }
        // 验证 嵌套事务NESTED 外部报错嵌套事务提交失败
        Account a3 = accountMapper.selectById(fromAccount);
        Account b3 = accountMapper.selectById(toAccount);
        System.err.println(JSONObject.toJSONString(a3));
        System.err.println(JSONObject.toJSONString(b3));
    }
}

执行结果:

嵌套事务初始数据====
{“balance”:500,“id”:1,“name”:“A1”}
{“balance”:0,“id”:2,“name”:“B2”}
嵌套事务REQUIRES_NEW====
{“balance”:400.00,“id”:1,“name”:“A1”}
{“balance”:100.00,“id”:2,“name”:“B2”}
嵌套事务NESTED====
{“balance”:400.00,“id”:1,“name”:“A1”}
{“balance”:100.00,“id”:2,“name”:“B2”}

5. 总结

Nested Transactions是Spring Boot中一项非常有用的功能,它可以帮助我们在复杂业务逻辑中实现更细粒度的控制。通过使用REQUIRES_NEW或者NESTED,我们可以轻松地在现有事务中创建新的事务传播机制,其中REQUIRES_NEW不受外部事务影响,NESTED则是会受到外部事务影响。所以,在实际的开发中我们也需要注意嵌套事务的局限性和潜在的性能问题,以确保应用程序的高效运行。

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

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

相关文章

upload-labs(Pass-18 ~ Pass-21)

1、Pass-18(条件竞争) 1、题目需要进行代码审计&#xff1a; <?php include ../config.php; include ../head.php; include ../menu.php;$is_upload false; $msg null;if(isset($_POST[submit])){$ext_arr array(jpg,png,gif);//白名单$file_name $_FILES[upload_fil…

OpenLayers3,地图探查功能实现

文章目录 一、前言二、代码实现三、总结 一、前言 图层探查&#xff0c;即对置于地图下方的图层进行一定范围的探查&#xff0c;以便用户查看到不易察觉的地理地况。本文基于OpenLayers3&#xff0c;实现地图探查的功能。 二、代码实现 <!DOCTYPE HTML PUBLIC "-//W…

滴滴拼车系统开发前景分析

滴滴拼车系统作为滴滴出行平台的一个重要组成部分&#xff0c;其开发前景分析如下&#xff1a; 市场需求&#xff1a;随着城市交通压力的增加和共享经济的普及&#xff0c;拼车服务因其便捷性和经济性受到越来越多用户的青睐&#xff0c;市场需求持续增长 。政策支持&#xf…

商业律师事务所借助 DocuSign 解决方案加快了 QES 和身份识别流程 | 电子签约律师事务解决方案

Roosbeh Karimi 是一位充满活力的年轻律师&#xff0c;他创办的商业律师事务所正引领着法律行业的数字化转型 KARIMI.legal 是一家总部位于柏林的商业律师事务所&#xff0c;专门从事商法、竞争法和法律技术集体诉讼。该商业律师事务所拥有一支由 11 名员工组成的团队&#xff…

音视频不同步问题总结

音视频同步的定义 指在视频播放过程中&#xff0c;图像和声音的播放时间保持一致&#xff0c;使得观众感觉到图像与声音是同时发生的。在实际的音视频 处理过程中&#xff0c;由于音频和视频的处理速度可能不同&#xff0c;或者由于网络传输的延迟&#xff0c;可能会导致音视…

地瓜直播间开播啦 | RDK X5-RWKV大模型部署实战

地瓜机器人新一代机器人开发者套件RDK X5&#xff0c;搭载旭日5智能计算方案&#xff0c;极简机器人开发体验&#xff0c;助力机器人开发一步通关&#xff08;更多产品信息请关注2024地瓜机器人开发者日暨新品发布活动&#xff0c;后续将在本公众号发布&#xff09;。 RWKV&…

零基础入门转录组数据分析——预后模型之lasso模型

零基础入门转录组数据分析——预后模型之lasso模型 目录 零基础入门转录组数据分析——预后模型之lasso模型1. 预后模型和lasso模型基础知识2. lasso预后模型&#xff08;Rstudio&#xff09;——代码实操2. 1 数据处理2. 2 构建lasso预后模型2. 3 提取Lasso预后基因2. 4 计算风…

Java框架myBatis(三)

一、特殊符号转义 特殊符号处理 在mybatis中的xml文件中&#xff0c;存在一些特殊的符号&#xff0c;比如&#xff1a;、"、&、<> 等&#xff0c;正常书写mybatis会报错&#xff0c;需要对这些符号进行转义。 具体转义如下所示&#xff1a; 特殊字符 转义字符…

图解 Elasticsearch 的 Fielddata Cache 使用与优化

1、难搞的 fielddata cache 在 ES 使用的几个内存缓存中&#xff0c;fielddata cache 算是一个让人头疼的家伙。 作为和 query cache 和 request cache 一样不受 GC 控制的内存使用者&#xff0c;fielddata cache 虽然也有 indices.fielddata.cache.size 的设置来阻止过度使用&…

vite-plugin-ejs:打包时报错:hook is not a function

现象&#xff1a;打包时提示hook is not a function 解决方法1&#xff1a; 在node_modules中找到vite-plugin-ejs的index.js&#xff0c;将handler修改为transform&#xff1a; 解决方法2&#xff1a; 使用vite --version命令查看本机的vite版本&#xff0c;根据插件的写法选…

WMS仓储管理系统的这些功能模块一定要做好

在当今物流行业迅猛发展的背景下&#xff0c;仓储管理的智能化升级已成为企业提升竞争力的关键一环。智能立体仓库系统的构建&#xff0c;正是这一趋势下的重要里程碑&#xff0c;它以高度自动化、精准化的货物处理能力&#xff0c;重新定义了仓储作业的标准。而这一切的核心驱…

CAD波浪线画法2

cad波浪线怎么画出来 - 软件自学网下面给大家介绍的是cad波浪线怎么画出来的方法&#xff0c;具体操作步骤如下&#xff1a;https://rjzxw.com/jiaocheng/18774.html这个是对的&#xff0c;适合多个版本

网络安全系统性学习路线「全文字详细介绍」

&#x1f91f; 基于入门网络安全打造的&#xff1a;&#x1f449;黑客&网络安全入门&进阶学习资源包 一、基础与准备 网络安全行业与法规 想要从事网络安全行业&#xff0c;必然要先对行业建立一个整体的认知&#xff0c;了解网络安全对于国家和社会的作用&#xff0…

C++学习笔记——最大的数

一、题目描述 二、代码 #include <iostream> using namespace std; double bijiao(double ca,double cb,double cc) {double* t &ca;if(*t < cb) t&cb;if(*t < cc) t&cc;return *t; } int main() {double a,b,c; cin >> a >> b >>…

聚鼎科技:新人开一家装饰画店铺怎么快速起店

在当下这个看重审美和个性表达的时代&#xff0c;开设一家装饰画店铺无疑是迎合市场的明智选择。对于新人来说&#xff0c;快速且有效地启动一家装饰画店铺并非易事&#xff0c;但通过遵循一些关键步骤&#xff0c;可以大大缩短起步时间并提高成功率。 进行市场调研&#xff0c…

[Meachines] [Medium] Bastard Drupal 7 Module Services-RCE+MS15-051权限提升

信息收集 IP AddressOpening Ports10.10.10.9TCP:80,135,49154 $ nmap -p- 10.10.10.9 --min-rate 1000 -sC -sV PORT STATE SERVICE VERSION 80/tcp open http Microsoft IIS httpd 7.5 | http-methods: |_ Potentially risky methods: TRACE | http-robots.…

如何轻松合并 PDF 文件

管理和组织电子文件是个人和专业人士的一项重要技能。组合 PDF 文件是一项常见任务&#xff0c;可以帮助增强您的工作流程&#xff0c;从而更好地共享信息、协作项目和维护整洁的数字工作流程。在这篇博文中&#xff0c;我们将探讨如何在笔记本电脑或计算机上轻松合并 PDF 文件…

异步任务的艺术:Bull应用详解

Bull 是一个强大的 Node.js 库&#xff0c;它基于 Redis 构建&#xff0c;为异步任务队列提供了简单而强大的解决方案。 它支持多种任务处理模式&#xff0c;包括延迟任务、重复任务和优先级队列&#xff0c;使得发送电子邮件、生成报告或处理图像等耗时操作变得轻而易举。Bull…

书生.浦江大模型实战训练营——(十四)MindSearch 快速部署

最近在学习书生.浦江大模型实战训练营&#xff0c;所有课程都免费&#xff0c;以关卡的形式学习&#xff0c;也比较有意思&#xff0c;提供免费的算力实战&#xff0c;真的很不错&#xff08;无广&#xff09;&#xff01;欢迎大家一起学习&#xff0c;打开LLM探索大门&#xf…

达梦数据库兼容Quartz定时框架

1、背景 近期项目中需要使用达梦数据库&#xff0c;现将mysql数据库切换为达梦数据库&#xff0c;其中兼容Quartz定时框架报错如下&#xff1a; 2、解决方案 2.1 起初配置完&#xff1a;达梦数据库驱动直接启动项目直接报错&#xff0c; 后面在yml中配置数据库表名前缀&…