Spring Data JPA-如何持久化任何现有或不存在的嵌套对象



使用Springboot 2.4.0,我试图开发一个简单的模块来管理实体ProductOrder之间的概念关系(ManyToMany关系(。

在订单中,我有这个products字段:

@ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
@JoinTable(
name="products_orders",
joinColumns=@JoinColumn(name="order_id"),
inverseJoinColumns=@JoinColumn(name="product_id")
)
private List<Product> products;

由于我不需要它,现在我省略了Product实体中的orders字段。

在此关系之前,我有一个用于Product实体的CRUD,并且正在正常运行,因此我可以通过产品存储库插入产品:

public interface ProductRepository extends JpaRepository<Product, Integer>

我需要下一个订单,里面有很多产品。我有这个代码:

List<Products> myListOfProducts = new ArrayList<>();
myListOfProducts.add(previouslyExistingProduct);
myListOfProducts.add(nonExistingProduct);
Order myorder = new Order(myListOfProducts, "mail", LocalDateTime.now());
myorder.save();

问题是:是否可以使用保存新订单的前一个代码以及链接到它的产品(保存现有和非现有产品(?

这是订单库:

public interface OrderRepository extends JpaRepository<Order, Integer>

这就是我目前所得到的:

  • 如果我将CascadeType更改为CascadeType.MERGE,我只能将现有产品链接到新订单,但对于新产品,我会收到以下错误:

org.hibernate.TransientObjectException:对象引用未保存的瞬态实例-在刷新前保存瞬态实例:com.gallloit.productcrud.model.Product;嵌套异常为java.lang.IllegalStateException:org.hibernate.TransientObjectException:对象引用未保存的瞬态实例-在刷新前保存瞬态实例:com.gallloit.productcrud.model.Product

  • 我给CascadeType(包括CascadeType.ALL(设置的任何其他值,我都会得到相反的结果:新产品被保存并链接到新订单,但对于现有产品,我会得到以下错误:

传递到持久化的分离实体:com.gallloit.productcrud.model.Product;嵌套异常为org.hibernate.PersistentObjectException:已分离的实体传递给persistent:com.gallloit.productcrud.model.Product

我知道我能做到"手动";获取每个产品,并在order.save()之前保存它们,但我想知道Spring Data是否为您做到了这一点。

知道吗?

在这种情况下,依赖CascadeType.MERGE是不安全的,因为它还会更新相关的Product,这意味着您可能会无意中覆盖现有的Product数据。

您可以通过要求JPA为现有实体创建代理来优化现有产品的获取,而不是从DB中实际获取它们的关联状态。这可以使用JpaRepository.getOne()来完成。你需要像这样的东西

order.setProducts(order.getProducts()
.map(product -> product.getId() == null ? productRepository.save(product) : productRepository.getOne(product.getId())
.collect(Collectors.toList());

如果您决定使用CascadeType.PERSISTCascadeType.ALL,您当然可以用product替换productRepository.save(product)。然而,CascadeType.ALL@ManyToMany来说没有意义,因为它意味着CascadeType.REMOVE,而你肯定不想要它。

此外,我假设保存订单的逻辑发生在事务上下文中,对吧?

最新更新