У меня возникла проблема с EntityManager.merge(), где слияние каскадируется на другие сущности, которые уже были удалены из базы данных. Скажем, у меня есть следующие объекты:JPA EntityManager.merge() attemps для каскадирования обновления для удаленных объектов
@Entity
public class Parent {
@OneToMany(cascade = CascadeType.ALL, orphanremoval = true, mappedBy = "parent")
private List<Child> children;
public void clearChildren() { children.clear(); }
public void createChildren(Template template) { ... }
}
@Entity
public class Child {
@ManyToOne
@JoinColumn(name = "parentId")
private Parent parent;
}
Ситуация, когда возникает проблема заключается в следующем:
- Пользователь создает новый экземпляр Родитель, и создает новые экземпляры дочерних на основе шаблона по их выбору путем вызова метода createChildren(). Шаблон определяет количество и свойства созданных детей.
- Пользователь сохраняет родительский элемент, который каскадирует сохранение для детей.
- Пользователь замечает, что использованный шаблон был неправильным. Он изменяет шаблон и сохраняет его, что приводит к удалению старых детей и созданию новых.
Обычно удаление старых детей будет обрабатываться автоматически с помощью свойства orphanRemoval, но Младенец компании имеет уникальный индекс с несколькими столбцов, и некоторые из новых детей созданы на основе нового шаблона может иметь одинаковые значения во всех столбцах индекса как некоторые из исходных детей. Когда изменения сбрасываются в базу данных, JPA выполняет вставки и обновления перед удалением (or at least Hibernate does), и возникает нарушение ограничения. Отложенные ограничения Oracle решат это, но мы также поддерживаем MS SQL, который AFAIK не поддерживает отложенные ограничения (исправьте меня, если я ошибаюсь).
Чтобы решить эту проблему, я вручную удаляю старых детей, очищаю изменения, создаю новых детей и сохраняю свои изменения. Ниже приведенный фрагмент кода показывает основные части того, что происходит сейчас. Из-за того, как работают наши рамки, объекты, переданные этому методу, всегда находятся в отдельном состоянии (что, я боюсь, является частью проблемы).
public void createNewChildren(Parent parent, Template template) {
for (Child child : parent.getChildren()) {
// Have to run a find since the entities are detached
entityManager.remove(entityManager.find(Child.class, child.getId()));
}
entityManager.flush();
parent.clearChildren();
parent.createChildren(template);
entityManager.merge(parent); // EntityNotFoundException is thrown
}
Последняя строка вызывает исключение, поскольку EntityManager пытается загрузить старые ребенок и объединить их, как хорошо, но не может, так как они уже удалены. Вопрос в том, Почему он пытается загрузить их в первую очередь? И что еще более важно, как я могу это предотвратить? Единственное, что приходит мне на ум, которое может вызвать это, - проблема устаревших кешей. Я не могу обновить родительский элемент, поскольку он может содержать другие несохраненные изменения, и они будут потеряны (плюс его отсоединение). Я попытался установить родительскую ссылку явно на null для каждого дочернего элемента, прежде чем удалять их, и я попытался выдворить старых детей из кэша второго уровня после их удаления. Ничего не помогло. Мы не изменили настройки кэша JPA каким-либо образом.
Мы используем Hibernate 4.3.5.
UPDATE:
Мы фактически клирингового детей от родителей, а также, это может быть немного неоднозначным изначально, так что я обновил фрагменты кода, чтобы понять.
Я очистные ребенок от родителей, в createChildren(). Мой фрагмент кода, возможно, немного неясен в этом, я обновил его, чтобы сделать его явным. –
Я обновил свой ответ. –
Я попробовал точный заказ, который вы предложили, это не имело никакого эффекта. Я все еще получаю EntityNotFoundException. –