I have bidirectional relationship setup as follows:
class Child{
@ManyToOne
@JoinTable(name = "CHILDREN_WITH_PARENT",
joinColumns = {@JoinColumn(name = "CHILD_ID")},
inverseJoinColumns = {@JoinColumn(name = "PARENT_ID")}
)
private Parent parent;
}
class Parent{
@OneToMany(mappedBy="parent", cascade=CascadeType.ALL)
Set<Child> childrens = new HashSet<Child>();
public void persistOrMerge() {
EntityManager em = entityManager();
em.getTransaction().begin();
try {
if (em.contains(this))
return;
if (id == null || id == 0) {
this.setCreatedDate(new Date());
em.persist(this);
} else {
Parent prev = em.find(Parent.class, this.id);
if (prev == null) {
em.persist(this);
} else{
this.setCreatedDate(new Date());
em.merge(this);
}
}
em.flush();
em.getTransaction().commit();
} finally {
em.close();
}
}
}
On my client side I have following code (GWT + EntityProxy)
Set<ChildProxy> children = new HashSet<ChildProxy>();
if(childIsNew)
child = request.create(Children.class)
else
child = request.edit(oldChild)
children.add(child);
//If children are deleted, they are not contained in the set
//we are sending back to server
parent.setChildren(children)
parent.persistOrMerge();
This code only works for adding new children. Removing of children from parent does not work even if parent class receives an empty children set. The linkages in JOIN table are not removed.
Can you please tell where I am missing something?
Thanks!
I will start by saying that is a really bad idea that the entity is the one using the entity manager directly.
The EntityManager.merge() method returns the actual merged instance, this implies that in your code, when you issue
em.merge(this)
You have no guarantee whatsoever that the merged instance corresponds to "this" anymore, and from that point on you may see all kinds of logical problems.
If you do not think this is such a big deal, your problem should be solved by turning on orphan removal on the OneToMany side of the relationship, provided that the children are not being used anywhere else in other relationships. Otherwise you will have to do the merging manually.
@OneToMany(mappedBy="parent", cascade=CascadeType.ALL, orphanRemoval=true)
Set<Child> childrens = new HashSet<Child>();
The JPA 2.0 specification states that
Associations that are specified as OneToOne or OneToMany support use of the orphanRemoval option. The following behaviors apply when orphanRemoval is in effect:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With