我只是花了几个小时试图找到一种方法并最终让它发挥作用。
免责声明
使用普通标准 API 进行最佳查询是不可能的。最佳选择是SELECT COUNT(*) FROM ( group by query here ) 或SELECT COUNT(*) OVER ()。两者都不可能。要获得最佳查询,请尽可能使用纯 SQL。对于我的情况,使用纯 SQL 是不可能的,因为我构建了一个非常复杂的逻辑来构建标准,并且我想使用相同的逻辑来解决聚合计数(解决分页的页面计数)。
解决方案
首先,我们将以下内容添加到用作标准基础的所有实体中:
@Entity
class MyEntity {
private Long aggregateRowCount;
@Formula(value="count(*) over()")
public Long getAggregateRowCount() {
return aggregateRowCount;
}
public void setAggregateRowCount(Long aggregateRowCount) {
this.aggregateRowCount = aggregateRowCount;
}
标准构建如下所示:
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);
这会生成如下所示的 SQL:
SELECT groupbyfield1, groupbyfield2, count(*) over()
FROM ...
GROUP BY groupbyfield1, groupbyfield2
LIMIT 1;
如果没有 LIMIT 1,结果将是
field1 | field2 | count
a | b | 12356
a | c | 12356
... | ... | 12356
但是我们添加了 LIMIT 1 (criteria.setMaxResults(1);),因为第一行已经包含了行数,这就是我们所需要的。
最后,我们的 AggegatedCountResultTransformer:
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?
}