我目前正在实现分组功能。
在前端,有一个网格显示“主题”实体。前端发送用于分组的列(字段)、所需的行数和一些过滤器。
在这种情况下,前端希望按“房间”列进行分组,该列是对“主题”上实体的引用。
为了确保分页正常工作,我需要返回如果不应用分页则存在的组总数。目前,我通过两个单独的查询来实现这一点,但我认为这不是最佳解决方案。
这是(稍微简化的)主要查询。它在自定义存储库中实现
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();
这似乎是一个非常愚蠢的解决方案。
您可以使用子查询来计算不同组的计数,然后返回分页数据。这样就可以一次性实现计数和分组结果了。
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");