Hibernate 6.x on EntityManager.refresh(entity) fetches LAZY collections from the DB

After migration from Hibernate 5.6 to 6.2 (also checked on 6.6), we faced the performance degradation in our application.

After enabling SQL logs (we tested on MySQL) we have seen a lot of queries that we don’t have in Hibernate 5th version.

Especially, on EntityManager.refresh(entity), we have got a big difference.
E,g, for the Orders in the Hibernate 5.6 we have 4 queries, but in Hibernate 6.2 we have 30 queries. (also checked on 6.6.x)

All these extra queries were for @OneToMany collections with FetchType.LAZY.

I was thinking that max_fetch_depth property may help, but it did not.

Is this expected behavior for 6.x version?
Could we come back in 6.x to 5.x behavior for EntityManager.refresh(entity)?

Thanks

Please check all the migration guides: 6.6 Migration Guide

In particular the ORM 6.0 migration guide has an entry that is most likely the reason for your issue: 6.0 Migration Guide

In your reference I see the vice-versa case:

Starting with Hibernate 6.0, the laziness of such associations is properly respected

But in our case the laziness of the associations is NOT respected.

I checked all the migrations guides (6.0, 6.1, 6.2) and I have not found anything about fetch type Eager/Lazy or something related to the @OneToMany annotation.

I guess this ties into the fact that EntityManager.refresh() will now try to refresh the whole graph reachable through CascadeType.REFRESH or implicitly also CascadeType.ALL, but it’s hard to say for sure without seeing the code.
Please try to create a reproducer with our test case template and if you are able to reproduce the issue, create a bug ticket in our issue tracker and attach that reproducer.

Thanks. Just to confirm ..
In Hibernate 6.x, for such a structure:

@Entity
public class Department { ;
    @OneToMany(mappedBy = "department", cascade = CascadeType.ALL)
    private Set<Employee> employees = new HashSet<>();
}

@Entity
public class Employee {
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "department_id")
    private Department department;

    @OneToMany(mappedBy = "task", cascade = CascadeType.ALL)
    private Set<Task> tasks = new HashSet<>();
}

@Entity
public class Task {
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "employee_id")
    private Department employee;
}

On
EntityManager.refresh(department)

We fetch from the DB whole graph? - Department, all its Employees, and all the Tasks of Employees?

If you refresh a department, you will more or less execute a query like this:

select *
from Department d
left join fetch d.employees e
left join fetch e.tasks t

This is because Department#employees is implicitly refresh cascaded, and then also Employee#tasks is implicity refresh cascaded.

This Jira issue is somewhat related: HHH-13284

1 Like