I am facing with 2 problems: N + 1 query and Out Of Memory (OOM).
I solved OOM by paging and lazy loading:
@OneToMany(fetch = FetchType.LAZY) @JoinColumn(name = "department_id") private Set<Employee> employees;
But when I use lazy loading, N + 1 query happened. So I try to use EntityGraph
as https://www.baeldung.com/spring-data-jpa-named-entity-graphs. But as my researches and local test, EntityGraph
always do eager loading for NamedAttributeNode
field – association field, which I want to be lazy loading – do not load all data at first:
@Entity @Table(name = "department") @NamedEntityGraph(name = "Department", attributeNodes = { @NamedAttributeNode("employees") } ) public class Department implements Serializable { @OneToMany(fetch = FetchType.LAZY) @JoinColumn(name = "department_id") private Set<Employee> employees; }
So are there any way to get them both ? Use EntityGraph
to avoid N + 1 and lazy loading to avoid OOM ?
UPDATE:
Can EntityGraph
works fine with Pageable effectively ? I mean do not load all data in JOIN query.
Advertisement
Answer
Using EntityGraph
all your NamedAttributeNode
associations will be loaded in 1 query with Join
clause. Enable sql log to see how many queries hibernate does for loading entities in different scenarios
logging.level.org.hibernate.SQL=DEBUG
You will see that using @OneToMany(fetch = FetchType.EAGER)
without EntityGraph
it loads employees in separate select
queries (N + 1), but using EntityGraph
it performs only 1 select ... join
Also don’t forget to specify entity graph name in repository like:
@EntityGraph(value = "Department") List<Department> findAll();
UPDATE: Spring DATA Pagination doesn’t work on database side. It will fetch all data and then filtered in memory. That’s how it works.. There are some workarounds, check this links:
VladMihalcea Blog The best way to fix the Hibernate HHH000104
As for me the solution could be creating custom repository and using EntityManager
to construct query manually.