[英]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
也需要設置這些別名。 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.