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

Sea*_*oyd 109 java criteriaquery jpa-2.0

我对JPA 2很新,它是CriteriaBuilder/CriteriaQuery API:

CriteriaQuery 的javadoc

CriteriaQuery 在Java EE 6教程中

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

CriteriaBuilder cb = entityManager.getCriteriaBuilder();

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

// initialize predicates here

return entityManager.createQuery(cq).getResultList().size();
Run Code Online (Sandbox Code Playgroud)

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

有解决方案吗?

Aff*_*ffe 205

类型的查询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();
Run Code Online (Sandbox Code Playgroud)

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

  • @Barett如果它是一个相当大的计数你可能不想将数百或数千个实体的列表加载到内存中,只是为了找出有多少实体! (6认同)
  • 这就是我自己想到的,谢谢.但这意味着我不能使用相同的查询实例来查询结果的数量,而且我知道的实际结果类似于SQL,但这会使这个API更像OOP.好吧,至少我可以重用一些谓词,我猜. (3认同)
  • 请注意,`qb.count`是在查询的`Root <MyEntity>`上完成的(`Root <MyEntity>`myEntity = cq.from(MyEntity.class)),这通常已经在您的正常选择代码中当你忘了你最终加入自我. (2认同)
  • 要重复使用相同的标准来检索对象和计数,您可能需要在根上使用别名,请参阅https://forum.hibernate.org/viewtopic.php?p=2471522#p2471522以获取示例. (2认同)
  • 在 where 条件下对我不起作用。它给了我表中记录的总数。忽略 where 条件,有什么理由吗? (2认同)

rey*_*iyo 30

我使用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();
    }
}
Run Code Online (Sandbox Code Playgroud)

希望能帮助到你 :)


axt*_*avt 22

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

return em.createQuery(cq).getSingleResult();
Run Code Online (Sandbox Code Playgroud)


G. *_*cki 11

正如其他的答案是正确的,但是过于简单,所以完整性我提出下面的代码片段来执行SELECT COUNT复杂的 JPA标准查询(有多个连接,获取,条件).

这个答案略有修改.

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

希望它节省了一些人的时间.

因为IMHO JPA Criteria API不直观也不易阅读.

  • @specializt当然它并不完美 - 例如上面的解决方案仍然缺少对提取的递归连接.但是你认为仅仅因为这个我不应该分享我的想法吗?恕我直言共享知识是StackOverfow背后的主要思想. (2认同)

Gui*_*ina 5

这有点棘手,具体取决于您使用的 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();
  }
Run Code Online (Sandbox Code Playgroud)

前几天我从 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();
  }
Run Code Online (Sandbox Code Playgroud)