JPA 乐观锁定:如果记录已更新,如何防止删除,反之亦然?

use*_*969 5 hibernate jpa optimistic-locking spring-data-jpa

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

场景1

  1. 用户A查看订单。

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

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

场景2

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

使用 JPA(Hibernate via Spring Data JPA),我尝试用来@Version实现这种乐观锁定行为:

@Entity
public class Order {

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

    @Version
    private Integer version;

    // many other fields
Run Code Online (Sandbox Code Playgroud)

删除时,UI 客户端向服务器提供订单 ID 列表以及每个订单 ID 的版本号。这篇文章(Spring Data JPA:删除乐观锁定语义)提到了一个标准解决方案:

if (entity.getVersion() != dto.getVersion()) {
    throw new OptimisticLockException("...");
}
Run Code Online (Sandbox Code Playgroud)

要使用这个,我必须

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

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

 delete from [Order] where orderId = ? and version = ? 
Run Code Online (Sandbox Code Playgroud)

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);
Run Code Online (Sandbox Code Playgroud)

方法2

添加 OrderRepository 方法:

int deleteByIdAndVersion(Long id, Integer version);

对于每个要删除的订单:

        int x = orderRepository.deleteByIdAndVersion(orderIdFromClient, versionFromClient);
        if (x==0) {
            throw new OptimisticLockException();
        }
Run Code Online (Sandbox Code Playgroud)