事务的传播行为

发布时间:2023年12月17日

1. 事务的传播行为

事务的传播行为,可以通俗地理解为在软件程序中,当你在执行一个需要多个步骤的任务时,这些步骤是如何关联到一个整体的事务(一个保证数据完整性和一致性的操作单元)中的规则。这在数据库操作中尤为重要。让我通过一个简单的比喻来解释:

想象一下,你正在做一道复杂的菜肴,这道菜需要多个步骤:切菜、炒菜、调味等。每个步骤就像程序中的一个操作。

  1. REQUIRED(必需)

    • 如果你已经开始做这道菜(已经有一个事务在进行中),那么新的步骤(比如调味)就加入到这个正在做的菜里。
    • 如果你还没开始做菜(没有事务),那么你就开始一个新的菜(新事务)。
  2. REQUIRES_NEW(需要新的)

    • 不管你是否正在做这道菜,你都会重新开始一个新的菜(开始一个全新的事务)。就好比你暂停当前的烹饪,开一个新锅做另一道菜。
  3. SUPPORTS(支持)

    • 如果你已经在做这道菜,那么新的步骤就加入进来。
    • 但如果你还没开始,那么你就不做这道菜(不在事务中进行操作)。
  4. MANDATORY(强制性)

    • 这个步骤只能在你已经开始做菜的时候进行。如果你还没开始做菜,就不能执行这个步骤(如果没有事务就会报错)。
  5. NEVER(从不)

    • 这个步骤只能在你没有开始做菜的时候进行。如果你已经在做菜了,就不能执行这个步骤(如果已经在事务中就会报错)。
  6. NOT_SUPPORTED(不支持)

    • 这个步骤总是单独进行,与你是否正在做菜无关。即使你正在做菜,这个步骤也要单独完成。
  7. NESTED(嵌套)

    • 如果你正在做这道菜,这个步骤就像是在这个过程中添加一个子任务。如果子任务失败了,只有这个部分会被撤销,而整个菜不会受影响。
    • 如果你还没开始做菜,它就像是开始一个新的菜。

通过选择合适的传播行为,你可以确保你的烹饪(或程序中的数据操作)既高效又符合逻辑。这在处理需要多个步骤协作完成的复杂任务时非常重要。

解释和用途

  • REQUIRED:适用于大多数情况,确保方法在事务环境中运行。
  • SUPPORTS:用于可选事务场景,即方法可以在事务中也可以不在事务中运行。
  • MANDATORY:适用于必须在事务中执行的操作,确保调用者已经开启了事务。
  • REQUIRES_NEW:适用于需要独立于当前事务运行的场景,例如,需要保证操作不被当前事务影响。
  • NOT_SUPPORTED:用于不应该运行在事务环境中的操作,例如,可能会耗时较长的读操作。
  • NEVER:确保方法不在事务环境中运行,如果存在事务环境则会抛出异常。
  • NESTED:适用于需要执行独立事务但又是当前事务的一部分的场景。嵌套事务允许单独的提交或回滚。

选择正确的事务传播行为对于确保应用程序的正确性和效率至关重要。这通常取决于业务逻辑的具体需求和上下文。

2. 举例说明

1. REQUIRED(默认)

假设有两个服务类,OrderServicePaymentServiceOrderService 有一个方法 createOrder,而 PaymentService 有一个方法 processPayment

@Service
public class OrderService {

    @Autowired
    private PaymentService paymentService;

    @Transactional(propagation = Propagation.REQUIRED)
    public void createOrder(Order order) {
        // 逻辑创建订单
        // ...

        paymentService.processPayment(order.getPayment());
    }
}

@Service
public class PaymentService {

    @Transactional(propagation = Propagation.REQUIRED)
    public void processPayment(Payment payment) {
        // 逻辑处理支付
        // ...
    }
}

在这个例子中,如果 createOrder 在没有现有事务的情况下被调用,它将启动一个新的事务。同样,processPayment 也会在没有现有事务的情况下启动一个新事务。但如果 processPayment 是从 createOrder 中调用的,它将加入 createOrder 的事务。

2. REQUIRES_NEW

考虑一个日志记录的场景,其中无论主事务是否成功,日志记录都应该被执行。

@Service
public class AuditService {

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void auditAction(String action) {
        // 逻辑记录审计日志
        // ...
    }
}

无论调用 auditAction 的方法是否在事务中,auditAction 都会在自己的新事务中执行。这意味着即使主事务失败并回滚,日志记录仍然会被保存。

3. NESTED

假设一个用户更新操作,其中用户的基本信息和地址信息分别由不同的方法处理,而地址更新可能比较复杂,需要单独回滚。

@Service
public class UserService {

    @Autowired
    private AddressService addressService;

    @Transactional(propagation = Propagation.REQUIRED)
    public void updateUser(User user) {
        // 更新用户基本信息
        // ...

        addressService.updateAddress(user.getAddress());
    }
}

@Service
public class AddressService {

    @Transactional(propagation = Propagation.NESTED)
    public void updateAddress(Address address) {
        // 复杂的地址更新逻辑
        // ...
    }
}

在这里,updateUser 方法在自己的事务中运行。当调用 updateAddress 时,如果地址更新失败,只有地址更新的部分会回滚,而用户的基本信息更新仍然有效。

4. MANDATORY

在某些情况下,一个方法必须在事务的上下文中执行。

@Service
public class InventoryService {

    @Transactional(propagation = Propagation.MANDATORY)
    public void reduceStock(String itemId, int quantity) {
        // 减少库存数量
        // ...
    }
}

如果 reduceStock 被调用时没有现有事务,它将抛出异常,因为它需要在一个现有的事务中执行。

这些例子展示了如何根据不同的业务需求和逻辑选择合适的事务传播行为。通过这样的选择,可以确保事务的正确性和一致性,同时满足不同业务场景的需求。

文章来源:https://blog.csdn.net/m0_54187478/article/details/135045700
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。