steven35
steven35

Reputation: 4017

Can a JPA entity have multiple OneToMany associations?

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

Answers (2)

Vlad Mihalcea
Vlad Mihalcea

Reputation: 154090

Why FetchType.EAGER is a bad idea

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.

Avoiding the 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.

Fetching multiple parents along with multiple child collections

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

abdulgafar jagun
abdulgafar jagun

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

Related Questions