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

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

CriteriaQuery javadoc

JavaEE6教程中的 CriteriaQuery

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

CriteriaBuilder cb = entityManager.getCriteriaBuilder();


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


// initialize predicates here


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

这不是正确的方法。

有解决办法吗?

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


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

类型为 MyEntity的查询将返回 MyEntity

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

很明显,您想要使用您在示例中跳过的限制和分组等来构建表达式。

我使用 cb.createQuery ()(没有 result type 参数)解决了这个问题:

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

希望对你有所帮助:)

这有点棘手,这取决于您使用的 JPA2实现,它适用于 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();
}

前几天我从 EclipseLink 迁移到 Hibernate,不得不把 count 函数改成如下,所以你可以随意使用,因为这是一个很难解决的问题,它可能不适合你的情况,它从 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();
}

正如其他的答案是正确的,但是太简单了,所以为了完整起见,我在下面展示了对 成熟 JPA Criteria 查询执行 SELECT COUNT的代码片段(包含多个连接、获取和条件)。

它稍微修改了 这个答案

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

希望能节省某人的时间。

因为 IMHO JPA Criteria API 既不直观也不易读。

你也可以使用 Projections:

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


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