spring-boot - Spring Data JPA 分页 HHH000104
问题描述
我得到了这个存储库代码:
@Query(value = "select distinct r from Reference r " +
"inner join fetch r.persons " +
"left outer join fetch r.categories " +
"left outer join fetch r.keywords " +
"left outer join fetch r.parentReferences",
countQuery = "select count(distinct r.id) from Reference r " +
"inner join r.persons " +
"left outer join r.categories " +
"left outer join r.keywords " +
"left outer join r.parentReferences")
Page<Reference> findsAllRelevantEntries(Pageable pageable);
当我对该查询运行测试时,我得到了这个 Hibernate 警告:
HHH000104: firstResult/maxResults specified with collection fetch; applying in memory!
@Test
void testFindAllRelevantAsync() throws ExecutionException, InterruptedException {
CompletableFuture<Page<Reference>> all = referenceService.findAllRelevantAsync(PageRequest.of(1, 20));
CompletableFuture.allOf(all).join();
assertThat(all.get()).isNotNull();
assertThat(all.get()).isNotEmpty();
}
存储库代码封装在此处未显示的服务方法中。它(服务方法)只是将来自服务的调用编组到存储库并返回。
此外,生成的 sql 查询不会生成limit
子句。虽然它确实触发了两个查询。
一个用于count
,另一个用于获取所有记录。
所以它获取所有记录并在内存中应用分页。
这会导致查询执行速度非常慢。
我怎样才能使分页与这个查询一起工作?
编辑
我知道这里经常被建议作为解决方案: 如何避免警告“firstResult/maxResults specified with collection fetch; apply in memory!” 什么时候使用休眠?
有没有办法用 Spring Data JPA 实现分页?我EntityManager
既不想硬连线,也不想从BasicTransformerAdapter
解决方案
您可以使用基于两个查询方法的通用/可重用方法。
一个用于检索实体的 SQL 查询IDs
和一个IN
包含来自第二个查询的谓词IDs
的第二个查询。
实现自定义 Spring Data JPA Executor:
@NoRepositoryBean
public interface AsimioJpaSpecificationExecutor<E, ID extends Serializable> extends JpaSpecificationExecutor<E> {
Page<ID> findEntityIds(Pageable pageable);
}
public class AsimioSimpleJpaRepository<E, ID extends Serializable> extends SimpleJpaRepository<E, ID>
implements AsimioJpaSpecificationExecutor<E, ID> {
private final EntityManager entityManager;
private final JpaEntityInformation<E, ID> entityInformation;
public AsimioSimpleJpaRepository(JpaEntityInformation<E, ID> entityInformation, EntityManager entityManager) {
super(entityInformation, entityManager);
this.entityManager = entityManager;
this.entityInformation = entityInformation;
}
@Override
public Page<ID> findEntityIds(Pageable pageable) {
CriteriaBuilder criteriaBuilder = this.entityManager.getCriteriaBuilder();
CriteriaQuery<ID> criteriaQuery = criteriaBuilder.createQuery(this.entityInformation.getIdType());
Root<E> root = criteriaQuery.from(this.getDomainClass());
// Get the entities ID only
criteriaQuery.select((Path<ID>) root.get(this.entityInformation.getIdAttribute()));
// Update Sorting
Sort sort = pageable.isPaged() ? pageable.getSort() : Sort.unsorted();
if (sort.isSorted()) {
criteriaQuery.orderBy(toOrders(sort, root, criteriaBuilder));
}
TypedQuery<ID> typedQuery = this.entityManager.createQuery(criteriaQuery);
// Update Pagination attributes
if (pageable.isPaged()) {
typedQuery.setFirstResult((int) pageable.getOffset());
typedQuery.setMaxResults(pageable.getPageSize());
}
return PageableExecutionUtils.getPage(typedQuery.getResultList(), pageable,
() -> executeCountQuery(this.getCountQuery(null, this.getDomainClass())));
}
protected static long executeCountQuery(TypedQuery<Long> query) {
Assert.notNull(query, "TypedQuery must not be null!");
List<Long> totals = query.getResultList();
long total = 0L;
for (Long element : totals) {
total += element == null ? 0 : element;
}
return total;
}
}
推荐阅读
- database - 在 postgres 表上读取金额
- reactjs - React mui-datatable 分页从 0 开始,而不是 1
- nginx - 安装certbot后找不到nginx页面错误
- jdbc - JMeter:正则表达式提取器获取JDBC请求返回数据后的列名
- javascript - 我怎么能访问和使用 event.currentTarget 但是当我打印事件时 currentTarget 为空?
- python - Scrapy 也将图像数组保存在 json 文件中,而不仅仅是 url
- html - HTML div 不会转到父 div 的底部
- mongodb - Kubernetes - Prometheus - 如果 Pod 已经被 prometheus 发现,是否真的需要导出器?
- python - 如何处理对象实例之间或模块之间的通信?
- python - 使用 python 从 Hive 下载区块链数据