Java JPA 清除集合并添加新项目

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/24724152/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-14 14:28:57  来源:igfitidea点击:

JPA clear collection and add new items

javahibernatejpaormhibernate-mapping

提问by Dennis Thrys?e

I have a @OneToMany collection (list) that I would like to clear, and add new elements to in the same transaction.

我有一个@OneToMany 集合(列表),我想清除它,并在同一个事务中添加新元素。

Using

使用

collection.clear();
collection.add(new EntityB());

Simply adds the new instance, and never removes anything. I have orphanRemoval = truefor the collection field.

只需添加新实例,而不会删除任何内容。我有orphanRemoval = true收集领域。

ADDED:

添加:

// Parent entity
@OneToMany(mappedBy = "product", orphanRemoval = true)
private List<Feature> features = new ArrayList<>();

// Child entity
@ManyToOne(cascade = CascadeType.ALL)
private Product product;

// Clear and add attempt
product.getFeatures().clear();

Feature feature = new Feature(product, ls);
product.getFeatures().add(feature);

采纳答案by Dennis Thrys?e

Turns out that the actual solution was using a @JoinColumn annotation instead of the mappedBy="" parameter.

事实证明,实际的解决方案是使用 @JoinColumn 注释而不是 mappingBy="" 参数。

回答by Maciej Dobrowolski

In Section 2.9, Entity Relationships, the JPA 2.1 spec says:

在第 2.9 节实体关系中,JPA 2.1 规范说:

If the entity being orphaned is a detached, new, or removed entity, the semantics of orphanRemoval do not apply.

如果被孤立的实体是分离的、新的或移除的实体,则 orphanRemoval 的语义不适用。

Are you sure that your entity is managed in the persistence context when removing it from collection?

从集合中删除时,您确定您的实体在持久性上下文中进行管理吗?

You can fix it by using fetch=fetchType.EAGERor by fetch joins. Alternatively (it depends on your use case), it may be sufficent to set appropriate cascadeoption.

您可以通过使用fetch=fetchType.EAGER或来修复它fetch joins。或者(这取决于您的用例),设置适当的cascade选项可能就足够了。

回答by Andrei I

This really seems to be a bug in many versions of Hibernate. I have tested it with EclipseLink and it works there without problem.

这似乎是许多 Hibernate 版本中的一个错误。我已经用 EclipseLink 对其进行了测试,它在那里工作没有问题。

As workaround in Hibernate(tested in Hibernate 4.3.6-Final): Remove any cascading in the Featureentity and add CascadeType.PERSIST(or CascadeType.ALL) in the Productentity.

作为Hibernate 中的解决方法(在 Hibernate 4.3.6-Final 中测试):删除Feature实体中的任何级联并在实体中添加CascadeType.PERSIST(或CascadeType.ALL) Product

Just to make sure that it does not work, try the following:

只是为了确保它不起作用,请尝试以下操作:

EntityManager em = ...//fetch the entitymanager. If a Container-managed transaction, you already got it injected
em.getTransaction().begin();//only if resource-local persistence unit. Otherwise if JTA: open the transaction the JTA-specific way (if that was not already done by the container)
Product product = em.find(Product.class, productId);
for (Feature crtFeature : product.getFeatures()) {
    if (!em.contains(crtFeature)) {
       throw new RuntimeException("Feature is not managed, so removeOrpahns cannot work");
    }
}
product.getFeatures().clear();

Feature feature = new Feature(product, ls);
em.persist(feature);//you need this, as there is no cascading from Product to Feature.
product.getFeatures().add(feature);

em.getTransaction().commit();//if you work with a resource-local persistence unit. Otherwise if JTA: commit the transaction the JTA-specific way (if that was not already done by the container)

回答by Vlad Mihalcea

You try to clear only one side of the bidirectional association.

您尝试只清除双向关联的一侧。

So instead of:

所以而不是:

collection.clear();

As explained in this article, try clearing both sides and it should work:

本文所述,尝试清除双方,它应该可以工作:

for(Iterator<Feature> featureIterator = features.iterator(); 
    featureIterator.hasNext(); ) {
    Feature feature = featureIterator .next();
    feature.setProduct(null);
    featureIterator.remove();
}

Also, remove the cascade from @ManyToOneand move it to @OneToMany.

此外,从 中移除级联@ManyToOne并将其移至@OneToMany

Mind the unique constraints

注意独特的约束

However, if you have a unique constraint, this clear + addAnti-Pattern will not work since the INSERT action is executed before the DELETE one as explained in this article.

但是,如果您有唯一约束,则此clear + add反模式将不起作用,因为 INSERT 操作在 DELETE 操作之前执行,如本文所述

The proper way to do it is to check which entries need to be removed and just remove those. Then, add the new ones, and update the ones that got modified. This is how you do a collection merge properly.

正确的方法是检查哪些条目需要删除,然后删除它们。然后,添加新的,并更新修改过的。这就是您正确执行集合合并的方式。

回答by Medo42

I faced a similar problem recently. For me, the issue was that the orphans were still referenced from another managed entity, and there was a PERSIST cascading defined for that relationship:

