SpringBoot嵌套事务详解及失效解决方案

2025-01-02 15:50

本文主要是介绍SpringBoot嵌套事务详解及失效解决方案,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

《SpringBoot嵌套事务详解及失效解决方案》在复杂的业务场景中,嵌套事务可以帮助我们更加精细地控制数据的一致性,然而,在SpringBoot中,如果嵌套事务的配置不当,可能会导致事务不生效的问题...

什么是嵌套事务?

嵌套事务是一种事务传播行为,允许在一个事务中嵌套另一个事务。Spring 提供了 PROPAGATION_NESTED 事务传播属性,用于实现嵌套事务。嵌套事务的特点是:

  • 依赖外层事务:嵌套事务的提交取决于外层事务。
  • 可以独立回滚:嵌套事务失败时,可以部分回滚,而不影响外层事务。

嵌套事务失效的原因

Spring 的事务管理基于 AOP 动态代理。当事务方法被直接调用(例如通过 this.method())时,不经过 Spring 的代理,事务功能会失效。

核心问题:

  • 内部方法调用不会触发 Spring 的代理逻辑。
  • 因此,事务注解如 @Trjavascriptansactional 无法生效。

示例代码如下:

@Service
public class ExampleService {
    @Autowired
    private DemoRepository demoRepository;
 
    @Transactional
    public void outerMethod() {
        saveOuter();
        this.innerMethod(); // 内部调用,事务不会生效
    }
 
    private void saveOuter() {
        DemoEntity entity = new DemoEntity();
        entity.setName("Outer");
        demoRepository.save(entity);
    }
 
    @Transactional(propagation = Propagation.NESTED)
    public void innerMethod() {
        DemoEntity entity = new DemoEntity();
        entity.setName("Inner");
        demoRepository.save(entity);
 
        // 模拟异常
        throw new RuntimeException("Inner transaction failed");
    }
}

在上面的代码中,outerMethod 调用了 innerMethod,但由于是通过 this 引用调用的,事务注解 @Transactional 不会生效。

嵌套事务的解决方案

为了解决嵌套事务失效的问题,我们可以通过以下方法确保方法调用走 Spring 的代理机制。

方案一:将嵌套事务方法提取到独立类

将 innerMethod 方法提取到一个新服务类中,确保通过 Spring 容器管理的代理类调用。

示例代码

  • 新建一个服务类处理嵌套事务:
@Service
public class InnerService {
    @Autowired
    private DemoRepository demoRepository;
 
    @Transactional(propagation = Propagation.NESTED)
    public void innerMethod() {
        DemoEntity entity = new DemoEntity();
        entity.setName("Inner");
        demoRepository.save(entity);
 
        // 模拟异常
        throw new RuntimeException("Inner transaction failed");
    }
}
  • 修改外层服务类:
@Service
public class ExampleService {
    @Autowired
    private InnerService innerService;
 
    @Autowired
    private DemoRepository demoRepository;
 
    @Transactional
    public void outerMethod() {
        saveOuter();
  php      innerService.innerMethod(); // 通过代理调用,事务生效
    }
 
    private void saveOuter() {
        DemoEntity entity = new DemoEntity();
        entity.setName("Outer");
        demoRepository.save(entity);
    }
}

这种方式最为常见,能够有效解决嵌套事务失效的问题。

方案二:使用 ApplicationContext 获取代理对象

通过 Spring 的 ApplicationContext 获取当前类的代理对象,确保事务方法调用通过代理进行。

示例代码

@Service
public class ExampleService {
    @Autowired
    private DemoRepository demoRepository;
 
    @Autowired
    private ApplicationContext applicationContext;
 
    @Transactional
    public void outerMethod() {
        saveOuter();
 
        // 获取自身代理
        ExampleService proxy = applicationContext.getBean(ExampleService.class);
        proxy.innerMethod(); // 通过代理调用,事务生效
    }
 
    private void saveOuter() {
        DemoEntity entity = new DemoEntity();
        entity.setName("Outer");
        demoRepository.save(entity);
    }
 
    @Transactional(propagation = Propagation.NESTED)
    public void innerMethod() {
        DemoEntity entity = new DemoEntity();
        entity.setName("Inner");
        demoRepository.save(http://www.chinasem.cnentity);
 
        // 模拟异常
        throw new RuntimeException("Inner transaction failed");
    }
}

方案三:使用 AopContext 获取代理对象

Spring 提供了 AopContext.currentProxy() 方法,可以在同一个类中获取当前类的代理对象。

示例代码

@Service
public class ExampleService {
    @Autowired
    private DemoRepository demoRepository;
 
    @Transactional
    public void outerMethod() {
        saveOuter();
 
        // 获取代理对象
        ExampleService proxy = (ExampleService) AopContext.currentProxy();
        proxy.innerMethod(); // 通过代理调用,事务生效
    }
 
    private void saveOuter() {
        DemoEntity entity = new DemoEntity();
        entity.setName("Outer");
        demoRepository.save(entity);
    }
 
