Reputation: 4017
Adding two OneToMany
associations to my entity class seems not to work. It works fine if I remove one of them.
@Entity
@Table(name = "school")
public class School {
private List<Teacher> teachers;
private List<Student> students;
@OneToMany(cascade=CascadeType.ALL, mappedBy = "school", fetch = FetchType.EAGER)
public List<Teacher> getTeachers()
return this.teachers;
}
public void setTeachers(List<Teacher> teachers) {
this.teachers = teachers;
}
@OneToMany(cascade=CascadeType.ALL, mappedBy = "school", fetch = FetchType.EAGER)
public List<Student> getStudents()
return this.students;
}
public void setStudents(List<Student> teachers) {
this.students = students;
}
}
Then in Teacher
and Student
I have the correct ManyToOne
annotations
@Entity
@Table(name = "teacher")
public class Teacher {
private School school;
@ManyToOne
@JoinColumn(name = "school_id")
public School getSchool() {
return this.school;
}
public void setSchool(School school) {
this.school = school;
}
}
@Entity
@Table(name = "student")
public class Student {
private School school;
@ManyToOne
@JoinColumn(name = "school_id")
public School getSchool() {
return this.school;
}
public void setSchool(School school) {
this.school = school;
}
}
I also have id
fields with the correct annotations (@Id
, @GeneratedValue
)
So to me it seems like I cannot have more than one @OneToMany
in the same class. Is this correct?
Upvotes: 2
Views: 14680
Reputation: 154090
You can have multiple one-to-many associations, as long as only one is EAGER.
But, even if you can use Set
instead of List
to bypass this MultipleBagFetchException
, it's not a good thing to do because you'll end up with a Cartesian Product.
The best thing to do is to NOT use EAGER at all. You should use LAZY
for all associations and eager fetch multiple many-to-one
and one-to-one
associations and at most one one-to-many
association using the JOIN FETCH JPQL directive.
If you want to fetch multiple one-to-many
associations, you can use the Hibernate.initialize(proxy)
method for the 2nd or 3rd association.
However, if you have a collection of parent entities that need to load multiple child associations, then you can use JOIN FETCH
for the first collection since, if you try to eagerly fetch multiple child collections, then you'll end up with a Cartesian Product.
SO, assuming you wanted to fetch multiple Post
parent entities along with their comments
and tags
collections, you can fetch the comments
in the first query:
List<Post> _posts = entityManager
.createQuery(
"select distinct p " +
"from Post p " +
"left join fetch p.comments " +
"where p.id between :minId and :maxId ", Post.class)
.setParameter("minId", 1L)
.setParameter("maxId", 50L)
.setHint(QueryHints.PASS_DISTINCT_THROUGH, false)
.getResultList();
And, for the second tags
collection, you can issue the following secondary query:
_posts = entityManager
.createQuery(
"select distinct p " +
"from Post p " +
"left join fetch p.tags t " +
"where p in :posts ", Post.class)
.setParameter("posts", _posts)
.setHint(QueryHints.PASS_DISTINCT_THROUGH, false)
.getResultList();
How does this work?
The first query fetches the Post
entities which are attached to the current EntityManager
. The comments
collection is fetched for each Post
entity, but the tags
collection is represented by a Proxy.
The second query will fetch the tags
, but since Hibernate already cached the Post
entities, it will just replace the tags
Proxy collections with the actual tags
entries fetched from the database.
Upvotes: 21
Reputation: 1
I think your @ManyToOne and @JoinColumn annotations should be put on the private school property of both type Teacher and Student. I noticed you have it on the public getter methods in both of them.
Upvotes: -1