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

Java JPA清除集合并添加新项目,java,hibernate,jpa,orm,hibernate-mapping,Java,Hibernate,Jpa,Orm,Hibernate Mapping,我有一个@OneToMany集合(列表),我想清除它,并在同一事务中添加新元素 使用 collection.clear(); collection.add(new EntityB()); 只需添加新实例,而从不删除任何内容。我为集合字段设置了orphaneremovation=true 增加: // Parent entity @OneToMany(mappedBy = "product", orphanRemoval = true) private List<Feature> f

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

使用

collection.clear();
collection.add(new EntityB());
只需添加新实例,而从不删除任何内容。我为集合字段设置了
orphaneremovation=true

增加:

// 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);
//父实体
@OneToMany(mappedBy=“product”,or=true)
private List features=new ArrayList();
//子实体
@多通(级联=级联类型.ALL)
私人产品;
//清除并添加尝试
product.getFeatures().clear();
特征=新特征(产品,ls);
product.getFeatures().add(feature);

在第2.9节“实体关系”中,JPA 2.1规范规定:

如果孤立的实体是分离的、新的或删除的实体, 删除的语义不适用

从集合中删除实体时,是否确实在持久性上下文中对其进行管理


您可以使用
fetch=fetchType.EAGER
fetch连接来修复它。或者(取决于您的用例),设置适当的
级联
选项就足够了。

这在许多版本的Hibernate中似乎确实是一个bug。我用EclipseLink测试过它,它在那里工作没有问题

作为Hibernate中的解决方法(在Hibernate 4.3.6-Final中测试):删除
功能
实体中的任何级联,并在
产品
实体中添加
级联类型。将
持久化(或
级联类型。所有

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

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)

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

因此,不是:

collection.clear();
尝试清除两侧,它应该可以工作:

for(Iterator<Feature> featureIterator = features.iterator(); 
    featureIterator.hasNext(); ) {
    Feature feature = featureIterator .next();
    feature.setProduct(null);
    featureIterator.remove();
}
for(迭代器特性迭代器=features.Iterator();
featureIterator.hasNext();){
Feature=featureIterator.next();
feature.setProduct(空);
featureIterator.remove();
}
另外,从
@manytone
中删除级联,并将其移动到
@OneToMany

注意独特的限制 但是,如果您有唯一的约束,则此
clear+add
反模式将不起作用,因为插入操作是在删除操作之前执行的


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

结果表明,实际的解决方案是使用@JoinColumn注释,而不是mappedBy=”“参数。

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

// 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<>();
从理论上讲,这里的问题是,如果只从产品实体中删除特性,而不从描述实体中删除特性,那么对象模型就会变得不一致。毕竟,您希望删除该特征,但它仍在从其他对象引用。从技术上讲,发生的情况是,有两个相互冲突的cascading进入特征实体,结果可能取决于它们的应用顺序

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

指定为OneToOne或OneToMany支持的关联使用 删除选项的一部分。以下行为适用于以下情况: 孤儿院搬迁生效:

  • 如果一个实体是 从关系中删除关系(通过设置 将关系设置为null或从关系中删除实体 集合),删除操作将应用于要删除的实体 孤儿清除操作在冲洗时应用 操作。孤立删除功能适用于实体 由其母公司私人“拥有”的。便携式 否则,应用程序不得依赖于特定的 删除,并且不得重新分配已孤立到的实体 另一个关系或尝试保持它。如果实体 孤立是一个分离的、新的或删除的实体,其语义是 删除不适用
但是,相同的特征仍然从描述实体中引用,描述实体具有向特征的持久级联。JPA 2.1规范说明如下:

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)
应用于实体X的刷新操作的语义如下 如下:

  • 如果X是一个托管实体,它将同步到 数据库

    • 对于由X的关系引用的所有实体Y,如果 与Y的关系已使用cascade元素进行了注释 值cascade=PERSIST或cascade=ALL,将应用PERSIST操作 对Y
因此,这种级联将对特征实体执行“持久化”操作,即使我们不对描述调用em.persist()。当执行刷新以触发此持久级联时,对描述进行管理就足够了


这意味着我们正在做规范告诉我们不应该做的事情——在同一个实体上执行孤立删除和持久化。在Hibernate中实际发生的情况似乎是,这两个操作依次应用。首先,删除操作将要素实体转换为“删除”状态,然后持久化操作将删除的实体转换回托管实体。因此,该功能不会从数据库中删除。

我尝试了许多级联选项。我也试着抓取。我的目标是删除集合中以前的所有实体,并添加一些新实体。但是没有一个被删除,即使是ne