    @Transactional(propagation = Propagatiphpon.NESTED)
    public void innerMethod() {
        DemoEntity entity = new DemoEntity();
        entity.setName("Inner");
        demoRepository.save(entity);
 
        // 模拟异常
        throw new RuntimeException("Inner transaction failed");
    }
}

使用该方案需要在配置中开启 exposeProxy

@Configuration
@EnableTransacphptionManagement(proxyTargetClass = true)
public class TransactionConfig {
}

总结

嵌套事务在 Spring Boot 中失效的主要原因是方法调用未通过 Spring 的代理机制。我们可以通过以下方式解决:

  1. 将嵌套事务方法提取到独立类(推荐方式)。
  2. 使用 ApplicationContext 获取代理对象
  3. 使用 AopContext 获取代理对象

选择合适的解决方案可以确保嵌套事务在复杂业务场景中正常工作,避免数据一致性问题。

到此这篇关于SpringBoot嵌套事务详解及失效解决方案的文章就介绍到这了,更多相关SpringBoot嵌套事务内容请搜索China编程(www.chinasem.cn)以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程China编程(www.chinasem.cn)!

这篇关于SpringBoot嵌套事务详解及失效解决方案的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1152898

相关文章

Java嵌套for循环优化方案分享

《Java嵌套for循环优化方案分享》介绍了Java中嵌套for循环的优化方法,包括减少循环次数、合并循环、使用更高效的数据结构、并行处理、预处理和缓存、算法优化、尽量减少对象创建以及本地变量优化,通... 目录Java 嵌套 for 循环优化方案1. 减少循环次数2. 合并循环3. 使用更高效的数据结构4

java两个List的交集,并集方式

《java两个List的交集,并集方式》文章主要介绍了Java中两个List的交集和并集的处理方法,推荐使用Apache的CollectionUtils工具类,因为它简单且不会改变原有集合,同时,文章... 目录Java两个List的交集,并集方法一方法二方法三总结java两个List的交集,并集方法一

Spring AI集成DeepSeek三步搞定Java智能应用的详细过程

《SpringAI集成DeepSeek三步搞定Java智能应用的详细过程》本文介绍了如何使用SpringAI集成DeepSeek,一个国内顶尖的多模态大模型,SpringAI提供了一套统一的接口,简... 目录DeepSeek 介绍Spring AI 是什么?Spring AI 的主要功能包括1、环境准备2

Spring AI集成DeepSeek实现流式输出的操作方法

《SpringAI集成DeepSeek实现流式输出的操作方法》本文介绍了如何在SpringBoot中使用Sse(Server-SentEvents)技术实现流式输出,后端使用SpringMVC中的S... 目录一、后端代码二、前端代码三、运行项目小天有话说题外话参考资料前面一篇文章我们实现了《Spring

Spring AI与DeepSeek实战一之快速打造智能对话应用

《SpringAI与DeepSeek实战一之快速打造智能对话应用》本文详细介绍了如何通过SpringAI框架集成DeepSeek大模型,实现普通对话和流式对话功能,步骤包括申请API-KEY、项目搭... 目录一、概述二、申请DeepSeek的API-KEY三、项目搭建3.1. 开发环境要求3.2. mav

Springboot的自动配置是什么及注意事项

《Springboot的自动配置是什么及注意事项》SpringBoot的自动配置(Auto-configuration)是指框架根据项目的依赖和应用程序的环境自动配置Spring应用上下文中的Bean... 目录核心概念:自动配置的关键特点:自动配置工作原理:示例:需要注意的点1.默认配置可能不适合所有场景

Nginx中location实现多条件匹配的方法详解

《Nginx中location实现多条件匹配的方法详解》在Nginx中,location指令用于匹配请求的URI,虽然location本身是基于单一匹配规则的,但可以通过多种方式实现多个条件的匹配逻辑... 目录1. 概述2. 实现多条件匹配的方式2.1 使用多个 location 块2.2 使用正则表达式

使用Apache POI在Java中实现Excel单元格的合并

《使用ApachePOI在Java中实现Excel单元格的合并》在日常工作中,Excel是一个不可或缺的工具,尤其是在处理大量数据时,本文将介绍如何使用ApachePOI库在Java中实现Excel... 目录工具类介绍工具类代码调用示例依赖配置总结在日常工作中,Excel 是一个不可或缺的工http://

Java8需要知道的4个函数式接口简单教程

《Java8需要知道的4个函数式接口简单教程》:本文主要介绍Java8中引入的函数式接口,包括Consumer、Supplier、Predicate和Function,以及它们的用法和特点,文中... 目录什么是函数是接口?Consumer接口定义核心特点注意事项常见用法1.基本用法2.结合andThen链

spring @EventListener 事件与监听的示例详解

《spring@EventListener事件与监听的示例详解》本文介绍了自定义Spring事件和监听器的方法,包括如何发布事件、监听事件以及如何处理异步事件,通过示例代码和日志,展示了事件的顺序... 目录1、自定义Application Event2、自定义监听3、测试4、源代码5、其他5.1 顺序执行