浅谈Spring事务传播行为实战

浅谈Spring事务传播行为实战

在开发中,我们经常需要处理一些涉及到数据库的事务操作。Spring框架提供了完善的事务管理机制,可以很好的解决事务处理的问题。其中,事务传播行为定义了在方法嵌套调用中如何传播事务。

事务传播行为的定义

Spring中定义了7种事务传播行为:

  • REQUIRED:表示当前方法必须运行在事务内部。如果当前存在事务,则加入该事务中;如果不存在事务,则新建一个事务并加入。
  • SUPPORTS:表示当前方法不需要运行在事务内部。如果当前存在事务,则加入该事务中;如果不存在事务,则直接执行该方法。
  • MANDATORY:表示当前方法必须运行在事务内部。如果当前不存在事务,则抛出异常。
  • REQUIRES_NEW:表示当前方法必须新建一个事务。如果当前存在事务,则挂起该事务并新建一个事务;如果不存在事务,则新建一个事务并加入。
  • NOT_SUPPORTED:表示当前方法不应该运行在事务内部。如果当前存在事务,则挂起该事务;如果不存在事务,则直接执行该方法。
  • NEVER:表示当前方法不应该运行在事务内部。如果当前存在事务,则抛出异常;如果不存在事务,则直接执行该方法。
  • NESTED:表示当前方法必须运行在一个已经存在的事务中,但该事务可以由外部事务管理器创建一个内嵌事务或加入内嵌事务中,外部事务中出现异常会导致所有嵌套的事务都回滚,而内部事务中出现异常仅会影响内部事务的数据。

事务传播行为的选型

在选择合适的事务传播行为时,需要根据实际业务需求来决定,关键要考虑以下几个因素:

  • 配置中的优先级最高。
  • 调用者是否需要始终在同一事务中执行。
  • 如果当前事务不存在,是要发起一个新的事务,还是希望整个方法都在非事务环境下执行。
  • 如果当前事务存在,是否可以共享事务,还是希望开启一个新的事务。
  • 如果当前事务存在,是否希望为某一段代码单独开启一个新的独立事务。

实战示例一

假设有这么一个需求:在处理A账户的转账操作时,需要调用另一个服务对B账户进行入账操作。此时,如果转账操作发生异常,我们希望B账户的入账操作也回滚,如果入账操作发生异常,则希望转账操作不受影响。这时,我们可以使用REQUIRES_NEW传播行为。当转账方法调用B账户的入账方法时,开启一个新的事务,这样如果B账户的入账方法出现异常,只会影响到新开启的事务,而不会影响到之前的转账操作。

示例代码如下:

@Service
public class AccountService {

    @Autowired
    private AccountMapper accountMapper;

    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
    public void transfer(String from, String to, BigDecimal amount) throws Exception {
        // A账户扣款
        AccountDTO accountDTO = accountMapper.selectByUserId(from);
        if (accountDTO.getBalance().compareTo(amount) < 0) {
            throw new Exception("余额不足");
        }
        accountMapper.updateBalance(from, accountDTO.getBalance().subtract(amount));

        // 调用B账户入账服务
        try {
            insertB(from, to, amount);
        } catch (Exception e) {
            throw new Exception("B账户入账失败!");
        }

        // 打印转账详情
        System.out.println(from + " 转 " + to + " " + amount + " 元");
    }

    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
    public void insertB(String from, String to, BigDecimal amount) throws Exception {
        // B账户入账
        AccountDTO accountDTO = accountMapper.selectByUserId(to);
        accountMapper.updateBalance(to, accountDTO.getBalance().add(amount));

        // 打印入账详情
        System.out.println(to + " 入账 " + amount + " 元");
    }

}

实战示例二

假设有这么一个需求:开发一个秒杀服务,在秒杀活动开始后,用户只有30分钟的时间内支付,否则订单自动取消。此时,我们希望调用外部支付接口,并检查支付结果,如果支付成功,则提交订单,否则取消订单。但是,在检查支付结果和提交订单之间,可能存在多个线程同时进行支付,这时需要避免并发冲突。此时,我们可以使用NESTED传播行为。在检查支付结果和提交订单之间,我们开启一个嵌套事务,这样如果支付结果检查出错,只会回滚该事务,而不会影响整个订单事务的提交。

示例代码如下:

@Service
public class SeckillService {

    @Autowired
    private OrderMapper orderMapper;

    @Autowired
    private PaymentService paymentService;

    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
    public void createOrder(String orderId) throws Exception {
        // 创建订单
        orderMapper.insert(orderId, new Date());

        // 调用支付接口
        boolean success = paymentService.pay(orderId);
        if (success) {
            // 提交订单
            try {
                submitOrder(orderId);
            } catch (Exception e) {
                throw new Exception("提交订单失败!");
            }
        } else {
            // 取消订单
            cancelOrder(orderId);
        }
    }

