[英]Filtering using EntityGraph in Spring Data JPA Repository
[英]Using Spring data JPA EntityGraph with LAZY load mode for NamedAttributeNode field
我面臨 2 個問題:N + 1 查詢和 Out Of Memory (OOM)。
我通過分頁和懶加載解決了OOM:
@OneToMany(fetch = FetchType.LAZY)
@JoinColumn(name = "department_id")
private Set<Employee> employees;
但是當我使用延遲加載時,發生了 N + 1 個查詢。 所以我嘗試使用EntityGraph
作為https://www.baeldung.com/spring-data-jpa-named-entity-graphs 。 但是作為我的研究和本地測試, 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;
}
那么有沒有什么辦法可以同時得到它們呢? 使用EntityGraph
避免N+1和懶加載避免OOM?
更新: EntityGraph
能否有效地與 Pageable 配合使用? 我的意思是不要在 JOIN 查詢中加載所有數據。
使用EntityGraph
所有NamedAttributeNode
協會將在1個查詢與被裝載Join
條款。 啟用sql log查看hibernate在不同場景下為加載實體做了多少查詢
logging.level.org.hibernate.SQL=DEBUG
您將看到使用@OneToMany(fetch = FetchType.EAGER)
沒有EntityGraph
它加載在不同的員工select
查詢(N + 1),但使用EntityGraph
它僅執行1 select ... join
另外不要忘記在存儲庫中指定實體圖名稱,例如:
@EntityGraph(value = "Department")
List<Department> findAll();
更新: Spring DATA 分頁在數據庫端不起作用。 它將獲取所有數據,然后在內存中進行過濾。 這就是它的工作原理..有一些解決方法,請查看此鏈接:
如何避免警告“用集合獲取指定的 firstResult/maxResults;在內存中應用!” 什么時候使用休眠?
避免“HHH000104:使用集合提取指定的 firstResult/maxResults;在內存中應用!” 使用 Spring 數據
VladMihalcea 博客 修復 Hibernate HHH000104 的最佳方法
至於我,解決方案可能是創建自定義存儲庫並使用EntityManager
手動構建查詢。
一個簡單的解決方案是:
@EntityGraph
獲取您的記錄。@EntityGraph
從之前返回的 ID 中獲取您的記錄。=> 沒有 N+1 復雜性,只有 2 個 SQL 請求,沒有本地查詢等。
例子:
在您的主存儲庫上,添加帶有您要獲取的關聯的@EntityGraph
注釋:
public interface CarRepository extends JpaRepository<Car, String>, JpaSpecificationExecutor<Car>, CarCustomRepository {
@Override
@EntityGraph(attributePaths = { "foo", "bar", "bar.baz" })
List<Car> findAllById(Iterable<String> ids);
}
使用findAllWithEntityGraph
方法創建自定義存儲庫:
public interface CarCustomRepository {
Page<Car> findAllWithEntityGraph(Specification<Car> specification, Pageable pageable);
}
自定義存儲庫的實現。 它首先獲取沒有實體圖的實體,然后使用實體圖重新獲取它們以加載關聯。 不要忘記重新排序實體以保持順序:
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());
}
}
然后,您只需要調用CarRepository#findAllWithEntityGraph
方法即可獲取具有分頁且沒有N+1 復雜性的記錄。
問題是:為什么hibernate默認沒有這個行為?
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.