1

In the below Hibernate generated query, the student_id and college_id_fk fields are selected twice, why is it so and what is the purpose? Can this be fixed.

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]

The calling code is as below -

  collegeRepo.findAll().forEach( c -> System.err.println("college wit students: " + c.getStudents() ) );

The above is spring data jpa provided method, so it has implementation from spring data jpa.

I have gone through similar Questions which suggested there could be some things wrong with entities like wrong mapping or wrong usage with @Id. So, I am pasting my entity relations here.

The entity relationships are - A student belongs to one college And a college can have multiple students. So there is ManyToOne relationship between Student --> College And a OneToMany relationship between College --> Student.

The entities are as below.
(small edit 1 - following advice from Ken, i have commented eager loading for College.students entity, But "selecting twice" problem still exists. End of edit 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;

and

@Entity
public class Student {

    @Id
    @GeneratedValue
    private int studentId;

    private String studentName;

    @ManyToOne
    @JoinColumn(name = "collegeId_fk")
    private College college;

I searched on search engines for "Hibernate is selecting same column twice." but there are no useful result. So, SOF is the only means to understand or fix this issue.

as requested by @tgdavies: the spring boot version is 2.6.1 and the hibernate-core version is 5.6.1

samshers
  • 1
  • 6
  • 37
  • 84
  • You should include the query and/or code which is generating this HQL output. Most likely, the underlying select is include the id column twice. – Tim Biegeleisen Feb 21 '22 at 02:53
  • @TimBiegeleisen - The calling code is as below - `collegeRepo.findAll().forEach( c -> System.err.println("college wit students: " + c.getStudents() ) );` The above is spring data jpa provided method, so it has implementation from spring data jpa. – samshers Feb 21 '22 at 02:56
  • also edited Q to add calling code. – samshers Feb 21 '22 at 02:56
  • Worth mentioning the exact version of hibernate -- might mean something to someone. – tgdavies Feb 21 '22 at 03:17
  • 1
    I suspect your `@JoinColumn` has something to do with this. What happens if you remove that? Which would probably require a `collegeId` column in your database instead of `@JoinColumn`. – M. Deinum Feb 21 '22 at 09:29
  • 1
    @tgdavies - the spring boot version is 2.6.1 and the hibernate-core version is 5.6.1 – samshers Feb 21 '22 at 10:21
  • @M.Deinum, i tried your suggestion. With just - `@JoinColumn`. But, Still there are two selects for the columns. `select students0_.college_college_id as college_3_12_0_, students0_.student_id as student_1_12_0_, students0_.student_id as student_1_12_1_, students0_.college_college_id as college_3_12_1_, students0_.student_name as student_2_12_1_ from student students0_ where students0_.college_college_id=?` Note: the hibernate generated fk name is `college_college_id`, even i did expect it to be just `college_id` – samshers Feb 21 '22 at 15:27
  • @M.Deinum, i further changed to `@JoinColumn (name = "college_id")` and still there are two selects in query `select students0_.college_id as college_3_12_0_, students0_.student_id as student_1_12_0_, students0_.student_id as student_1_12_1_, students0_.college_id as college_3_12_1_, students0_.student_name as student_2_12_1_ from student students0_ where students0_.college_id=?` – samshers Feb 21 '22 at 15:34

2 Answers2

2

Hibernate before version 6.0 relied on aliases generated for every column usage. So if you have multiple associations or even just an inverse to-many association, you will see duplicate column selects because Hibernate before 6.0 fetches values based on aliases. Hibernate 6.0 switched to position based fetching and de-duplicates selections for the same column.

Christian Beikov
  • 15,141
  • 2
  • 32
  • 58
  • thanks for the response +1, but could you explain further. What is the point of having a select of the column from the same table twice. Is there a way to resolve with out switching to hibernate 6.x. Does it effect performance in any manner. – samshers Feb 22 '22 at 10:15
  • i understand to some extent, but if you could add more details with some mapping/examples of the alias, it will help further and i could accept the answer. – samshers Feb 22 '22 at 10:21
  • I can't tell you why it was done this way, but this is something that Hibernate did for a long time. We know that it is wasteful to select columns multiple times, but usually this only happens for PK/FK columns which are small, so this shouldn't be a concern. There is nothing you can do other than switch to Hibernate 6 if you want the position based selection. – Christian Beikov Feb 22 '22 at 10:57
  • if you got time - would you mind helping with this [Criteria API and JPQL API with GROUP BY and CONCAT?](https://stackoverflow.com/questions/73129333/criteria-api-and-jpql-api-with-group-by-and-concat/73163002#73163002) – samshers Jul 30 '22 at 17:07
1

The select SQL that you mentioned is triggered by the eager loading of the students for a college. I briefly trace the source codes and it boils down to OneToManyPersister to generate the select clauses at here. I have no ideas why the author will generate the select clause which include some duplicated columns for this case, and I don't think there are anything that you can do to change this behaviour unless you use your own patched version of hibernate.

If you really don't want it to happen , my advise is not to use eager fetching for the college 's students. After all , it is a bad idea because eager fetching will introduce N+1 query issue. If you have 100 colleges , such SQL will repeat 100 times to load the students for these 100 colleges one by one after you load the colleges.

To avoid N+1 query issue and the duplicated columns in SQL , you can write a JPQL query to fetch join the college and its students together:

@Query("select distinct c from College c left join fetch c.students")
List<College> findAllCollegeWithStudents();

Or use @EntityGraph to do it declaratively if you like :

@EntityGraph(value = "students")
List<College> findAll(); 
Ken Chan
  • 84,777
  • 26
  • 143
  • 172
  • *EAGER will introduce an N+1* What is the basis for this statement? I would expect that JPA providers would be smart enough that they could recognize that EAGER means they _should_ perform a join; LAZY is where you tend to trigger N+1s. – chrylis -cautiouslyoptimistic- Feb 21 '22 at 04:23
  • They are not smart enough. eager fetching only define when to fetch the related entities. it does not defined how to fetch them and by default it will fetch them one by one. (see my answer at [this](https://stackoverflow.com/a/53939326/339637)) – Ken Chan Feb 21 '22 at 04:28
  • following your advice, i have commented eager loading for College.students entity, But "selecting twice" problem still exists. Edited the Q accordingly. – samshers Feb 21 '22 at 06:16
  • Ken, if you could share your though on "why multiple selects", it will help me and this Question. I know you for some time and have learned a lot from your answers/Questions. ++1. Hope to hear your expert advice again. – samshers Feb 21 '22 at 06:19
  • which select twice ? your question 's log only mention one select SQL which should repeat N times where N is the number of the college. Not only you need to disable eager loading , but also need to use `findAllCollegeWithStudents()` to get all the colleges and loop through them. – Ken Chan Feb 21 '22 at 08:45
  • 1
    `student_id` is selected twice, i am referring to these in the select statement - `students0_.student_id as student_1_12_0_` and `students0_.student_id as student_1_12_1_`, similarly with `college_id_fk`. Does this help. Else i can clarify further. – samshers Feb 21 '22 at 08:53
  • 1
    This answer doesn't really answer the question, as that was about duplicated columns in the select statement. **Not** about eager loading and fetch modes. – M. Deinum Feb 21 '22 at 09:23
  • ok. i re-read again and misread your question before . I just revised my answer accordingly based on my latest understanding. thanks – Ken Chan Feb 21 '22 at 17:54
  • That still doesn't answer the question as it still focusses on the 1+N select issue which is totally not what the question is about. – M. Deinum Feb 22 '22 at 07:02
  • yes or no . i think most probably only the author know why need to do in this way. So does it mean that only the author , and no one in this world can answer this question ? I just provide the advise to OP if he mind the select SQL to have such behaviour – Ken Chan Feb 22 '22 at 12:49
  • answer from @christian-beikov addresses my concern. – samshers Feb 23 '22 at 03:03