Java 在 JPA 2 中,使用 CriteriaQuery,如何计算结果

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2883887/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 14:02:40  来源:igfitidea点击:

In JPA 2, using a CriteriaQuery, how to count results

javajpa-2.0criteriaquery

提问by Sean Patrick Floyd

I am rather new to JPA 2 and it's CriteriaBuilder / CriteriaQuery API:

我对 JPA 2 比较陌生,它是 CriteriaBuilder / CriteriaQuery API:

CriteriaQueryjavadoc

CriteriaQuery文档

CriteriaQueryin the Java EE 6 tutorial

CriteriaQuery在 Java EE 6 教程中

I would like to count the results of a CriteriaQuery without actually retrieving them. Is that possible, I did not find any such method, the only way would be to do this:

我想计算 CriteriaQuery 的结果而不实际检索它们。这可能吗,我没有找到任何这样的方法,唯一的方法就是这样做:

CriteriaBuilder cb = entityManager.getCriteriaBuilder();

CriteriaQuery<MyEntity> cq = cb
        .createQuery(MyEntityclass);

// initialize predicates here

return entityManager.createQuery(cq).getResultList().size();

And that can't be the proper way to do it...

这不是正确的方法......

Is there a solution?

有解决办法吗?

采纳答案by Affe

A query of type MyEntityis going to return MyEntity. You want a query for a Long.

类型的查询MyEntity将返回MyEntity。您想要查询Long.

CriteriaBuilder qb = entityManager.getCriteriaBuilder();
CriteriaQuery<Long> cq = qb.createQuery(Long.class);
cq.select(qb.count(cq.from(MyEntity.class)));
cq.where(/*your stuff*/);
return entityManager.createQuery(cq).getSingleResult();

Obviously you will want to build up your expression with whatever restrictions and groupings etc you skipped in the example.

显然,您将希望使用示例中跳过的任何限制和分组等来构建您的表达式。

回答by axtavt

CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Long> cq = cb.createQuery(Long.class);
cq.select(cb.count(cq.from(MyEntity.class)));

return em.createQuery(cq).getSingleResult();

回答by reyiyo

I've sorted this out using the cb.createQuery() (without the result type parameter):

我已经使用 cb.createQuery() (没有结果类型参数)对此进行了整理:

public class Blah() {

    CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
    CriteriaQuery query = criteriaBuilder.createQuery();
    Root<Entity> root;
    Predicate whereClause;
    EntityManager entityManager;
    Class<Entity> domainClass;

    ... Methods to create where clause ...

    public Blah(EntityManager entityManager, Class<Entity> domainClass) {
        this.entityManager = entityManager;
        this.domainClass = domainClass;
        criteriaBuilder = entityManager.getCriteriaBuilder();
        query = criteriaBuilder.createQuery();
        whereClause = criteriaBuilder.equal(criteriaBuilder.literal(1), 1);
        root = query.from(domainClass);
    }

    public CriteriaQuery<Entity> getQuery() {
        query.select(root);
        query.where(whereClause);
        return query;
    }

    public CriteriaQuery<Long> getQueryForCount() {
        query.select(criteriaBuilder.count(root));
        query.where(whereClause);
        return query;
    }

    public List<Entity> list() {
        TypedQuery<Entity> q = this.entityManager.createQuery(this.getQuery());
        return q.getResultList();
    }

    public Long count() {
        TypedQuery<Long> q = this.entityManager.createQuery(this.getQueryForCount());
        return q.getSingleResult();
    }
}

Hope it helps :)

希望能帮助到你 :)

回答by Guido Medina

It is a bit tricky, depending on the JPA 2 implementation you use, this one works for EclipseLink 2.4.1, but doesn't for Hibernate, here a generic CriteriaQuery count for EclipseLink:

这有点棘手,具体取决于您使用的 JPA 2 实现,这个适用于 EclipseLink 2.4.1,但不适用于 Hibernate,这里是 EclipseLink 的通用 CriteriaQuery 计数:

