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:
// In the entity class of Parent1 write a method to unlink parent2
public void unLinkParent2(Parent2 parent2)
{
//remove columns from the link table, pertaining to the parent2
getParent2Collection().remove(parent2);
// using the parent2 object remove 'this' parent1 entity link columns
parent2.getParent1Collection().remove(this);
}
Parent1: Parent2: LinkP1-P2 -------------------------------------------------- Id1(PK) Id2(PK) Id1 (composite PK) name1 Name2 Id2 (composite PK)
Id1
and Id2
of the Link table together is a primary key referring to Parent1
and Parent2
tables.
The ManyToOne mapping controls the relationship - you have marked the OneToMany as mappedby the child's relation to its parent. This means that changes to the relationship are only picked up if merged from the child side. Because children removed from the parents list are no longer in it, merge cannot be cascaded to them, and any changes in them are never persisted to the database.
As others pointed out, orphan removal will allow for any children not in the list to be deleted on merge. This might be overkill when all you want is the relationship nulled out. In this scar, you can probably reverse the relationship so that it is owned by the parent. That will ensure the relation gets updated, but any changes in removed children will still not get picked up. The only way to get those changes picked up is to add the children to a new parent that gets merged, or to merge them independently. If they can exist without a parent, calling persist/merge on them directly is the better option.
这个问题是有关的: JPA CascadeType.ALL不会删除孤儿
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.