Hibernate 两次选择相同的列。为什么以及目的是什么?
Hibernate is selecting same columns twice. Why and what's the purpose?
在下面的 Hibernate 生成的查询中,student_id
和 college_id_fk
字段被选择了两次,为什么会这样,目的是什么?这个能修好吗
2022-02-21 07:12:33.213 TRACE 19824 --- [ main] o.h.type.descriptor.sql.BasicExtractor : extracted value ([student_1_12_0_] : [INTEGER]) - [6]
Hibernate:
select
students0_.college_id_fk as college_3_12_0_,
students0_.student_id as student_1_12_0_,
students0_.student_id as student_1_12_1_,
students0_.college_id_fk as college_3_12_1_,
students0_.student_name as student_2_12_1_
from
student students0_
where
students0_.college_id_fk=?
2022-02-21 07:12:33.214 TRACE 19824 --- [ main] o.h.type.descriptor.sql.BasicBinder : binding parameter [1] as [INTEGER] - [1]
调用代码如下-
collegeRepo.findAll().forEach( c -> System.err.println("college wit students: " + c.getStudents() ) );
以上是springdata jpa提供的方法,所以有springdata jpa的实现。
我经历过类似的问题,这些问题表明实体可能存在一些问题,例如错误映射或@Id 的错误使用。所以,我在这里粘贴我的实体关系。
实体关系为-
一个学生属于一个学院,一个学院可以有多个学生。
所以在 Student --> College 之间存在 ManyToOne 关系,在 College --> Student 之间存在 OneToMany 关系。
实体如下。
(小编辑 1 - 根据 Ken 的建议,我评论了 College.students 实体的预加载,但“选择两次”问题仍然存在。编辑 1 结束)
@Entity
public class College {
@Id
@GeneratedValue
private int collegeId;
private String collegeName;
@OneToMany(targetEntity = Student.class, mappedBy = "college") //, cascade = CascadeType.ALL, fetch = FetchType.EAGER)
//as you can see students is loaded eagerly.
private List<Student> students;
和
@Entity
public class Student {
@Id
@GeneratedValue
private int studentId;
private String studentName;
@ManyToOne
@JoinColumn(name = "collegeId_fk")
private College college;
我在搜索引擎上搜索“Hibernate 正在两次选择同一列”。但没有有用的结果。因此,SOF 是了解或解决此问题的唯一方法。
根据@tgdavies 的要求:spring 启动版本为 2.6.1,hibernate-core 版本为 5.6.1
你说的selectSQL是某高校学生急切加载引发的。我简要地追踪了源代码,归结为 OneToManyPersister
在 here 处生成 select 子句。我不知道为什么作者会为这种情况生成包含一些重复列的 select 子句,而且我认为除非您使用自己的修补版本,否则您无法更改此行为休眠。
如果你真的不希望它发生,我的建议是不要对大学的学生使用预取。毕竟,这是一个坏主意,因为预取会引入 N+1 查询问题。如果你有100所大学,这样SQL会重复100次,加载完这100所大学的学生后,一一加载。
为了避免 N+1 查询问题和 SQL 中的重复列,您可以编写一个 JPQL 查询来获取将大学及其学生连接在一起:
@Query("select distinct c from College c left join fetch c.students")
List<College> findAllCollegeWithStudents();
如果您愿意,也可以使用 @EntityGraph
以声明方式进行:
@EntityGraph(value = "students")
List<College> findAll();
6.0 版之前的 Hibernate 依赖于为每个列使用生成的别名。因此,如果您有多个关联,甚至只有一个反向 to-many 关联,您将看到重复的列选择,因为 6.0 之前的 Hibernate 根据别名获取值。 Hibernate 6.0 切换到基于位置的提取和同一列的 de-duplicates 选择。
在下面的 Hibernate 生成的查询中,student_id
和 college_id_fk
字段被选择了两次,为什么会这样,目的是什么?这个能修好吗
2022-02-21 07:12:33.213 TRACE 19824 --- [ main] o.h.type.descriptor.sql.BasicExtractor : extracted value ([student_1_12_0_] : [INTEGER]) - [6]
Hibernate:
select
students0_.college_id_fk as college_3_12_0_,
students0_.student_id as student_1_12_0_,
students0_.student_id as student_1_12_1_,
students0_.college_id_fk as college_3_12_1_,
students0_.student_name as student_2_12_1_
from
student students0_
where
students0_.college_id_fk=?
2022-02-21 07:12:33.214 TRACE 19824 --- [ main] o.h.type.descriptor.sql.BasicBinder : binding parameter [1] as [INTEGER] - [1]
调用代码如下-
collegeRepo.findAll().forEach( c -> System.err.println("college wit students: " + c.getStudents() ) );
以上是springdata jpa提供的方法,所以有springdata jpa的实现。
我经历过类似的问题,这些问题表明实体可能存在一些问题,例如错误映射或@Id 的错误使用。所以,我在这里粘贴我的实体关系。
实体关系为- 一个学生属于一个学院,一个学院可以有多个学生。 所以在 Student --> College 之间存在 ManyToOne 关系,在 College --> Student 之间存在 OneToMany 关系。
实体如下。
(小编辑 1 - 根据 Ken 的建议,我评论了 College.students 实体的预加载,但“选择两次”问题仍然存在。编辑 1 结束)
@Entity
public class College {
@Id
@GeneratedValue
private int collegeId;
private String collegeName;
@OneToMany(targetEntity = Student.class, mappedBy = "college") //, cascade = CascadeType.ALL, fetch = FetchType.EAGER)
//as you can see students is loaded eagerly.
private List<Student> students;
和
@Entity
public class Student {
@Id
@GeneratedValue
private int studentId;
private String studentName;
@ManyToOne
@JoinColumn(name = "collegeId_fk")
private College college;
我在搜索引擎上搜索“Hibernate 正在两次选择同一列”。但没有有用的结果。因此,SOF 是了解或解决此问题的唯一方法。
根据@tgdavies 的要求:spring 启动版本为 2.6.1,hibernate-core 版本为 5.6.1
你说的selectSQL是某高校学生急切加载引发的。我简要地追踪了源代码,归结为 OneToManyPersister
在 here 处生成 select 子句。我不知道为什么作者会为这种情况生成包含一些重复列的 select 子句,而且我认为除非您使用自己的修补版本,否则您无法更改此行为休眠。
如果你真的不希望它发生,我的建议是不要对大学的学生使用预取。毕竟,这是一个坏主意,因为预取会引入 N+1 查询问题。如果你有100所大学,这样SQL会重复100次,加载完这100所大学的学生后,一一加载。
为了避免 N+1 查询问题和 SQL 中的重复列,您可以编写一个 JPQL 查询来获取将大学及其学生连接在一起:
@Query("select distinct c from College c left join fetch c.students")
List<College> findAllCollegeWithStudents();
如果您愿意,也可以使用 @EntityGraph
以声明方式进行:
@EntityGraph(value = "students")
List<College> findAll();
6.0 版之前的 Hibernate 依赖于为每个列使用生成的别名。因此,如果您有多个关联,甚至只有一个反向 to-many 关联,您将看到重复的列选择,因为 6.0 之前的 Hibernate 根据别名获取值。 Hibernate 6.0 切换到基于位置的提取和同一列的 de-duplicates 选择。