[英]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.