[英]hibernate criteria count over with group by
I have a spring app with the user
entity and the users
table.我有一个带有
user
实体和users
表的 spring 应用程序。 I would like to get a number of all users grouped by certain fields (not per group but in total ).我想获得按某些字段分组的所有用户的数量(不是每个组,而是总数)。 In sql It would be:
在 sql 中它将是:
select
count(*) OVER () as totalRecords
from users u
group by
u.first_name,
u.last_name,
u.age
order by u.age DESC
OFFSET 1 ROWS FETCH NEXT 1 ROWS ONLY;
But I really can't do that using hibernate criteria.但我真的不能使用休眠标准来做到这一点。 I could do something like:
我可以做这样的事情:
public Long getTotalCount() {
ProjectionList groupBy = projectionList();
groupBy.add(groupProperty("firstName"), "first_name");
groupBy.add(groupProperty("last_name"), "last_name");
groupBy.add(groupProperty("age"), "age");
groupBy.add(Projections.rowCount());
return (Long) getSession().createCriteria("User")
.setProjection(groupBy)
.uniqueResult();
}
but it's not what I want.但这不是我想要的。 It does counting per each group, I would like to count rows that are the result of the
group by
clause它对每个组进行计数,我想计算
group by
子句的结果行
I just spend couple hours trying to find out a way and finally got it working.我只是花了几个小时试图找出一种方法,并最终让它发挥作用。
Disclaimer免责声明
It is impossible to do an optimal query with plain criteria API.使用普通标准 API 不可能进行最佳查询。 Optimal would be either
SELECT COUNT(*) FROM ( group by query here )
or SELECT COUNT(*) OVER ()
.最佳
SELECT COUNT(*) FROM ( group by query here )
是SELECT COUNT(*) FROM ( group by query here )
或SELECT COUNT(*) OVER ()
。 Neither is possible.两者都不可能。 To get an optimal query, use plain SQL if possible.
要获得最佳查询,请尽可能使用普通 SQL。 For my case using plain SQL was not possible, because I have constructed a very complex logic that builds criteria and I want to use the same logic for resolving the count of aggregate also (to resolve count of pages for pagination).
对于我的情况,使用纯 SQL 是不可能的,因为我构建了一个非常复杂的逻辑来构建标准,并且我想使用相同的逻辑来解决聚合计数(以解决分页的页面计数)。
Solution解决方案
First we add the following to all Entities that are used as base of criteria:首先,我们将以下内容添加到用作标准基础的所有实体中:
@Entity
class MyEntity {
private Long aggregateRowCount;
@Formula(value="count(*) over()")
public Long getAggregateRowCount() {
return aggregateRowCount;
}
public void setAggregateRowCount(Long aggregateRowCount) {
this.aggregateRowCount = aggregateRowCount;
}
Criteria building looks like this:标准构建看起来像这样:
Criteria criteria = // construct query here
ProjectionList projectionList = // construct Projections.groupProperty list here
projectionList.add(Projections.property("aggregateRowCount")); // this is our custom entity field with the @Formula annotation
criteria.setProjection(projectionList);
criteria.setMaxResults(1);
criteria.setResultTransformer(AggregatedCountResultTransformer.instance());
List<?> res = builder.criteria.list();
if (res.isEmpty()) return 0L;
return (Long) res.get(0);
This generates SQL that looks like this:这将生成如下所示的 SQL:
SELECT groupbyfield1, groupbyfield2, count(*) over()
FROM ...
GROUP BY groupbyfield1, groupbyfield2
LIMIT 1;
Without LIMIT 1 the result would be如果没有 LIMIT 1 结果将是
field1 | field2 | count
a | b | 12356
a | c | 12356
... | ... | 12356
but we add the LIMIT 1 ( criteria.setMaxResults(1);
) because the first row already contains the number of rows and that is all we need.但是我们添加了 LIMIT 1(
criteria.setMaxResults(1);
),因为第一行已经包含了行数,这就是我们所需要的。
Finally, our AggegatedCountResultTransformer:最后,我们的 AggregatedCountResultTransformer:
class AggregatedCountResultTransformer implements ResultTransformer {
private static final AggregatedCountResultTransformer instance = new AggregatedCountResultTransformer();
public static ResultTransformer instance() {
return instance;
}
@Override
public Object transformTuple(Object[] values, String[] fields) {
if (values.length == 0) throw new IllegalStateException("Values is empty");
return values[values.length-1]; // Last value of selected fields (the count)
}
@SuppressWarnings("rawtypes")
@Override
public List transformList(List allResults) {
return allResults; // This is not actually used?
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.