问题
I have
public class RelationObject {
@OneToMany(orphanRemoval = true, mappedBy = "relationObject")
private Set<RelationParticipant> participants = new HashSet<RelationParticipant>();
}
public class BusinessObject {
@OneToMany(orphanRemoval = true, mappedBy = "businessObject")
private Set<RelationParticipant> participants = new HashSet<RelationParticipant>();
}
and
public class RelationParticipant {
@ManyToOne
@JoinColumn(name = "ro_id", nullable = false)
private RelationObject relationObject;
@ManyToOne
@JoinColumn(name = "bo_id", nullable = false)
private BusinessObject businessObject;
}
And I have a RelationParticipant connected to one RelationObject (relobj) and one BusinessObject. Now I do em.remove(relobj), and on commit or flush I get an integrity exception. Or sometimes I don't, it depends.
According to JPA spec, "If the remove operation is applied to a managed source entity, the remove operation will be cascaded to the relationship target". But sometimes that just does not happen. And sometimes does. Why?
回答1:
JPA states that orphan removal will operate the same as the cascade remove on a relationship, so there is no need to set cascade remove - when you remove a RelationObject, JPA will remove any referenced RelationParticipants if orphanRemoval = true.
You might not see this behavior in all cases because JPA can only cascade the removal to entities it knows about. In your case, check that the RelationObject's participants collection really holds 2 RelationParticipants that reference it in the database. If it does not, JPA will only remove the ones it knows about. A quick way to check is to call refresh on the RelationObject before calling remove on it. This is common in applications that do not maintain both sides of bidirectional relational relationships, for instance, by setting the RelationParticipants->RelationObject reference without also adding the RelationParticipants to the RelationObject's participants collection. If this is the case, JPA does not maintain bidirectional relationships for you - the application is required to keep both sides in synch itself.
回答2:
After a couple of days' research I found out what looks like the following behavior. OrphanRemoval only cascades removal to entities which becomes orphans; and not just orphans by one relation (whose source is being removed), but orphans by all relations. Expample: if I delete Source which contains X in its Target[], X will become orphan UNLESS there is another source S for which X is also a Target. Of course this is natural when X is "master" of S (S contains FK to X); but the cascading will not happen even if X is "child" of S (X contains FK to S, and X is source for inverse relation: "mappedBy=..."). I can't find this special condition in JPA specification, so maybe I am wrong? But at least this explanation makes sense.
So, removing relobj will NOT cascade to its RelationParticipants in my example because every one of the latter is also referred by its BusinessObject (as an inverse, non-owning side of relation).
Another interesting bit of behavior: cascading CAN STILL occur even in that circumstances, if EntityManager does not know about any obstacles. Say, in DB there is a BusinessObject which refers to RelationParticipant, but it was not yet loaded by EntityManager. In this case EntityManager will not detect this BusinessObject's inverse reference to RelationParticipant, and em.remove(relobj) will successfully cascade to relobj.getParticipants().
来源:https://stackoverflow.com/questions/34318864/orphan-removal-not-working-if-not-complete-orphan