JPA 实体可以有多个 OneToMany 关联吗?

Can a JPA entity have multiple OneToMany associations?

向我的实体 class 添加两个 OneToMany 关联似乎不起作用。如果我删除其中一个,它会正常工作。

@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;
    }
}

然后在 TeacherStudent 我有正确的 ManyToOne 注释

@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;
    }
}

我还有 id 个带有正确注释的字段(@Id@GeneratedValue

所以对我来说,我似乎不能在同一个 class 中拥有超过一个 @OneToMany。这是正确的吗?

为什么 FetchType.EAGER 是个坏主意

您可以有多个一对多关联,只要只有一个是 EAGER。

但是,即使您可以使用 Set 而不是 List 来绕过此 MultipleBagFetchException,这也不是一件好事,因为您最终会得到笛卡尔积.

避免笛卡尔积

最好的办法是根本不要使用 EAGER。您应该对所有关联使用 LAZY,并使用 JOIN FETCH JPQL 指令预先获取多个 many-to-oneone-to-one 关联,最多一个 one-to-many 关联。

如果要获取多个one-to-many关联,可以对第2个或第3个关联使用Hibernate.initialize(proxy)方法。

获取多个父集合以及多个子集合

但是,如果您有一个需要加载多个子关联的父实体集合,那么您可以对第一个集合使用 JOIN FETCH,因为如果您尝试急切地获取多个子集合,那么您最终会得到笛卡尔积。

SO,假设您想获取多个 Post 父实体及其 commentstags 集合,您可以在第一个查询中获取 comments

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();

并且,对于第二个 tags 集合,您可以发出以下辅助查询:

_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();

这是如何工作的?

第一个查询获取附加到当前 EntityManagerPost 个实体。 comments 集合是为每个 Post 实体获取的,但 tags 集合由代理表示。

第二个查询将获取 tags,但由于 Hibernate 已经缓存了 Post 实体,它只会用实际的 tags 替换 tags 代理集合从数据库中提取的条目。

我认为您的@ManyToOne 和@JoinColumn 注释应该放在教师和学生类型的私立学校属性 上。我注意到你在 public getter 方法中都有它。