我正在尝试实现以下便捷方法:
/**
* Counts the number of results of a search.
* @param criteria The criteria for the query.
* @return The number of results of the query.
*/
public int findCountByCriteria(CriteriaQuery<?> criteria);
在Hibernate中,这是由
完成的criteria.setProjection(Projections.rowCount());
JPA中与上述内容相同的是什么?我找到了许多简单的计数示例,但没有一个使用了应该确定行数的CriteriaQuery。
修改
我遗憾地发现@Pascal的答案不正确。问题非常微妙,只有在使用连接时才显示:
// Same query, but readable:
// SELECT *
// FROM Brain b
// WHERE b.iq = 170
CriteriaQuery<Person> query = cb.createQuery(Person.class);
Root<Person> root = query.from(Person.class);
Join<Object, Object> brainJoin = root.join("brain");
Predicate iqPredicate = cb.equal(brainJoin.<Integer>get("iq"), 170);
query.select(root).where(iqPredicate);
调用findCountByCriteria(query)
时,它会因以下异常而死:
org.hibernate.hql.ast.QuerySyntaxException: Invalid path: 'generatedAlias1.iq' [select count(generatedAlias0) from xxx.tests.person.dom.Person as generatedAlias0 where generatedAlias1.iq=170]
有没有其他方法可以提供这样的CountByCriteria
方法?
答案 0 :(得分:17)
我写了一个实用工具类,JDAL JpaUtils来做它:
Long count = JpaUtils.count(em, criteriaQuery);
JpaUtils.copyCriteria(em, criteriaQueryFrom, criteriaQueryTo);
CriteriaQuery<Long> countCriteria = JpaUtils.countCriteria(em, criteria)
依旧......
如果您对源代码感兴趣,请参阅JpaUtils.java
答案 1 :(得分:4)
我使用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();
}
}
希望有所帮助:)
我所做的是类似于CriteriaBuilder的构建器,您可以使用相同的条件限制构建查询和调用list()或count()
答案 2 :(得分:1)
你在找这样的东西吗?
/**
* Counts the number of results of a search.
*
* @param criteria The criteria for the query.
* @return The number of results of the query.
*/
public <T> Long findCountByCriteria(CriteriaQuery<?> criteria) {
CriteriaBuilder builder = em.getCriteriaBuilder();
CriteriaQuery<Long> 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();
}
你可以这样使用:
// a search based on the Criteria API
CriteriaBuilder builder = em.getCriteriaBuilder();
CriteriaQuery<Person> criteria = builder.createQuery(Person.class);
Root<Person> personRoot = criteria.from(Person.class);
criteria.select(personRoot);
Predicate personRestriction = builder.and(
builder.equal(personRoot.get(Person_.gender), Gender.MALE),
builder.equal(personRoot.get(Person_.relationshipStatus), RelationshipStatus.SINGLE)
);
criteria.where(personRestriction);
//...
// and to get the result count of the above query
Long count = findCountByCriteria(criteria);
PS:我不知道这是否是实现此目的的正确/最佳方式,仍在学习Criteria API ...
答案 3 :(得分:1)
标准查询的整个概念是它们是强类型的。因此,每个解决方案都使用原始类型(在CriteriaQuery或Root或Root中没有泛型) - 这些解决方案都违背了这个主要想法。我只是遇到了同样的问题,我正在努力用“正确的”(以及JPA2)的方式解决它。
答案 4 :(得分:1)
以上解决方案都不适用于EclipseLink 2.4.1,它们都以笛卡尔积(N ^ 2)结束,这里是EclipseLink的一个小黑客,唯一的缺点是我不知道是什么如果您选择FROM多个实体,它将尝试从您的CriteriaQuery的第一个找到的根计数,这个解决方案虽然不适用于Hibernate(JDAL确实如此,但JDAL不适用于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();
}
答案 5 :(得分:1)
如果你想要结果和所有元素的计数,比如Spring Data&#39; s Page - 元素你可以做两个查询。您可以做的是将条件与查询执行分开。
按城市查找用户的示例
public List<User> getUsers(int userid, String city, other values ...) {
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<User> q = cb.createQuery(User.class);
Root<User> c = q.from(User.class);
List<Predicate> conditions = createConditions(c, cb, userid, city, ...other values);
List<User> users = em.createQuery(q.select(c).where(conditions.toArray(new Predicate[] {})).distinct(true))
.setMaxResults(PAGE_ELEMENTS).setFirstResult(page * PAGE_ELEMENTS).getResultList();
return users;
}
添加到getUser方法,您可以构建一个计算元素的第二个
public Long getElemCount(int userid, String city, ...other values) {
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Long> q = cb.createQuery(Long.class);
Root<Location> root = q.from(Location.class);
List<Predicate> conditions = createConditions(root, cb, userid, page, city, filter, module, isActive);
Long userCount = em.createQuery(q.select(cb.count(root)).where(conditions.toArray(new Predicate[] {})).distinct(true))
.getSingleResult();
return userCount;
}
并且createConditions方法将处理两者,因此您不必复制标准的逻辑。
<T> List<Predicate> createConditions(Root<T> root, CriteriaBuilder cb, int userid, String city, ... other values) {
Join<User, SecondEntity> usr = root.join("someField");
// add joins as you wish
/*
* Build Conditions
*/
List<Predicate> conditions = new ArrayList<>();
conditions.add(cb.equal(root.get("id"), userid));
if (!city.equals("")) {
conditions.add(cb.like(...));
}
// some more conditions...
return conditions;
}
在您的控制器中,您可以执行类似
的操作long elementCount = yourCriteriaClassInstance.getElementCount(...); List users = yourCriteriaClassInstance.getUsers(...)
答案 6 :(得分:-3)
我用hibernate和criteria api
做类似的事情public Long getRowsCount(List<Criterion> restrictions ) {
Criteria criteria = getSession().createCriteria(ThePersistenclass.class);
for (Criterion x : restrictions)
criteria.add(x);
return criteria.setProjection(Projections.rowCount()).uniqueResult();
}
希望帮助