17

My question here is almost similar to my other question Explicit delete on JPA relationships but I thought of simplifying it further to warrant more detailed answers.

Imagine I have a OneToMany relationship between a parent and a child.

@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;
}

In my UI, I am showing a list of parent. User can choose one parent and he/she can edit the list of its children. To show this, I have used a currentParent variable to represent the current parent edited. User can choose to add/edit children's name.

Upon clicking a button, I would save the new children's list using an EJB call.

@ViewScoped
public class MyBean(){
    private Parent currentParent;
    @EJB
    private MyEJB myEJB;

    public void saveChanges(){
        myEJB.save(currentParent);
    }
}

My EJB call looks like this.

@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);
        }
    }
}

My problem is that, I am able to change the name of the parent but any changes to the childrens list is not reflected into the DB. It does not alter or execute any SQL to delete/update/add childs.

What could be the cause?

UPDATE

Well after some trial and error and reading thru various links, it seems that setting the orphanRemoval option in my @onetomany would solve my problem. Eclipselink automatically deletes the orphan rows during merging.

So much for JPA complexity..

@Entity
public class Parent {
    private String name;
    @OneToMany(mappedBy="owner",cascade=CascadeType.ALL, fetch=FetchType.EAGER,orphanRemoval = true)
    private List<Child> children;
}
Community
  • 1
  • 1
Mark Estrada
  • 9,013
  • 37
  • 119
  • 186
  • Maybe this will help: http://stackoverflow.com/questions/10656765/handling-collection-updates-with-jpa – d1e Oct 31 '12 at 08:57
  • Hi, I am not sure if I get the answer from that link, so you mean I have to loop thru the collection and save it one by one? I actually thought that eclipselink will handle the saving of my collection? So for example I have 5 children in one entity, and then I edited it with just two children, when I call the merge operation, it will take care of deleting/adding the rows. Thanks – Mark Estrada Oct 31 '12 at 09:10

2 Answers2

8

This is a standard problem with merge. Changes to child objects in the collection will get picked up.

The merge call can only cascade over objects that are in the list, so changes to objects not in the list get lost. Unfortunately, the oneToMany doesn't own the relationship: the child's ManyToOne does. So the child entities need to be merged for changes to be seen and pushed to the database. Child entities are independent objects and need to be merged individually when the are changed. That, or added to a different parent/tree to get merged.

Chris
  • 20,138
  • 2
  • 29
  • 43
  • 1
    Any number of options. One would be to add them to a different parent if possible. Or if they cannot be independent of their parent, mark them with orphan removal so they are deleted when dereferenced. Or handle the difference in the collection in the merge call for the parent by finding the parent first and fixing children missing from the object passed to merge - this might even be doable in an event. or just be sure the app calls merge on children it removes from a parent. – Chris Nov 05 '12 at 13:52
1

Your question What could be the cause?. The cause is value content of parentNew. If the parentNew is one of the following cause, it will not effect/update to child.

  1. No reference instance parent and child vise visa. ---> It will throw exception.
  2. Parent parameter value have null or empty list of Clild --> This will be your cause.
  3. Child don't get updated name from UI.--> This will be your cause.

Example. I quick write it. It is OK.

Already exist data in DB

Parent                      Child
==================    =======================================
'P001', 'Parent 1'   'C002', 'Child 2', 'P001'
                     'C003', 'Child 3', 'P001'
                     'C001', 'Child 1', 'P001'

After Upldated:

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);
Zaw Than oo
  • 9,651
  • 13
  • 83
  • 131