防止删除级联到实体实例



考虑以下JPA实体:

@Entity @Table(name = "product") class Product { ... }
@Entity @Table(name = "stock") class Stock {
  @JoinColumn(name = "product_id", updatable = false)
  @ManyToOne(fetch = FetchType.LAZY)
  private Product product;
  @Column(name = "quantity")
  private Long quantity;
}
@Entity @Table(name = "cart") class Cart {
  @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "cart", orphanRemoval = true)
  private List<CartItem> items = new ArrayList<>();
  public void addItem(CartItem item) { items.add(item); }
  public void removeItem(CartItem item) { items.remove(item); }
}
@Entity @Table(name = "cart_item") class CartItem {
  @JoinColumn(name = "cart_id", updatable = false)
  @ManyToOne(fetch = FetchType.LAZY)
  private Cart cart;
  @JoinColumn(name = "product_id", updatable = false)
  @ManyToOne(fetch = FetchType.LAZY)
  private Product product;
  @Column(name = "quantity")
  private Long quantity;
  @JoinColumn(name = "stock_id", updatable = false)
  @ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
  private Stock stock;
  public void setQuantity(Long quantity) {
    final Long delta = this.quantity - Math.max(0L, quantity);
    this.quantity += delta;
    this.stock.setQuantity(this.stock.getQuantity() - delta);
    if(quantity < 1) { cart.removeItem(this); }
  }
}

注意从CartItemStock的关联。对这种关联进行了注释,使得更改购物车项目数量会在另一个方向上影响其可用库存,也就是说,如果购物车项目的数量增加,产品的可用库存数量就会减少,反之亦然。

这允许我触发cartRepository.save(cart),保存所有购物车项目并同时更新其库存(由于Cascade.ALLCartCartItem)。只要购物车项目的数量为非零,这就可以正常工作。

然而,当在调用cart.removeItem(item)之后调用cartRepository.save(cart)时,级联也会尝试删除购物车项目的库存,这不是目的。购物车项目应该被删除,但其相关库存应该简单地更新。有没有一种方法可以将更新从CartItem级联到Stock,但将CartItem上的级联删除作为Stock上的更新?

更改

class CartItem {
  @JoinColumn(name = "stock_id", updatable = false)
  @ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
  private Stock stock;
}

class CartItem {
  @JoinColumn(name = "stock_id", updatable = false)
  @ManyToOne(cascade = { CascadeType.MERGE, CascadeType.PERSIST }, fetch = FetchType.LAZY)
  private Stock stock;
}

CascadeType.ALL{ CascadeType.MERGE, CascadeType.PERSIST })解决了该问题。以前,调用cartItem.setQuantity(0)时执行以下SQL查询:

delete from cart_item where id=?
delete from stock where id=?

更改后,将根据需要执行以下SQL查询:

update stock set quantity=? where id=?
delete from cart_item where id=?

Github上提供了用于检查解决方案正确性的示例应用程序。

至少可以说,您的方法非常有问题。如果多个线程修改同一产品的数量,会发生什么?JPA不防止并发访问!即使在技术上不会遇到同步问题,在逻辑上也会出现,因为(持久)量要么会被错误的值覆盖(要么会得到OptimisticLockException)。

您应该避免这种头痛,将数量视为动态值,并使用select语句计算产品的当前数量。

如果您坚持缓存数量,则应该为此任务使用单独的服务,该服务使用适当的同步、并发映射等。

相关内容

  • 没有找到相关文章

最新更新