Hibernate 两次选择相同的列。为什么以及目的是什么?

Hibernate is selecting same columns twice. Why and what's the purpose?

在下面的 Hibernate 生成的查询中,student_idcollege_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是某高校学生急切加载引发的。我简要地追踪了源代码,归结为 OneToManyPersisterhere 处生成 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 选择。