使用 Criteria API 计算元组查询的总数?
Calculate total count for a tuple query using Criteria API?
我有一个Tag
table(device_id、customer_id是这个table中的外键):
------------------------------------------
| name | value | device_id | customer_id |
------------------------------------------
| a | a | 10 | 2389 |
------------------------------------------
| a | a | 20 | 2389 |
------------------------------------------
| a | a | 30 | 2389 |
------------------------------------------
|tag-n | tag-v | 10 | 2389 |
------------------------------------------
我正在尝试获取带有名称和值的标签(作为搜索过滤器)并取回 device_id
计数、name
和 value
。
示例: 如果我按 name = a and value = a
搜索,则响应应为:
{
"customer_id": "2389",
"tags": [
{
"name": "a",
"value": "a",
"device_count": 3 //since 3 devices have same name/value pairs
}
],
"pagination": {
"offset": 0,
"page": 0,
"count_per_page": 1,
"total_count": 1
}
}
我的服务逻辑中的 Criteria 查询是:
PageRequest pageRequest = (!StringUtils.isBlank(sortBy)) ? PageRequest.of(page, limit, Sort.by(sortBy)) : PageRequest.of(page, limit);
CriteriaBuilder criteriaBuilder = em.getCriteriaBuilder();
CriteriaQuery<Tuple> query = criteriaBuilder.createTupleQuery();
Root<TagEntity> root = query.from(TagEntity.class);
// add predicates
Predicate[] predArray = new Predicate[predicates.size()];
predicates.toArray(predArray);
query.multiselect(root.get(TagEntity_.name), root.get(TagEntity_.value), criteriaBuilder.count(deviceJoin.get(DeviceEntity_.ID)))
.where(predArray).groupBy(root.get(TagEntity_.name), root.get(TagEntity_.value));
TypedQuery<Tuple> typedQuery = em.createQuery(query);
//this works fine
List<Tuple> result = typedQuery
.setFirstResult((int) pageRequest.getOffset())
.setMaxResults(pageRequest.getPageSize())
.getResultList();
//This code fails with InvalidPathException: 'generatedAlias2.customerId'
CriteriaQuery<Long> countQuery = criteriaBuilder.createQuery(Long.class);
Root<TagEntity> tagCountRoot = countQuery.from(TagEntity.class);
countQuery.select(criteriaBuilder.count(tagCountRoot)).where(predArray);
Long count = em.createQuery(countQuery).getResultList().get(0);
Page<Tuple> tagEntities = new PageImpl<>(result, pageRequest, count);
我的问题是如何编写总计数查询,因为在我的例子中,我得到的是 Tuple
而不是整个 TagEntity
。任何帮助将不胜感激。
TIA。
P.S。我在同一个 method
中创建了 Predicates
,如下所示:
List<Predicate> predicates = new ArrayList<>();
Join<TagEntity, DeviceEntity> deviceJoin = root.join(TagEntity_.deviceEntity, JoinType.LEFT);
Join<TagEntity, CustomerEntity> customerJoin = root.join(TagEntity_.customerEntity, JoinType.LEFT);
predicates.add(criteriaBuilder.equal(customerJoin.get(CustomerEntity_.customerId), customerId));
//search by exact name and exact value
predicates.add(criteriaBuilder.and(criteriaBuilder.equal(root.get(TagEntity_.name), exactName), criteriaBuilder.equal(root.get(TagEntity_.value), exactValue)));
您必须始终使用带分页的排序,即使是默认情况下也是如此。
计算distinct(name, value)
对更简单。不要忘记对它们进行排序。
这里最好使用内连接
Join<TagEntity, CustomerEntity> customerJoin = root.join(TagEntity_.customerEntity, JoinType.LEFT);
问题已解决。对于可能导致 exception
的两个查询,我使用了在方法中定义的相同 predicates
数组。一个更好的方法是创建一个 specification
如下:
private Specification<TagEntity> getTagEntitySpecification(String customerId, String name, String value) {
return (Root<TagEntity> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) -> {
List<Predicate> predicates = new ArrayList<>();
Join<TagEntity, CustomerEntity> customerJoin = root.join(TagEntity_.customerEntity, JoinType.INNER);
predicates.add(criteriaBuilder.equal(customerJoin.get(CustomerEntity_.customerId), customerId));
//search by name and value
predicates.add(criteriaBuilder.and(criteriaBuilder.equal(root.get(TagEntity_.name), exactName),
criteriaBuilder.equal(root.get(TagEntity_.value), exactValue)));
return criteriaBuilder.and(predicates.toArray(new Predicate[0]));
};
}
在 where
子句中使用 specification.toPredicate(root, criteriaQuery, criteriaBuilder)
如下所示:
final Specification<TagEntity> specification = getTagEntitySpecification(customerId, name, value);
// query 1
query.multiselect(root.get(TagEntity_.name), root.get(TagEntity_.value),criteriaBuilder.count(deviceJoin.get(DeviceEntity_.ID))).where(specification.toPredicate(root, query, criteriaBuilder)).groupBy(root.get(TagEntity_.name), root.get(TagEntity_.value));
//count query
countQuery.select(cbCount.count(tagCountRoot)).where(specification.toPredicate(tagCountRoot, countQuery, cbCount));
Long count = em.createQuery(countQuery).getResultList().get(0);
tagEntities = new PageImpl<>(result, pageRequest, count);
我有一个Tag
table(device_id、customer_id是这个table中的外键):
------------------------------------------
| name | value | device_id | customer_id |
------------------------------------------
| a | a | 10 | 2389 |
------------------------------------------
| a | a | 20 | 2389 |
------------------------------------------
| a | a | 30 | 2389 |
------------------------------------------
|tag-n | tag-v | 10 | 2389 |
------------------------------------------
我正在尝试获取带有名称和值的标签(作为搜索过滤器)并取回 device_id
计数、name
和 value
。
示例: 如果我按 name = a and value = a
搜索,则响应应为:
{
"customer_id": "2389",
"tags": [
{
"name": "a",
"value": "a",
"device_count": 3 //since 3 devices have same name/value pairs
}
],
"pagination": {
"offset": 0,
"page": 0,
"count_per_page": 1,
"total_count": 1
}
}
我的服务逻辑中的 Criteria 查询是:
PageRequest pageRequest = (!StringUtils.isBlank(sortBy)) ? PageRequest.of(page, limit, Sort.by(sortBy)) : PageRequest.of(page, limit);
CriteriaBuilder criteriaBuilder = em.getCriteriaBuilder();
CriteriaQuery<Tuple> query = criteriaBuilder.createTupleQuery();
Root<TagEntity> root = query.from(TagEntity.class);
// add predicates
Predicate[] predArray = new Predicate[predicates.size()];
predicates.toArray(predArray);
query.multiselect(root.get(TagEntity_.name), root.get(TagEntity_.value), criteriaBuilder.count(deviceJoin.get(DeviceEntity_.ID)))
.where(predArray).groupBy(root.get(TagEntity_.name), root.get(TagEntity_.value));
TypedQuery<Tuple> typedQuery = em.createQuery(query);
//this works fine
List<Tuple> result = typedQuery
.setFirstResult((int) pageRequest.getOffset())
.setMaxResults(pageRequest.getPageSize())
.getResultList();
//This code fails with InvalidPathException: 'generatedAlias2.customerId'
CriteriaQuery<Long> countQuery = criteriaBuilder.createQuery(Long.class);
Root<TagEntity> tagCountRoot = countQuery.from(TagEntity.class);
countQuery.select(criteriaBuilder.count(tagCountRoot)).where(predArray);
Long count = em.createQuery(countQuery).getResultList().get(0);
Page<Tuple> tagEntities = new PageImpl<>(result, pageRequest, count);
我的问题是如何编写总计数查询,因为在我的例子中,我得到的是 Tuple
而不是整个 TagEntity
。任何帮助将不胜感激。
TIA。
P.S。我在同一个 method
中创建了 Predicates
,如下所示:
List<Predicate> predicates = new ArrayList<>();
Join<TagEntity, DeviceEntity> deviceJoin = root.join(TagEntity_.deviceEntity, JoinType.LEFT);
Join<TagEntity, CustomerEntity> customerJoin = root.join(TagEntity_.customerEntity, JoinType.LEFT);
predicates.add(criteriaBuilder.equal(customerJoin.get(CustomerEntity_.customerId), customerId));
//search by exact name and exact value
predicates.add(criteriaBuilder.and(criteriaBuilder.equal(root.get(TagEntity_.name), exactName), criteriaBuilder.equal(root.get(TagEntity_.value), exactValue)));
您必须始终使用带分页的排序,即使是默认情况下也是如此。
计算distinct(name, value)
对更简单。不要忘记对它们进行排序。
这里最好使用内连接
Join<TagEntity, CustomerEntity> customerJoin = root.join(TagEntity_.customerEntity, JoinType.LEFT);
问题已解决。对于可能导致 exception
的两个查询,我使用了在方法中定义的相同 predicates
数组。一个更好的方法是创建一个 specification
如下:
private Specification<TagEntity> getTagEntitySpecification(String customerId, String name, String value) {
return (Root<TagEntity> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) -> {
List<Predicate> predicates = new ArrayList<>();
Join<TagEntity, CustomerEntity> customerJoin = root.join(TagEntity_.customerEntity, JoinType.INNER);
predicates.add(criteriaBuilder.equal(customerJoin.get(CustomerEntity_.customerId), customerId));
//search by name and value
predicates.add(criteriaBuilder.and(criteriaBuilder.equal(root.get(TagEntity_.name), exactName),
criteriaBuilder.equal(root.get(TagEntity_.value), exactValue)));
return criteriaBuilder.and(predicates.toArray(new Predicate[0]));
};
}
在 where
子句中使用 specification.toPredicate(root, criteriaQuery, criteriaBuilder)
如下所示:
final Specification<TagEntity> specification = getTagEntitySpecification(customerId, name, value);
// query 1
query.multiselect(root.get(TagEntity_.name), root.get(TagEntity_.value),criteriaBuilder.count(deviceJoin.get(DeviceEntity_.ID))).where(specification.toPredicate(root, query, criteriaBuilder)).groupBy(root.get(TagEntity_.name), root.get(TagEntity_.value));
//count query
countQuery.select(cbCount.count(tagCountRoot)).where(specification.toPredicate(tagCountRoot, countQuery, cbCount));
Long count = em.createQuery(countQuery).getResultList().get(0);
tagEntities = new PageImpl<>(result, pageRequest, count);