如何使用带有分页功能的 Spring Boot JPA 获取组的总数?

问题描述 投票:0回答:1

我目前正在实现分组功能。

在前端,有一个网格显示“主题”实体。前端发送用于分组的列(字段)、所需的行数和一些过滤器。

在这种情况下,前端希望按“房间”列进行分组,该列是对“主题”上实体的引用。

为了确保分页正常工作,我需要返回如果不应用分页则存在的组总数。目前,我通过两个单独的查询来实现这一点,但我认为这不是最佳解决方案。

这是(稍微简化的)主要查询。它在自定义存储库中实现

final CriteriaBuilder criteriaBuilder = em.getCriteriaBuilder();
final CriteriaQuery<Tuple> mainQuery = criteriaBuilder.createQuery(Tuple.class);
final Root<Topic> groupRoot = mainQuery.from(Topic.class);

var groupJoin = groupRoot.join(Topic_.room, JoinType.LEFT);

mainQuery.multiselect(groupJoin);
mainQuery.groupBy(groupJoin);

//passed in
Specification<Topic> combinedSpecification = filterSpec.and(keysSpec);
Predicate mainPredicate = combinedSpecification.toPredicate(groupRoot, mainQuery, criteriaBuilder);
mainQuery.where(mainPredicate);

var startAndSize = getStartAndSize(paginationParameter);
final List<Tuple> mainQueryResult = em.createQuery(mainQuery)
            .setFirstResult(startAndSize.getFirst()) //<- paginated
            .setMaxResults(startAndSize.getSecond()) //<- paginated
            .getResultList();


对于计数查询,我构建了几乎相同的查询,但没有 setFirstResult/setMaxResults,然后对它们进行计数。对于每一列,我都有两个几乎相同的查询。有没有办法无需额外查询即可获取组的计数?

我尝试使用如下所示的单独查询来计算组数。

final CriteriaQuery<Long> countQuery = criteriaBuilder.createQuery(Long.class);
final Root<Topic> countRoot = countQuery.from(Topic.class);
var countJoin = countRoot.join(Topic_.room, JoinType.LEFT);

Specification<Topic> combinedSpecification = filterSpec.and(keysSpec);
Predicate countPredicate = combinedSpecification.toPredicate(countRoot, countQuery, criteriaBuilder);

countQuery.where(countPredicate);
countQuery.groupBy(countJoin);
countQuery.multiselect(criteriaBuilder.count(countJoin));
int totalCount = em.createQuery(countQuery).getResultList().size();

这似乎是一个非常愚蠢的解决方案。

java database spring-boot group-by spring-data-jpa
1个回答
0
投票

您可以使用子查询来计算不同组的计数,然后返回分页数据。这样就可以一次性实现计数和分组结果了。

final CriteriaBuilder criteriaBuilder = em.getCriteriaBuilder();
final CriteriaQuery<Tuple> mainQuery = criteriaBuilder.createQuery(Tuple.class);
final Root<Topic> groupRoot = mainQuery.from(Topic.class);
var groupJoin = groupRoot.join(Topic_.room, JoinType.LEFT);

mainQuery.multiselect(groupJoin);
mainQuery.groupBy(groupJoin);

Specification<Topic> combinedSpecification = filterSpec.and(keysSpec);
Predicate mainPredicate = combinedSpecification.toPredicate(groupRoot, mainQuery, criteriaBuilder);
mainQuery.where(mainPredicate);

final Subquery<Long> groupCountQuery = mainQuery.subquery(Long.class);
Root<Topic> groupCountRoot = groupCountQuery.from(Topic.class);
var groupCountJoin = groupCountRoot.join(Topic_.room, JoinType.LEFT);
groupCountQuery.select(criteriaBuilder.countDistinct(groupCountJoin)).where(mainPredicate);

var startAndSize = getStartAndSize(paginationParameter);
final List<Tuple> mainQueryResult = em.createQuery(mainQuery)
    .setFirstResult(startAndSize.getFirst())
    .setMaxResults(startAndSize.getSecond())
    .getResultList();

long totalGroupCount = em.createQuery(groupCountQuery).getSingleResult();

Query query = em.createQuery(
    "SELECT new package.CountData((SELECT COUNT(DISTINCT r) FROM Topic t LEFT JOIN t.room r WHERE <conditions>), t) " +
    "FROM Topic t LEFT JOIN t.room r WHERE <conditions> GROUP BY r");
© www.soinside.com 2019 - 2024. All rights reserved.