浅谈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日

相关文章

  • @Accessors 注解参数

    @Accessors 注解参数经常会在实体类上看到,记录一下,方便以后复习 @Accessors注解的作用:当属性字段在生成 getter 和 setter 方法时,做一些相关的设置。 @Accessors 共有三个属性,分别是 fluent,chain,prefix fluent 属性 不写默认为false,当该值为 true 时,对应字段的 getter…

    Java 2023年5月9日
    00
  • 优雅地在Java 8中处理异常的方法详解

    下面是“优雅地在Java 8中处理异常的方法详解”的完整攻略。 1. 为什么要优雅地处理异常? 在Java编程中,异常处理是不可避免的。良好的异常处理可以提高代码的可读性和可维护性。而不良的异常处理则会导致代码臃肿且难以维护。因此,我们需要一个优雅的方式来处理异常。 2. Java 8中的新特性 Java 8中引入了Lambda表达式和Optional类,这…

    Java 2023年5月26日
    00
  • Tomcat环境变量如何配置

    Tomcat是一个用于Java应用程序的Web服务器和Servlet容器。在使用Tomcat的过程中,为了保证Web应用程序的正常运行,需要正确地配置Tomcat环境变量。下面是配置Tomcat环境变量的完整攻略: 1. 下载和安装Tomcat 在开始配置Tomcat环境变量之前,我们首先需要下载和安装Tomcat。Tomcat的下载地址为:https://…

    Java 2023年5月19日
    00
  • Java简单计算两个日期月数差的方法

    Java计算两个日期月数差的方法可以分为以下几个步骤: 将两个日期按照年、月、日拆分成年、月、日分别存储; 计算两个日期之间相差的总月数以及剩余天数; 根据剩余天数是否大于零进行判断,如果是则月数加一。 代码实现如下: import java.time.LocalDate; import java.time.Period; public class Date…

    Java 2023年5月20日
    00
  • JSON 与对象、集合之间的转换的示例

    JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,常用于前后端数据传输。在JavaScript中,可以轻松将JSON格式的数据存储在对象或集合中,也可以将对象或者集合转换为JSON格式的数据。下面,我们通过两个示例来详细讲解JSON与对象、集合之间的转换攻略。 示例一:JSON字符串转对象 我们假设有如下JSON字符…

    Java 2023年5月26日
    00
  • 【深度思考】聊聊JDK动态代理原理

    1. 示例 首先,定义一个接口: public interface Staff { void work(); } 然后,新增一个类并实现上面的接口: public class Coder implements Staff { @Override public void work() { System.out.println(“认真写bug……”); } } …

    Java 2023年4月17日
    00
  • java实现猜拳游戏试题

    下面我将详细讲解“java实现猜拳游戏试题”的完整攻略。 1. 确定游戏规则 在开始编写程序之前,需要先确定猜拳游戏的规则。通常猜拳游戏有剪刀、石头和布三种手势,其中剪刀克制布,布克制石头,石头克制剪刀。参与游戏的两个玩家选择其中一种手势,如果两个玩家选择的手势相同,则为平局;否则根据手势的胜负关系判断胜负,并输出胜负结果。 2. 编写程序 2.1. 实现游…

    Java 2023年5月23日
    00
  • JavaWeb文件上传开发实例

    JavaWeb文件上传开发实例 在JavaWeb开发中,文件上传是一个常见的功能。本篇文章将为大家介绍如何在JavaWeb项目中实现文件上传的功能。 1. 前置条件 在开发文件上传功能前,需要先了解JavaWeb中FileUpload组件。FileUpload组件是Apache提供的一个工具包,用于在Web应用中处理文件上传功能。我们需要从官方网站上下载Fi…

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