    @Transactional(rollbackFor = Exception.class, propagation = Propagation.NESTED)
    public void submitOrder(String orderId) throws Exception {
        // 检查库存
        if (checkInventory(orderId)) {
            // 提交订单
            orderMapper.submit(orderId);
        } else {
            throw new Exception("库存不足!");
        }
    }

    @Transactional(rollbackFor = Exception.class, propagation = Propagation.NESTED)
    public void cancelOrder(String orderId) throws Exception {
        // 取消订单
        orderMapper.cancel(orderId);
    }
}

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:浅谈Spring事务传播行为实战 - Python技术站

(0)
上一篇 2023年5月19日
下一篇 2023年5月19日

相关文章

  • java中删除 数组中的指定元素方法

    当我们需要删除数组中指定元素时,可以通过以下步骤实现: 遍历数组,找到需要删除的元素; 将被删除元素后面的所有元素向前移动一位; 将数组末尾元素设为null或者0,以保证数组的正确长度。 这里提供两个示例: 示例1: 我们定义一个数组int[] arr = {1, 2, 3, 4, 5},现在我们需要删除元素2,实现代码如下: int[] arr = {1,…

    Java 2023年5月26日
    00
  • 如何编写Java单元测试?

    当我们编写Java代码时,单元测试是非常重要的一部分。它可以帮助我们在开发过程中就确定代码是否正确,而不必等到部署到实际环境中才发现问题。本篇文章将会给出针对Java代码的单元测试的完整攻略。 步骤一:选择合适的单元测试框架 在Java中,有很多单元测试框架可供选择,包括JUnit、TestNG、Spock等。其中,JUnit是最常用的框架之一。本文将以JU…

    Java 2023年5月11日
    00
  • 如何使用Java编译期注解?

    下面是关于“如何使用Java编译期注解”的完整使用攻略。 什么是编译期注解? 编译期注解是在Java编译期间处理的一种注解,它可以被编译器直接解释和处理。编译器可以识别和处理这些注解,并在编译期执行相应的操作。相比于运行时注解,编译期注解更加高效、可靠和安全。 如何使用Java编译期注解? 使用Java编译期注解需要按照以下步骤进行: 1. 定义注解类 首先…

    Java 2023年5月11日
    00
  • 在Java编程中定义方法

    在Java编程中定义方法,可以方便地对代码进行模块化,同时也可以提高代码的可读性和可维护性。下面,我将详细讲解Java编程中定义方法的完整攻略。 什么是方法? 在Java中,方法(Method)是一个独立的代码块,用于封装一些指令。它可以接受参数,处理数据以及返回值。我们可以在代码中通过调用方法来执行其中的指令。方法可以用于多处调用,提高了代码的复用性。 方…

    Java 2023年5月26日
    00
  • Spring Data JPA框架的Repository自定义实现详解

    下面就是关于Spring Data JPA框架的Repository自定义实现的详细攻略。 介绍 Spring Data JPA 是 Spring 框架的一部分,它提供了一种简单的方法来访问关系型数据库中的数据。它使用JPA规范来访问数据库,简化了与数据库的交互,大大减少了操作数据库的代码量。在 Spring Data JPA 中,我们可以使用 Reposi…

    Java 2023年6月3日
    00
  • 使用maven开发springboot项目时pom.xml常用配置(推荐)

    在使用Maven开发Spring Boot项目时,pom.xml文件是非常重要的配置文件。本文将详细讲解pom.xml文件中常用的配置,以及如何使用这些配置来构建Spring Boot项目。 1. 常用配置 以下是pom.xml文件中常用的配置: 1.1 项目信息 <groupId>com.example</groupId> <…

    Java 2023年5月15日
    00
  • Java跨平台原理与虚拟机相关简介

    Java跨平台原理与虚拟机相关简介 什么是Java跨平台? Java成为跨平台的编程语言,是因为Java源代码编译成字节码(Bytecode),而字节码可以在任何安装了Java运行环境(Java Runtime Environment,JRE)的计算机上运行。Java跨平台原理主要是因为Java虚拟机(Java Virtual Machine,JVM)的存在…

    Java 2023年5月24日
    00
  • 利用java实现一个客户信息管理系统

    利用Java实现客户信息管理系统攻略 系统设计思路 客户信息管理系统主要是为了方便企业记录并管理客户信息数据,并用于后续的数据分析和处理等工作。 在系统设计中,我们需要考虑以下几个方面: 数据库设计 客户信息管理系统需要存储大量的客户数据,因此需要设计合理的数据库结构。通常可以使用MySQL或者Oracle等关系型数据库进行实现。在设计数据库时,需要考虑到数…

    Java 2023年5月19日
    00
合作推广
合作推广
分享本页
返回顶部