java - Spring Data JPA - 如何保留任何现有的嵌套对象
问题描述
使用 Springboot 2.4.0,我正在尝试开发一个简单的模块来管理实体之间的典型关系Product
和Order
(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;
因为我不需要它,所以现在我省略了实体中的一个orders
字段。Product
在此关系之前,我有一个用于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.gallelloit.productcrud.model.Product;嵌套异常是 java.lang.IllegalStateException: org.hibernate.TransientObjectException: 对象引用了一个未保存的瞬态实例 - 在刷新之前保存瞬态实例:com.gallelloit.productcrud.model.Product
- 我为 CascadeType 设置的任何其他值(包括
CascadeType.ALL
),我得到相反的结果:新产品被保存并链接到新订单,但对于现有产品,我收到此错误:
传递给持久化的分离实体:com.gallelloit.productcrud.model.Product;嵌套异常是 org.hibernate.PersistentObjectException:分离的实体传递给坚持:com.gallelloit.productcrud.model.Product
我知道我可以“手动”获取每个产品,并在 之前保存它们order.save()
,但我想知道 Spring Data 是否为您做到了。
任何的想法?
解决方案
在这种情况下,依赖CascadeType.MERGE
是不安全的,因为它Product
也会更新关联的 s,这意味着您可能会无意中覆盖现有Product
数据。
您可以通过要求 JPA 为现有实体创建代理而不是实际从数据库中获取它们的关联状态来优化现有产品的获取。这可以使用JpaRepository.getOne()
. 你需要这样的东西:
order.setProducts(order.getProducts()
.map(product -> product.getId() == null ? productRepository.save(product) : productRepository.getOne(product.getId())
.collect(Collectors.toList());
如果您决定使用CascadeType.PERSIST
or CascadeType.ALL
,您当然可以productRepository.save(product)
用 just替换product
。CascadeType.ALL
但是,对于 没有意义@ManyToMany
,因为它暗示CascadeType.REMOVE
,您肯定不想要。
另外,我假设保存订单的逻辑发生在事务上下文中,对吗?
推荐阅读
- javascript - Angular 中带有 ngModel 和多个字段的自定义组件
- javascript - ES6:如何在回调中使用等待
- javascript - 如何获取不同集合中引用的数据
- php - 如何使用提交按钮从表中删除 1 行?
- android - MainActivity中如何设置DialogFragment的onDismissListener?
- react-native - 关于 React Native 中带有列表的 map() 函数的问题
- c# - 如何使用复选框winform c#创建序列
- graph - 如何截断或删除整个 neo4j 图?
- spring - graphiQL 是否支持弹簧安全性?添加弹簧安全性后,graphiQL UI 无法正常工作
- java - 如何将 Canvas 从另一个类添加到 JFrame?