2010-05-21 32 views
83

我是相當新的JPA 2,它是CriteriaBuilder/CriteriaQuery中API:在JPA 2,使用CriteriaQuery中,怎麼算結果

CriteriaQuery javadoc

CriteriaQuery in the Java EE 6 tutorial

我想算結果CriteriaQuery的實際沒有檢索它們。這是可能的,我沒有發現任何這樣的方法,唯一的方法是這樣:

CriteriaBuilder cb = entityManager.getCriteriaBuilder(); 

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

// initialize predicates here 

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

這不能做正確的方式...

是否有解?

回答

158

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(); 

很明顯,您會希望通過您在示例中跳過的任何限制和分組等來構建表達式。

+3

這就是我已經想通自己,謝謝。但這意味着我不能使用相同的查詢實例來查詢結果數量和實際結果,我知道這些結果與SQL相似,但這會使此API更像OOP。那麼,我猜,至少我可以重用一些謂詞。 – 2010-05-21 18:34:48

+4

@Barett如果它是一個相當大的數量,你可能不想將數百或數千個實體加載到內存中,只是爲了找出有多少實體! – Affe 2012-12-10 06:31:19

+0

@Barett這是用於分頁的情況下。因此需要一個總數並且只有實際行的一個子集。 – gkephorus 2013-11-27 13:35:34

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

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

使用countDistinct而不是count

+0

你完全沒有得到這個問題。 – 2013-11-28 16:37:49

24

我使用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(); 
    } 
} 

希望它能幫助:)

1

這是一個有點棘手,取決於您使用JPA 2實現,這一項工程的EclipseLink 2.4.1,但不會對Hibernate,這裏的通用CriteriaQuery中計數的EclipseLink:

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(); 
    } 
+0

如果查詢已加入,該怎麼辦? – Dave 2014-06-07 20:24:44

+0

我認爲唯一的情況是危險的是當你有一個左連接,並且選中的根不是主要實體。否則,這並不重要,因爲無論選擇的實體如何,計數都是相同的。對於左連接實體,我相當確定選擇中的第一個實體是參考實體,例如,如果您有學生離開參加課程,那麼選擇學生應該是很自然的事情,因爲可能有學生沒有的課程就讀。 – 2014-06-08 12:00:55

+1

如果原始查詢是groupBy查詢,則結果將是每個組的一個計數。如果我們可以將一個CriteriaQuery製作成一個SubQuery,然後計算子查詢,它可以在任何情況下工作。我們能做到嗎? – Dave 2014-07-06 01:21:19

6

正如其他的答案是正確的,但是過於簡單,所以爲了完整起見,我在下面的代碼片段中展示SELECT COUNT複雜的 JPA標準查詢(具有多個聯接,提取,條件)。

它稍作修改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); 
    } 
} 

希望能節省一些人的時間。

因爲恕我直言,JPA Criteria API不直觀也不可讀。

+1

其實很可愛,你實際上認爲你的代碼是「複雜」的,因爲你在示例中添加了幾行......但它仍然很容易出錯。 – specializt 2015-09-13 19:30:18

+1

@specializt當然並不完美 - 例如上面的解決方案仍然缺少對提取的遞歸連接。但是你認爲僅僅因爲這個原因,我不應該分享我的想法?恕我直言分享知識是StackOverfow背後的主要思想。 – 2015-09-14 06:30:42

+0

數據庫上的遞歸總是可以想象的最糟糕的解決方案...這是一個初學者的錯誤。 – specializt 2015-09-14 07:58:10

0

您還可以使用預測:

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

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

恐怕Projections API是Hibernate特有的,但問題是關於JPA 2的問題。 – gersonZaragocin 2018-03-02 21:39:20