public static Long count(final EntityManager em, final CriteriaQuery<?> criteria)
  {
    final CriteriaBuilder builder=em.getCriteriaBuilder();
    final CriteriaQuery<Long> countCriteria=builder.createQuery(Long.class);
    countCriteria.select(builder.count(criteria.getRoots().iterator().next()));
    final Predicate
            groupRestriction=criteria.getGroupRestriction(),
            fromRestriction=criteria.getRestriction();
    if(groupRestriction != null){
      countCriteria.having(groupRestriction);
    }
    if(fromRestriction != null){
      countCriteria.where(fromRestriction);
    }
    countCriteria.groupBy(criteria.getGroupList());
    countCriteria.distinct(criteria.isDistinct());
    return em.createQuery(countCriteria).getSingleResult();
  }

The other day I migrated from EclipseLink to Hibernate and had to change my count function to the following, so feel free to use either as this is a hard problem to solve, it might not work for your case, it has been in use since Hibernate 4.x, notice that I don't try to guess which is the root, instead I pass it from the query so problem solved, too many ambiguous corner cases to try to guess:

前几天我从 EclipseLink 迁移到 Hibernate 并且不得不将我的计数函数更改为以下内容,因此请随意使用,因为这是一个难以解决的问题,它可能不适用于您的情况,它自 Hibernate 以来一直在使用4.x,请注意,我没有尝试猜测哪个是根,而是从查询中传递它以便解决问题,太多模棱两可的极端情况无法尝试猜测:

  public static <T> long count(EntityManager em,Root<T> root,CriteriaQuery<T> criteria)
  {
    final CriteriaBuilder builder=em.getCriteriaBuilder();
    final CriteriaQuery<Long> countCriteria=builder.createQuery(Long.class);

    countCriteria.select(builder.count(root));

    for(Root<?> fromRoot : criteria.getRoots()){
      countCriteria.getRoots().add(fromRoot);
    }

    final Predicate whereRestriction=criteria.getRestriction();
    if(whereRestriction!=null){
      countCriteria.where(whereRestriction);
    }

    final Predicate groupRestriction=criteria.getGroupRestriction();
    if(groupRestriction!=null){
      countCriteria.having(groupRestriction);
    }

    countCriteria.groupBy(criteria.getGroupList());
    countCriteria.distinct(criteria.isDistinct());
    return em.createQuery(countCriteria).getSingleResult();
  }

回答by G. Demecki

As others answers are correct, but too simple, so for completeness I'm presenting below code snippet to perform SELECT COUNTon a sophisticatedJPA Criteria query (with multiple joins, fetches, conditions).

由于其他答案是正确的,但太简单了,所以为了完整起见,我将在下面的代码片段中展示SELECT COUNT一个复杂的JPA Criteria 查询(具有多个连接、提取、条件)。

It is slightly modified this answer.

这个答案略有修改。

public <T> long count(final CriteriaBuilder cb, final CriteriaQuery<T> selectQuery,
        Root<T> root) {
    CriteriaQuery<Long> query = createCountQuery(cb, selectQuery, 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);
    }
}

Hope it saves somebody's time.

希望它可以节省某人的时间。

Because IMHO JPA Criteria API is not intuitive nor quite readable.

因为恕我直言 JPA Criteria API 既不直观也不可读。

回答by Pavel Evstigneev

You can also use Projections:

您还可以使用投影:

ProjectionList projection = Projections.projectionList();
projection.add(Projections.rowCount());
criteria.setProjection(projection);

Long totalRows = (Long) criteria.list().get(0);

回答by kafkas

With Spring Data Jpa, we can use this method:

使用 Spring Data Jpa,我们可以使用这种方法:

    /*
     * (non-Javadoc)
     * @see org.springframework.data.jpa.repository.JpaSpecificationExecutor#count(org.springframework.data.jpa.domain.Specification)
     */
    @Override
    public long count(@Nullable Specification<T> spec) {
        return executeCountQuery(getCountQuery(spec, getDomainClass()));
    }