首页 > 解决方案 > JPA Optimistic Locking:如果记录已更新,如何防止删除,反之亦然?

问题描述

我需要我的应用程序具有以下行为:

方案 1

  1. 用户 A 查看订单。

  2. 用户 B 查看相同的订单。

  3. 用户 A 删除订单。
  4. 用户 B 请求更新订单。这应该失败。

方案 2

  1. 用户 A 查看订单
  2. 用户 B 查看相同的订单
  3. 用户 A 更新订单。
  4. 用户 B 请求删除订单。这应该失败。

使用 JPA(通过 Spring Data JPA 休眠),我试图用它@Version来实现这种乐观的锁定行为:

@Entity
public class Order {

    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    private Long id;

    @Version
    private Integer version;

    // many other fields

删除时,UI 客户端向服务器提供一个订单 ID 列表以及每个订单 ID 的版本号。这个 [post( Spring Data JPA: Delete Optimistic Locking semantics ) 提到了一个标准的解决方案:

if (entity.getVersion() != dto.getVersion()) {
    throw new OptimisticLockException("...");
}

要使用它,我必须

  1. 从数据库中查找实体使用来自客户端的订单 ID
  2. 将实体版本与客户端的 DTO 版本进行比较
  3. 执行删除。

问题是在第 2 步,实体和 DTO 版本可能相同。但是在第 3 步,版本可能会有所不同。有没有办法让休眠执行检查和更新作为单个原子操作,例如:

 delete from [Order] where orderId = ? and version = ? 

StaleObjectStateException如果没有删除,则抛出。

更新

我发现了两种应该可行的方法。这两种方法之一有问题吗?第二种方法涉及较少的数据库访问。客户通常一次只发送一个要删除的订单,因此这里的性能应该不是问题。

方法一

对于要删除的每个订单:

        Order order = orderRepository.findById(
                orderIdFromClient).orElseThrow(() ->
            new OptimisticLockException());

        if (!order.getVersion().equals(versionFromClient)) {
            throw new OptimisticLockException();
        }

        // We now know the managed entity has the same version
        // as the requested version. If some other transaction
        // has changed the entity, Hibernate will rollback and
        // throw OptimisticLockException.
        orderRepository.delete(order);

方法二

添加一个 OrderRepository 方法:

int deleteByIdAndVersion(Long id, Integer version);

对于要删除的每个订单:

        int x = orderRepository.deleteByIdAndVersion(orderIdFromClient, versionFromClient);
        if (x==0) {
            throw new OptimisticLockException();
        }

标签: hibernatejpaspring-data-jpaoptimistic-locking

解决方案


推荐阅读