如何计算通用JPA DAO中JPA 2 CriteriaQuery的行数?

sta*_*ker 7 java orm pagination jpa count

我是JPA的新手,想要实现一个通用的JPA DAO,需要找到查询结果集的行数来实现分页.搜索网络后,我找不到切实可行的方法.以下是许多文章中建议的代码:

public <T> Long findCountByCriteria(CriteriaQuery<?> criteria) {
    CriteriaBuilder builder = em.getCriteriaBuilder();

    CriteriaQuery<Long> countCriteria = builder.createQuery(Long.class);
    Root<?> entityRoot = countCriteria.from(criteria.getResultType());
    countCriteria.select(builder.count(entityRoot));
    countCriteria.where(criteria.getRestriction());

    return em.createQuery(countCriteria).getSingleResult();
}
Run Code Online (Sandbox Code Playgroud)

但是,该代码在使用时不起作用join.有没有办法使用JPA Criteria API计算查询结果集的行数?

更新:这是创建CriteriaQuery的代码:

    CriteriaQuery<T> queryDefinition = criteriaBuilder.createQuery(this.entityClass);
    Root<T> root = queryDefinition.from(this.entityClass);
Run Code Online (Sandbox Code Playgroud)

并且可以将一些联接添加到根,直到执行查询为止:

public Predicate addPredicate(Root<T> root) {
                Predicate predicate = getEntityManager().getCriteriaBuilder().ge(root.join(Entity_.someList).get("id"), 13);
                return predicate;
}
Run Code Online (Sandbox Code Playgroud)

并且生成的异常如下:

org.hibernate.hql.ast.QuerySyntaxException:无效路径:'generatedAlias1.id'[从entity.Entity中选择count(generatedAlias0)为generatedAlias0,其中(generatedAlias0.id> = 13L)和((generatedAlias1.id <= 34L)) ]

其中generatedAlias1应该在Entity上,generatedAlias0应该在我加入的关联上.请注意,我正确实现了Join,因为当我执行不带计数查询的查询时,它执行时没有错误,并且Join正常工作但是当我尝试执行count查询时它会抛出异常.

小智 7

我这样做了:

public Long getRowCount(CriteriaQuery criteriaQuery,CriteriaBuilder criteriaBuilder,Root<?> root){
    CriteriaQuery<Long> countCriteria = criteriaBuilder.createQuery(Long.class);
    Root<?> entityRoot = countCriteria.from(root.getJavaType());
    entityRoot.alias(root.getAlias());
    doJoins(root.getJoins(),entityRoot);
    countCriteria.select(criteriaBuilder.count(entityRoot));
    countCriteria.where(criteriaQuery.getRestriction());
    return this.entityManager.createQuery(countCriteria).getSingleResult();
}

private void doJoins(Set<? extends Join<?, ?>> joins,Root<?> root_){
    for(Join<?,?> join: joins){
        Join<?,?> joined = root_.join(join.getAttribute().getName(),join.getJoinType());
        doJoins(join.getJoins(), joined);
    }
}

private void doJoins(Set<? extends Join<?, ?>> joins,Join<?,?> root_){
    for(Join<?,?> join: joins){
        Join<?,?> joined = root_.join(join.getAttribute().getName(),join.getJoinType());
        doJoins(join.getJoins(),joined);
    }
}
Run Code Online (Sandbox Code Playgroud)

当然,您不需要Root作为输入参数,您可以从条件查询中获取它,


per*_*ssf 2

我无法告诉你问题出在哪里,但我可以告诉你,带有连接的计数查询工作得很好,至少在eclipselinkjpa 中是这样。我的猜测是,这是标准的东西,所以它也应该在休眠状态下工作。我将首先简化您的代码,以便找出问题所在。我发现您已经从主查询中复制了部分连续查询。也许您可以尝试稍微改变一下这种方法,只是为了调试目的。

我通常做的事情是:

CriteriaQuery cqCount = builder.createQuery();
Root<T> root = cq.from(T.class);
cqCount.select(builder.count(root)); 
ListJoin<T, Entity> join = root.join(T_.someList);
Predicate predicate = builder.ge(join.get(Entity_.id), "myId"); 
cqCount.where(predicate);
TypedQuery<Long> q = em.createQuery(cqCount);
Run Code Online (Sandbox Code Playgroud)

查看您的伪代码,您似乎在 join 方法中使用了错误的类:它必须是起始类,而不是目标类。