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

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

public  Long findCountByCriteria(CriteriaQuery criteria) { CriteriaBuilder builder = em.getCriteriaBuilder(); CriteriaQuery 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 queryDefinition = criteriaBuilder.createQuery(this.entityClass); Root root = queryDefinition.from(this.entityClass); 

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

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

并且生成的exception如下:

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

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

我做到了这个:

 public Long getRowCount(CriteriaQuery criteriaQuery,CriteriaBuilder criteriaBuilder,Root root){ CriteriaQuery 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> joins,Root root_){ for(Join join: joins){ Join joined = root_.join(join.getAttribute().getName(),join.getJoinType()); doJoins(join.getJoins(), joined); } } private void doJoins(Set> 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  long count(final CriteriaBuilder cb, final CriteriaQuery criteria, Root root) { CriteriaQuery query = createCountQuery(cb, criteria, root); return this.entityManager.createQuery(query).getSingleResult(); } private  CriteriaQuery createCountQuery(final CriteriaBuilder cb, final CriteriaQuery criteria, final Root root) { final CriteriaQuery countQuery = cb.createQuery(Long.class); final Root 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> joins, Root root) { doJoins((Set>) joins, root); } private void doJoins(Set> 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> 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 root = cq.from(T.class); cqCount.select(builder.count(root)); ListJoin join = root.join(T_.someList); Predicate predicate = builder.ge(join.get(Entity_.id), "myId"); cqCount.where(predicate); TypedQuery q = em.createQuery(cqCount); 

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