2015-03-11 4 views
1

У меня возникла проблема с 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; 
} 

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

  1. Пользователь создает новый экземпляр Родитель, и создает новые экземпляры дочерних на основе шаблона по их выбору путем вызова метода createChildren(). Шаблон определяет количество и свойства созданных детей.
  2. Пользователь сохраняет родительский элемент, который каскадирует сохранение для детей.
  3. Пользователь замечает, что использованный шаблон был неправильным. Он изменяет шаблон и сохраняет его, что приводит к удалению старых детей и созданию новых.

Обычно удаление старых детей будет обрабатываться автоматически с помощью свойства 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:

Мы фактически клирингового детей от родителей, а также, это может быть немного неоднозначным изначально, так что я обновил фрагменты кода, чтобы понять.

ответ

2

Попробуйте удалить детей из родителя перед их удалением, таким образом, MERGE не может быть каскадирован для них, потому что они не находятся в коллекции родителя.

for (Child child : parent.getChildren()) { 
    // Have to run a find since the entities are detached 
    Child c = entityManager.find(Child.class, child.getId()); 
    parent.getChildren().remove(c); // ensure that the child is actually removed 
    entityManager.remove(c); 
} 

UPDATE

Я до сих пор считаю, что порядок операций является причиной проблем здесь, попробовать, если это работает

public void createNewChildren(Parent parent, Template template) { 
    for (Child child : parent.getChildren()) { 
    // Have to run a find since the entities are detached 
    Child c = entityManager.find(Child.class, child.getId()); 
    parent.getChildren().remove(c); // ensure that the child is actually removed 
    c.setParent(null); 
    entityManager.remove(c); 
    } 
    parent.createChildren(template); 
    entityManager.merge(parent); 
} 
+0

Я очистные ребенок от родителей, в createChildren(). Мой фрагмент кода, возможно, немного неясен в этом, я обновил его, чтобы сделать его явным. –

+0

Я обновил свой ответ. –

+0

Я попробовал точный заказ, который вы предложили, это не имело никакого эффекта. Я все еще получаю EntityNotFoundException. –

Смежные вопросы