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;
}
}
然后在 Teacher
和 Student
我有正确的 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-one
和 one-to-one
关联,最多一个 one-to-many
关联。
如果要获取多个one-to-many
关联,可以对第2个或第3个关联使用Hibernate.initialize(proxy)
方法。
获取多个父集合以及多个子集合
但是,如果您有一个需要加载多个子关联的父实体集合,那么您可以对第一个集合使用 JOIN FETCH
,因为如果您尝试急切地获取多个子集合,那么您最终会得到笛卡尔积。
SO,假设您想获取多个 Post
父实体及其 comments
和 tags
集合,您可以在第一个查询中获取 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();
这是如何工作的?
第一个查询获取附加到当前 EntityManager
的 Post
个实体。 comments
集合是为每个 Post
实体获取的,但 tags
集合由代理表示。
第二个查询将获取 tags
,但由于 Hibernate 已经缓存了 Post
实体,它只会用实际的 tags
替换 tags
代理集合从数据库中提取的条目。
我认为您的@ManyToOne 和@JoinColumn 注释应该放在教师和学生类型的私立学校属性 上。我注意到你在 public getter 方法中都有它。
向我的实体 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;
}
}
然后在 Teacher
和 Student
我有正确的 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-one
和 one-to-one
关联,最多一个 one-to-many
关联。
如果要获取多个one-to-many
关联,可以对第2个或第3个关联使用Hibernate.initialize(proxy)
方法。
获取多个父集合以及多个子集合
但是,如果您有一个需要加载多个子关联的父实体集合,那么您可以对第一个集合使用 JOIN FETCH
,因为如果您尝试急切地获取多个子集合,那么您最终会得到笛卡尔积。
SO,假设您想获取多个 Post
父实体及其 comments
和 tags
集合,您可以在第一个查询中获取 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();
这是如何工作的?
第一个查询获取附加到当前 EntityManager
的 Post
个实体。 comments
集合是为每个 Post
实体获取的,但 tags
集合由代理表示。
第二个查询将获取 tags
,但由于 Hibernate 已经缓存了 Post
实体,它只会用实际的 tags
替换 tags
代理集合从数据库中提取的条目。
我认为您的@ManyToOne 和@JoinColumn 注释应该放在教师和学生类型的私立学校属性 上。我注意到你在 public getter 方法中都有它。