我最近遇到了类似的问题。对我来说,问题是孤儿仍然被另一个托管实体引用,并且为该关系定义了一个 PERSIST 级联:

// Parent entity
@OneToMany(mappedBy = "product", orphanRemoval = true)
private List<Feature> features = new ArrayList<>();

// Child entity
@ManyToOne
private Product product;

@ManyToOne
private Description description;

// Another entity (let's say descriptions can be shared between features)
@OneToMany(mappedBy = "description", cascade = CascadeType.PERSIST)
private List<Feature> features = new ArrayList<>();

Assume that all of the involved entities are managed, i.e. loaded into the persistence context. Now we do the same thing as the OP:

假设所有涉及的实体都被管理,即加载到持久性上下文中。现在我们做与 OP 相同的事情:

// Clear and add attempt
product.getFeatures().clear();

Feature feature = new Feature(product, ls);
product.getFeatures().add(feature);

Philosophically, the issue here is that the object model becomes inconsistent if you only remove the Feature from the Product entity, but not from the Description entity. After all, you want the Feature to be removed, but it is still being referenced from other objects. Technically, what happens is that there are two conflicting cascadings going to the Feature entity, and the result may depend on the order in which they are applied.

从哲学上讲,这里的问题是,如果您只从 Product 实体中删除 Feature 而不是从 Description 实体中删除,则对象模型会变得不一致。毕竟,您希望 Feature 被删除,但它仍然被其他对象引用。从技术上讲,会发生两个冲突的级联到 Feature 实体,结果可能取决于它们的应用顺序。

Since the Feature was removed from the collection in product, the orphan removal applies, and the Feature entity transitions to "removed" state during the next flush, as stated in the JPA 2.1 spec (2.9). I added emphasis on the relevant parts:

由于 Feature 从产品集合中移除,孤立移除适用,并且 Feature 实体在下一次刷新期间转换为“已移除”状态,如 JPA 2.1 规范 (2.9) 中所述。我强调了相关部分:

Associations that are specified as OneToOne or OneToMany support use of the orphanRemoval option. The following behaviors apply when orphanRemoval is in effect:

  • If an entity that is the target of the relationship is removed from the relationship (by setting the relationship to null or removing the entity from the relationship collection), the remove operation will be applied to the entity being orphaned. The remove operation is applied at the time of the flush operation. The orphanRemoval functionality is intended for entities that are privately “owned” by their parent entity. Portable applications must otherwise not depend upon a specific order of removal, and must notreassign an entity that has been orphaned to another relationship or otherwise attempt to persist it. If the entity being orphaned is a detached, new, or removed entity, the semantics of orphanRemoval do not apply.

指定为 OneToOne 或 OneToMany 的关联支持使用 orphanRemoval 选项。当 orphanRemoval 生效时,以下行为适用:

  • 如果作为关系目标的实体从关系中移除(通过将关系设置为 null 或从关系集合中移除实体),移除操作将应用于被孤立的实体。删除操作在刷新操作时应用。orphanRemoval 功能适用于由其父实体私有“拥有”的实体。否则,便携式应用程序不得依赖于特定的删除顺序,并且不得将已孤立的实体重新分配给另一个关系或以其他方式尝试保留它。如果被孤立的实体是分离的、新的或移除的实体,则 orphanRemoval 的语义不适用。

However, the same Feature is still referenced from a Description entity, which has a PERSIST cascading towards the Feature. The JPA 2.1 spec says the following:

但是,仍然从描述实体引用相同的特征,该实体具有向特征级联的 PERSIST。JPA 2.1 规范说明如下:

The semantics of the flush operation, applied to an entity X are as follows:

  • If X is a managed entity, it is synchronized to the database.

    • For all entities Y referenced by a relationship from X, if the relationship to Y has been annotated with the cascade element value cascade=PERSIST or cascade=ALL, the persist operation is applied to Y.

应用于实体 X 的刷新操作的语义如下:

  • 如果 X 是受管实体,则将其同步到数据库。

    • 对于由来自 X 的关系引用的所有实体 Y,如果已使用级联元素值级联元素值级联 = PERSIST 或级联 = ALL 注释了与 Y 的关系,则对 Y 应用持久操作。

So this cascading will perform a "persist" operation on the Feature entity, even if we don't call em.persist() on the Description. It is enough for the Description to be managed when a flush is performed to trigger this persist cascading.

所以这个级联会对 Feature 实体执行“持久化”操作,即使我们没有在 Description 上调用 em.persist() 。当执行刷新以触发此持久级联时,管理描述就足够了。

This means we are doing exactly what the spec told us we shouldn't - performing orphan removal and persisting on the same entity. What seems to happen in practice in Hibernate is that both operations are applied in turn. First the remove operation makes the Feature entity transition to "removed" state, then the persist operation turns the removed entity back into a managed one. As a result, the Feature is not removed from the database.

这意味着我们正在做规范告诉我们不应该做的事情 - 执行孤立删除并在同一实体上持久化。在 Hibernate 的实践中似乎发生的是,这两个操作是轮流应用的。首先,删除操作使 Feature 实体转换为“已删除”状态,然后持久操作将已删除的实体恢复为托管实体。因此,该功能不会从数据库中删除。