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 происходит то, что обе операции применяются по очереди. Сначала операция удаления переводит объект компонента в состояние "удалено", затем операция сохранения возвращает удаленный объект в управляемый один. В результате функция не удаляется из базы данных.