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

时间:2019-05-13 20:41:08

标签: hibernate jpa spring-data-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

更新

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

方法1

对于每个要删除的订单:

        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);

方法2

添加OrderRepository方法:

int deleteByIdAndVersion(Long id, Integer version);

对于每个要删除的订单:

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

0 个答案:

没有答案