We have three entities with bidirectional many-to-many mappings in a A <-> B <-> C “hierarchy” like so (simplified, of course):
@Entity
Class A {
@Id int id;
@JoinTable(
name = "a_has_b",
joinColumns = {@JoinColumn(name = "a_id", referencedColumnName = "id")},
inverseJoinColumns = {@JoinColumn(name = "b_id", referencedColumnName = "id")})
@ManyToMany
Collection<B> bs;
}
@Entity
Class B {
@Id int id;
@JoinTable(
name = "b_has_c",
joinColumns = {@JoinColumn(name = "b_id", referencedColumnName = "id")},
inverseJoinColumns = {@JoinColumn(name = "c_id", referencedColumnName = "id")})
@ManyToMany(fetch=FetchType.EAGER,
cascade=CascadeType.MERGE,CascadeType.PERSIST,CascadeType.REFRESH})
@org.hibernate.annotations.Fetch(FetchMode.SUBSELECT)
private Collection<C> cs;
@ManyToMany(mappedBy = "bs", fetch=FetchType.EAGER,
cascade={CascadeType.MERGE,CascadeType.PERSIST, CascadeType.REFRESH})
@org.hibernate.annotations.Fetch(FetchMode.SUBSELECT)
private Collection<A> as;
}
@Entity
Class C {
@Id int id;
@ManyToMany(mappedBy = "cs", fetch=FetchType.EAGER,
cascade={CascadeType.MERGE,CascadeType.PERSIST, CascadeType.REFRESH})
@org.hibernate.annotations.Fetch(FetchMode.SUBSELECT)
private Collection<B> bs;
}
There’s no conecpt of an orphan – the entities are “standalone” from the application’s point of view – and most of the time we’re going to have a fistful of A:s, each with a couple of B:s (some may be “shared” among the A:s), and some 1000 C:s, not all of which are always “in use” by any B. We’ve concluded that we need bidirectional relations, since whenever an entity instance is removed, all links (entries in the join tables) have to be removed too. That is done like this:
void removeA( A a ) {
if ( a.getBs != null ) {
for ( B b : a.getBs() ) { //<--------- ConcurrentModificationException here
b.getAs().remove( a ) ;
entityManager.merge( b );
}
}
entityManager.remove( a );
}
If the collection, a.getBs() here, contains more than one element, then a ConcurrentModificationException is thrown. I’ve been banging my head for a while now, but can’t think of a reasonable way of removing the links without meddling with the collection, which makes underlying the Iterator angry.
Q1: How am I supposed to do this, given the current ORM setup? (If at all…)
Q2: Is there a more reasonable way do design the OR-mappings that will let JPA (provided by Hibernate in this case) take care of everything. It’d be just swell if we didn’t have to include those I'll be deleted now, so everybody I know, listen carefully: you don't need to know about this!-loops, which aren’t working anyway, as it stands…
The issue is that the collections inside of A, B, and C are magical Hibernate collections so when you run the following statement:
this removes a from b’s collection but it also removes b from a’s list which happens to be the collection being iterated over in the for loop. That generates the
ConcurrentModificationException.Matt’s solution should work if you are really removing all elements in the collection. If you aren’t however another work around is to copy all of the b’s into a collection which removes the magical Hibernate collection from the process.
That should get you a little further down the road.