Почему слияние не каскадируется в отношениях от одного до многих
Мой вопрос здесь почти аналогичен моему другому вопросу Явное удаление по отношениям JPA
но я подумал о том, чтобы упростить его, чтобы гарантировать более подробные ответы.
Представьте, что у меня есть отношения OneToMany между родителем и дочерним элементом.
@Entity
public class Parent {
private String name;
@OneToMany(mappedBy="owner",cascade=CascadeType.ALL, fetch=FetchType.EAGER)
private List<Child> children;
}
@Entity
public class Child {
private String name;
@ManyToOne
private Parent owner;
}
В моем пользовательском интерфейсе я показываю список родителей. Пользователь может выбрать одного родителя, и он может редактировать список своих детей.
Чтобы показать это, я использовал переменную currentParent для представления текущего родительского объекта.
Пользователь может выбрать для добавления/редактирования имени детей.
После нажатия кнопки я сохраню список новых детей с помощью вызова EJB.
@ViewScoped
public class MyBean(){
private Parent currentParent;
@EJB
private MyEJB myEJB;
public void saveChanges(){
myEJB.save(currentParent);
}
}
Мой вызов EJB выглядит следующим образом.
@Stateless
public class MyEJB{
@PersistenceContext(unitName = "MyPU")
private EntityManager em;
public void save(Parent parentNew){
Parent pOld = em.find(entityClass, p.getId());
if(pOld !=null){
pOld.setName(parentNew.getName());
pOld.setChildren(parentNew.getChildren());
em.merge(pOld);
}
}
}
Моя проблема в том, что я могу изменить имя родителя, но любые изменения в списке дочерних элементов не отражаются в БД.
Он не изменяет и не выполняет какой-либо SQL для удаления/обновления/добавления дочерних элементов.
В чем может быть причина?
UPDATE
Хорошо после некоторых проб и ошибок и чтения через различные ссылки, кажется, что установка опции orphanRemoval в моем @onetomany решит мою проблему. Eclipselink автоматически удаляет строки сирот во время слияния.
Так много для сложности JPA.
@Entity
public class Parent {
private String name;
@OneToMany(mappedBy="owner",cascade=CascadeType.ALL, fetch=FetchType.EAGER,orphanRemoval = true)
private List<Child> children;
}
Ответы
Ответ 1
Это стандартная проблема слиянием. Изменения в дочерних объектах в коллекции будут получены.
Слияние может только каскадироваться по объектам, находящимся в списке, так что изменения в объектах, не входящих в список, теряются. К сожалению, oneToMany не имеет отношения: ребенок ManyToOne делает. Таким образом, дочерние объекты должны быть объединены для изменений, которые должны быть замечены и перенесены в базу данных. Резидентные объекты являются независимыми объектами и должны быть объединены индивидуально, когда они изменены. Это или добавлено к другому родительскому/дереву для объединения.
Ответ 2
Ваш вопрос What could be the cause?
. Причиной является значение content parentNew
. Если parentNew
является одной из следующих причин, он не будет влиять на /child/update.
- Нет родительской и дочерней визовой справки. --- > Он будет генерировать исключение.
-
Parent
значение параметра имеет нулевой или пустой список Clild
→ Это будет вашей причиной.
-
Child
не получать обновленное имя из пользовательского интерфейса.-- > Это будет вашей причиной.
Пример. Я быстро напишу. Все в порядке.
Уже существуют данные в DB
Parent Child
================== =======================================
'P001', 'Parent 1' 'C002', 'Child 2', 'P001'
'C003', 'Child 3', 'P001'
'C001', 'Child 1', 'P001'
После обновления:
Parent Child
================== =======================================
'P001', 'Update Parent 1' 'C002', 'Update Child 2', 'P001'
'C003', 'Update Child 3', 'P001'
'C001', 'Update Child 1', 'P001'
List<Child> clidList = new ArrayList<Child>();
Parent parent = new Parent("P001", "Update Parent 1", clidList);
Child c1 = new Child("C001", "Update Child 1", parent);
Child c2 = new Child("C002", "Update Child 2", parent);
Child c3 = new Child("C003", "Update Child 3", parent);
clidList.add(c1);
clidList.add(c2);
clidList.add(c3);
Parent existingParent = em.find(Parent.class, parent.getId());
existingParent.setName(parent.getName());
existingParent.setChildren(parent.getChildren());
em.merge(existingParent);