JPA 标准:在所有列的完整结果之前获取总数;重用 Where 子句

JPA Criteria: Obtain total count just before full result with all columns; reuse Where clause

JPA Criteria 中,我有一个复杂的有效查询。它涉及许多 Join 和一个复杂的 Where 子句。但就在我 运行 完整 selection 之前,我需要快速 COUNT 完整结果集。

我尝试使用 cb.count 重用我的 where 子句和我的顶级元素 nvRoot 中的所有连接和 select。但是我收到错误 Caused by: java.lang.IllegalStateException: No criteria query roots were specified.

     CriteriaBuilder cb = entityManager.getCriteriaBuilder();
     CriteriaQuery<Result> criteriaQuery = cb.createQuery(Result.class);
     Root<NvisionTrainee> nvRoot = criteriaQuery.from(Nv.class);
     Join<Object,Object> plans = nvRoot.join("plans", JoinType.LEFT);
     // etc., other Joins

     Predicate where = cb.conjunction();
     // Complex Where clause built...
     criteriaQuery.where(where);

     // --- HERE I NEED TO RUN A QUICK COUNT QUERY, with all Joins/Where as built
     // --- BUT THE BELOW DOESN'T WORK: 
     // --- Caused by: java.lang.IllegalStateException: No criteria query roots were specified
     CriteriaQuery<Long> cqCount = cb.createQuery(Long.class);
     cqCount.select(cb.count(nvRoot));     
     cqCount.distinct(true);
     cqCount.where(where);
     Long totalCount = entityManager.createQuery(cqCount).getSingleResult();

     // --- THIS FULL QUERY WORKS (THE REMAINDER), IT GETS ME MY FULL SELECTION
     CompoundSelection<Result> selectionFull = cb.construct(
                                   Result.class,
                                   nvRoot.get("firstName"),
                                   // etc. - many columns
                                   );
     criteriaQuery.select(selectionFull);
     criteriaQuery.distinct(true);
     TypedQuery<Result> query = entityManager.createQuery(criteriaQuery);
     List<Result> results = query.getResultList();

根据下面的评论,我尝试在代码中添加 cqCount.from(Nv.class),但这给了我:

Invalid path: 'generatedAlias2.id'

最简单的解决方法是将谓词构建部分提取到方法中并像这样重用它:

CriteriaBuilder cb = entityManager.getCriteriaBuilder();

//count query
CriteriaQuery<Long> cqCount = cb.createQuery(Long.class);
Root<NvisionTrainee> nvCountRoot = buildQuery(cqCount, ...);
cqCount.select(cb.count(nvCountRoot));     
cqCount.distinct(true);
Long totalCount = entityManager.createQuery(cqCount).getSingleResult();

//actual query
CriteriaQuery<Result> criteriaQuery = cb.createQuery(Result.class);
Root<NvisionTrainee> nvRoot = buildQuery(criteriaQuery, ...); //you might need to return other paths created inside buildQuery if you need to use them in the SELECT clause
CompoundSelection<Result> selectionFull = cb.construct(
    Result.class,
    nvRoot.get("firstName"),
    ...
    );
criteriaQuery.select(selectionFull);
criteriaQuery.distinct(true);
TypedQuery<Result> query = entityManager.createQuery(criteriaQuery);
List<Result> results = query.getResultList();

其中 buildQuery 的定义如下:

private Root<NvisionTrainee> buildQuery(CriteriaQuery<?> query, ... /* possibly many other arguments*/) {
    Root<NvisionTrainee> nvRoot = query.from(Nv.class);
    Join<Object,Object> plans = nvRoot.join("plans", JoinType.LEFT);
    // etc., other Joins - build your WHERE clause here
    return nvRoot;
}

根的别名是在查询之间以某种随机方式生成的,所以让我们对它们进行硬编码。

 CriteriaBuilder cb = entityManager.getCriteriaBuilder();
 CriteriaQuery<Result> criteriaQuery = cb.createQuery(Result.class);
 Root<NvisionTrainee> nvRoot = criteriaQuery.from(Nv.class);
 // -- root alias --
 nvRoot.alias("nvRoot");
 Join<Object,Object> plans = nvRoot.join("plans", JoinType.LEFT);
 // -- root alias --
 plans.alias("plansRoot");
 // etc., other Joins

 Predicate where = cb.conjunction();
 // Complex Where clause built...
 criteriaQuery.where(where);


 CriteriaQuery<Long> cqCount = cb.createQuery(Long.class);
 // -- Added additional roots with the same alias names --
 Root<NvisionTrainee> nvRootCqCount  = cqCount.from(Nv.class);
 nvRootCqCount.alias("nvRoot");
 Join<Object,Object> plansCqCount = nvRootCqCount.join("plans", JoinType.LEFT);
 plansCqCount.alias("plansRoot");
 // etc., other Joins

 cqCount.select(cb.count(nvRootCqCount));     
 cqCount.distinct(true);
 // -- and here 'where' substituted with 'criteriaQuery.getRestriction()' --
 cqCount.where(criteriaQuery.getRestriction());
 Long totalCount = entityManager.createQuery(cqCount).getSingleResult();

 // --- THIS FULL QUERY WORKS (THE REMAINDER), IT GETS ME MY FULL SELECTION
 CompoundSelection<Result> selectionFull = cb.construct(
                               Result.class,
                               nvRoot.get("firstName"),
                               // etc. - many columns
                               );
 criteriaQuery.select(selectionFull);
 criteriaQuery.distinct(true);
 TypedQuery<Result> query = entityManager.createQuery(criteriaQuery);
 List<Result> results = query.getResultList();

手写的,所以我不确定这是否有效。我遇到过类似的错误问题:Invalid path: 'generatedAlias2.id'.