簡體   English   中英

如何計算通用JPA DAO中JPA 2 CriteriaQuery的行數?

[英]How to count the number of rows of a JPA 2 CriteriaQuery in a generic JPA DAO?

我是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();
}

但是,使用join時該代碼不起作用。 有沒有辦法使用JPA Criteria API計算查詢結果集的行數?

更新:這是創建CriteriaQuery的代碼:

    CriteriaQuery<T> queryDefinition = criteriaBuilder.createQuery(this.entityClass);
    Root<T> root = queryDefinition.from(this.entityClass);

並且可以將一些聯接添加到根,直到執行查詢為止:

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

並且生成的異常如下:

org.hibernate.hql.ast.QuerySyntaxException:無效路徑:'generatedAlias1.id'[從entity.Entity中選擇count(generatedAlias0)為generatedAlias0,其中(generatedAlias0.id> = 13L)和((generatedAlias1.id <= 34L)) ]

其中generatedAlias1應該在Entity上,generatedAlias0應該在我加入的關聯上。 請注意,我正確實現了Join,因為當我執行不帶計數查詢的查詢時,它執行時沒有錯誤,並且Join正常工作但是當我嘗試執行count查詢時它會拋出異常。

我這樣做了:

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);
    }
}

當然,您不需要Root作為輸入參數,您可以從條件查詢中獲取它,

@ lubo08給出了正確的答案 - 對他不以為然。 但對於兩個角落的情況,他/她的代碼將不起作用:

  • 當條件查詢的限制使用別名進行連接時 - 那么COUNT也需要設置這些別名。
  • 條件查詢使用fetch join [ root.fetch(..)而不是root.join(..) ]

因此,為了完整起見,我敢於改進他/她的解決方案,並在下面提出:

public <T> long count(final CriteriaBuilder cb, final CriteriaQuery<T> criteria,
        Root<T> root) {
    CriteriaQuery<Long> query = createCountQuery(cb, criteria, root);
    return this.entityManager.createQuery(query).getSingleResult();
}

private <T> CriteriaQuery<Long> createCountQuery(final CriteriaBuilder cb,
        final CriteriaQuery<T> criteria, final Root<T> root) {

    final CriteriaQuery<Long> countQuery = cb.createQuery(Long.class);
    final Root<T> countRoot = countQuery.from(criteria.getResultType());

    doJoins(root.getJoins(), countRoot);
    doJoinsOnFetches(root.getFetches(), countRoot);

    countQuery.select(cb.count(countRoot));
    countQuery.where(criteria.getRestriction());

    countRoot.alias(root.getAlias());

    return countQuery.distinct(criteria.isDistinct());
}

@SuppressWarnings("unchecked")
private void doJoinsOnFetches(Set<? extends Fetch<?, ?>> joins, Root<?> root) {
    doJoins((Set<? extends Join<?, ?>>) joins, root);
}

private void doJoins(Set<? extends Join<?, ?>> joins, Root<?> root) {
    for (Join<?, ?> join : joins) {
        Join<?, ?> joined = root.join(join.getAttribute().getName(), join.getJoinType());
        joined.alias(join.getAlias());
        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());
        joined.alias(join.getAlias());
        doJoins(join.getJoins(), joined);
    }
}

雖然它仍然不完美 ,因為只有一個根被尊重。
但我希望它對某人有所幫助。

我不能告訴你你的問題在哪里,但我可以告訴你,連接的計數查詢工作得很好,至少在eclipselink jpa中。 我的猜測是這是標准的東西,所以它也應該在hibernate中工作。 我將從簡化代碼開始,以便了解問題所在。 我看到你從主查詢中復制了一些續查詢。 也許您可以嘗試改變這種方法,僅用於調試目的。

我通常做的是:

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);

看看你的偽代碼,似乎你在join方法中使用了錯誤的類:它必須是起始類,而不是目標類。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM