[英]Using Spring data JPA EntityGraph with LAZY load mode for NamedAttributeNode field
I am facing with 2 problems: N + 1 query and Out Of Memory (OOM).我面临 2 个问题:N + 1 查询和 Out Of Memory (OOM)。
I solved OOM by paging and lazy loading:我通过分页和懒加载解决了OOM:
@OneToMany(fetch = FetchType.LAZY)
@JoinColumn(name = "department_id")
private Set<Employee> employees;
But when I use lazy loading, N + 1 query happened.但是当我使用延迟加载时,发生了 N + 1 个查询。 So I try to use EntityGraph
as https://www.baeldung.com/spring-data-jpa-named-entity-graphs .所以我尝试使用EntityGraph
作为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:但是作为我的研究和本地测试, EntityGraph
总是对NamedAttributeNode
字段 - 关联字段进行预加载,我想延迟加载 - 首先不要加载所有数据:
@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?使用EntityGraph
避免N+1和懒加载避免OOM?
UPDATE: Can EntityGraph
works fine with Pageable effectively?更新: EntityGraph
能否有效地与 Pageable 配合使用? I mean do not load all data in JOIN query.我的意思是不要在 JOIN 查询中加载所有数据。
Using EntityGraph
all your NamedAttributeNode
associations will be loaded in 1 query with Join
clause.使用EntityGraph
所有NamedAttributeNode
协会将在1个查询与被装载Join
条款。 Enable sql log to see how many queries hibernate does for loading entities in different scenarios启用sql log查看hibernate在不同场景下为加载实体做了多少查询
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
您将看到使用@OneToMany(fetch = FetchType.EAGER)
没有EntityGraph
它加载在不同的员工select
查询(N + 1),但使用EntityGraph
它仅执行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.更新: Spring DATA 分页在数据库端不起作用。 It will fetch all data and then filtered in memory.它将获取所有数据,然后在内存中进行过滤。 That's how it works.. There are some workarounds, check this links:这就是它的工作原理..有一些解决方法,请查看此链接:
How can I avoid the Warning "firstResult/maxResults specified with collection fetch; applying in memory!" 如何避免警告“用集合获取指定的 firstResult/maxResults;在内存中应用!” when using Hibernate? 什么时候使用休眠?
Avoiding "HHH000104: firstResult/maxResults specified with collection fetch; applying in memory!" 避免“HHH000104:使用集合提取指定的 firstResult/maxResults;在内存中应用!” using Spring Data 使用 Spring 数据
VladMihalcea Blog The best way to fix the Hibernate HHH000104 VladMihalcea 博客 修复 Hibernate HHH000104 的最佳方法
As for me the solution could be creating custom repository and using EntityManager
to construct query manually.至于我,解决方案可能是创建自定义存储库并使用EntityManager
手动构建查询。
A simple solution is:一个简单的解决方案是:
@EntityGraph
.首先使用分页但不@EntityGraph
获取您的记录。@EntityGraph
from previous returned ids.使用@EntityGraph
从之前返回的 ID 中获取您的记录。=> No N+1 complexity, only 2 SQL requests and no native queries, etc. => 没有 N+1 复杂性,只有 2 个 SQL 请求,没有本地查询等。
Example:例子:
On your main repository, add @EntityGraph
annotation with associations you want to fetch:在您的主存储库上,添加带有您要获取的关联的@EntityGraph
注释:
public interface CarRepository extends JpaRepository<Car, String>, JpaSpecificationExecutor<Car>, CarCustomRepository {
@Override
@EntityGraph(attributePaths = { "foo", "bar", "bar.baz" })
List<Car> findAllById(Iterable<String> ids);
}
Create a custom repository with a findAllWithEntityGraph
method:使用findAllWithEntityGraph
方法创建自定义存储库:
public interface CarCustomRepository {
Page<Car> findAllWithEntityGraph(Specification<Car> specification, Pageable pageable);
}
The implementation of the custom repository.自定义存储库的实现。 It first fetch entities without entity graph, then it re-fetch them with entity graph in order to load associations.它首先获取没有实体图的实体,然后使用实体图重新获取它们以加载关联。 Don't forget to re-sort entities in to preserve order:不要忘记重新排序实体以保持顺序:
public class CarCustomRepositoryImpl implements CarCustomRepository {
@Autowired
@Lazy
private CarRepository carRepository;
@Override
public Page<Car> findAllWithEntityGraph(Specification<Car> specification, Pageable pageable) {
Page<Car> page = carRepository.findAll(specification, pageable);
List<String> ids = page.getContent().stream().map(Car::getId).collect(Collectors.toList());
List<Car> cars = carRepository.findAllById(ids).stream()
.sorted(Comparator.comparing(car -> ids.indexOf(car.getId())))
.collect(Collectors.toList());
return new PageImpl<>(cars, pageable, page.getTotalElements());
}
}
Then, you just have to invoke CarRepository#findAllWithEntityGraph
method in order to fetch records with pagination and no N+1 complexity.然后,您只需要调用CarRepository#findAllWithEntityGraph
方法即可获取具有分页且没有N+1 复杂性的记录。
The question is: why hibernate does not have this behavior by default?问题是:为什么hibernate默认没有这个行为?
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.