You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Spring Boot JPA分页场景下获取分组总数量?

优化分组分页查询的总数量统计方案

你当前通过两个重复的Criteria查询实现分组分页和总分组数统计,存在代码冗余和效率问题,可从复用查询逻辑和优化统计方式两方面改进:

1. 优化总分组数的统计方式

你当前的统计查询是先分组再统计结果集大小,需要数据库返回所有分组数据后再计数,效率较低。改用countDistinct直接统计唯一分组值的数量,避免执行完整的分组查询:

final CriteriaBuilder criteriaBuilder = em.getCriteriaBuilder();
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
Specification<Topic> combinedSpecification = filterSpec.and(keysSpec);
Predicate countPredicate = combinedSpecification.toPredicate(countRoot, countQuery, criteriaBuilder);

countQuery.where(countPredicate);
// 直接统计distinct的Room关联实体数量,即总分组数
countQuery.select(criteriaBuilder.countDistinct(countJoin));

// 直接获取单一结果,无需处理结果集大小
Long totalCount = em.createQuery(countQuery).getSingleResult();

2. 复用公共查询构建逻辑

为避免主查询和统计查询重复创建Root、Join、Predicate,可抽离公共逻辑,复用相同的构建结果:

// 1. 构建公共查询组件
final CriteriaBuilder criteriaBuilder = em.getCriteriaBuilder();
Specification<Topic> combinedSpecification = filterSpec.and(keysSpec);

// 主查询部分
final CriteriaQuery<Tuple> mainQuery = criteriaBuilder.createQuery(Tuple.class);
final Root<Topic> mainRoot = mainQuery.from(Topic.class);
var mainJoin = mainRoot.join(Topic_.room, JoinType.LEFT);
Predicate mainPredicate = combinedSpecification.toPredicate(mainRoot, mainQuery, criteriaBuilder);

mainQuery.multiselect(mainJoin)
         .groupBy(mainJoin)
         .where(mainPredicate);

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

// 统计查询部分(复用Specification,无需重复构建Join逻辑)
final CriteriaQuery<Long> countQuery = criteriaBuilder.createQuery(Long.class);
final Root<Topic> countRoot = countQuery.from(Topic.class);
var countJoin = countRoot.join(Topic_.room, JoinType.LEFT);
Predicate countPredicate = combinedSpecification.toPredicate(countRoot, countQuery, criteriaBuilder);

countQuery.where(countPredicate)
          .select(criteriaBuilder.countDistinct(countJoin));

Long totalCount = em.createQuery(countQuery).getSingleResult();

若想进一步简化,可封装工具方法负责创建Root、Join和Predicate,供两个查询调用,彻底消除代码重复。

关键说明

  • countDistinct让数据库直接计算分组总数,无需返回所有分组数据,比分组后统计结果集大小更高效。
  • 复用Specification和Join逻辑,避免重复编写相同的查询条件构建代码,提升可维护性。

内容的提问来源于stack exchange,提问作者Eoban

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 08:55:12