Hibernate 防止delete级联到实体实例

Hibernate 防止delete级联到实体实例,hibernate,spring-data-jpa,jpa-2.1,hibernate-cascade,Hibernate,Spring Data Jpa,Jpa 2.1,Hibernate Cascade,考虑以下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 = "q

考虑以下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); }
  }
}
@Entity@Table(name=“product”)类产品{…}
@实体@Table(name=“stock”)类股票{
@JoinColumn(name=“product\u id”,updateable=false)
@manytone(fetch=FetchType.LAZY)
私人产品;
@列(名称=“数量”)
私人长数量;
}
@实体@Table(name=“cart”)类购物车{
@OneToMany(cascade=CascadeType.ALL,fetch=FetchType.LAZY,mappedBy=“cart”,orphan=true)
私有列表项=新的ArrayList();
public void addItem(CartItem){items.add(item);}
public void removietem(CartItem){items.remove(item);}
}
@实体@Table(name=“cart\u item”)类CartItem{
@JoinColumn(name=“cart\u id”,updateable=false)
@manytone(fetch=FetchType.LAZY)
私家车;
@JoinColumn(name=“product\u id”,updateable=false)
@manytone(fetch=FetchType.LAZY)
私人产品;
@列(名称=“数量”)
私人长数量;
@JoinColumn(name=“stock\u id”,updateable=false)
@manytone(cascade=CascadeType.ALL,fetch=FetchType.LAZY)
私人股;
公共无效设置数量(长数量){
最终长增量=this.quantity-数学最大值(0升,数量);
这个数量+=增量;
this.stock.setQuantity(this.stock.getQuantity()-delta);
如果(数量<1){cart.removietem(this);}
}
}
注意从
CartItem
Stock
的关联。对该关联进行了注释,以便更改购物车项目数量会在另一个方向上影响其可用库存,也就是说,如果购物车项目数量增加,则产品的可用库存数量减少,反之亦然

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


但是,当调用
cart.removietem(item)
后调用
cartRepository.save(cart)
时,级联也会尝试删除购物车项目的库存,这不是目的。应删除购物车项目,但只需更新其关联库存即可。是否有一种方法可以将更新从
CartItem
级联到
Stock
,但在
CartItem
上级联删除作为
Stock
上的更新?

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

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

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

class CartItem {
  @JoinColumn(name = "stock_id", updatable = false)
  @ManyToOne(cascade = CascadeType.ALL, 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=?
示例应用程序用于检查解决方案的正确性

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