JPA очистить коллекцию и добавить новые элементы

у меня есть коллекция @OneToMany (список), которую я хотел бы очистить, и добавить новые элементы в ту же транзакцию.

используя

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

просто добавляет новый экземпляр, и никогда ничего не удаляет. У меня orphanRemoval = 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);

5 ответов


вы пытаетесь очистить только одну сторону двунаправленные ассоциации.

так вместо:

collection.clear();

как поясняется в в этой статье, попробуйте очистить обе стороны, и это должно работать:

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

кроме того, удалите каскад из @ManyToOne и переместите его в @OneToMany.

виду уникальности

однако, если у вас есть уникальное ограничение, это clear + add Anti-Pattern не будет работать, так как действие вставки выполняется перед удалением, как описано в в этой статье.

правильный способ сделать это-проверить, какие записи необходимо удалить, и просто удалить их. Затем добавьте новые и обновите те, которые были изменены. Это то, как вы делаете слияние коллекции правильно.


оказывается, что фактическое решение использовало аннотацию @JoinColumn вместо параметра mappedBy="".


это действительно кажется ошибкой во многих версиях Hibernate. Я протестировал его с EclipseLink, и он работает там без проблем.

As обходной путь в спящем режиме (протестировано в Hibernate 4.3.6-Final): удалите все каскады в Feature объект и добавить CascadeType.PERSIST (или CascadeType.ALL) в Product сущности.

просто чтобы убедиться, что он не работает, попробуйте следующее:

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)

В разделе 2.9, отношения сущностей, спецификация JPA 2.1 говорит:

если потерянный объект является обособленным, новым или удаленным объектом, семантика orphanRemoval не применяется.

вы уверены, что ваша сущность управляется в контексте персистентности при удалении ее из коллекции?

вы можете исправить это с помощью fetch=fetchType.EAGER или fetch joins. В качестве альтернативы (это зависит от вашего варианта использования), может быть достаточно установить соответствующий .


я столкнулся с подобной проблемой. Для меня проблема заключалась в том, что на сирот все еще ссылались из другого управляемого объекта, и для этих отношений был определен постоянный каскад:

// 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<>();

предположим, что все вовлеченные сущности управляются, т. е. загружаются в контекст персистентности. Теперь мы делаем то же самое, что и OP:

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

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

философски проблема здесь в том, что объектная модель становится непоследовательной, если вы только удаляете функцию из сущности Product, но не из сущности Description. В конце концов, вы хотите, чтобы функция была удалена, но на нее все еще ссылаются из других объектов. Технически происходит так, что к объекту объекта идут два конфликтующих каскада, и результат может зависеть от порядка их применения.

поскольку функция была удалена из коллекции в продукте, применяется удаление сироты, и сущность функции переходит в состояние "удалено" во время следующий флеш, как указано в спецификации JPA 2.1 (2.9). Я добавил акцент на соответствующие части:

ассоциации, которые указаны как использование поддержки OneToOne или OneToMany опции orphanRemoval. Следующие действия применяются, когда orphanRemoval в силу:

  • если объект, который является целью связь удаляется из связи (путем установки отношение к null или удаление сущности из отношения collection), операция удаления будет применена к сущности осиротевший. операция удаления применяется во время промывки операция. Функциональность orphanRemoval предназначена для сущностей они находятся в частной собственности их материнской компании. Портативный в противном случае заявки не должны зависеть от конкретного порядка удаление и не должен переназначить объект, который был потерян в другие отношения или в противном случае попытайтесь упорствуйте. Если сущность быть осиротевшим-это обособленная, новая или удаленная сущность, семантика orphanRemoval не применяются.

однако та же функция по-прежнему ссылается на объект описания, который имеет постоянный каскадный переход к функции. Спецификация JPA 2.1 гласит следующее:

семантика операции flush, применяемая к сущности X, выглядит следующим образом следует:

  • Если X управляемый объект, синхронизируется с база данных.

    • для всех сущностей Y, на которые ссылается отношение из X, если отношение к Y было аннотировано элементом cascade значение cascade=PERSIST или cascade=ALL, применяется операция persist Я.

таким образом, этот каскад будет выполнять операцию "persist" для объекта Feature, даже если мы не вызываем em.persist () в описании. Это достаточно для описания, чтобы управлять, когда выполняется сброс, чтобы вызвать это сохранение каскадирования.

это означает, что мы делаем именно то, что спецификация сказала нам, что мы не должны - выполнение удаления сироты и упорство на том же самом объекте. На практике в Hibernate происходит то, что обе операции применяются по очереди. Сначала операция удаления переводит объект компонента в состояние "удалено", затем операция сохранения возвращает удаленный объект в управляемый один. В результате функция не удаляется из базы данных.