【问题标题】:hibernate criteria count over with group by休眠条件通过分组计数
【发布时间】:2019-02-19 13:15:13
【问题描述】:

我有一个带有 user 实体和 users 表的 spring 应用程序。我想获得按某些字段分组的所有用户的数量(不是每个组,而是总计)。 在 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;

但我真的不能使用休眠标准来做到这一点。我可以这样做:

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

但这不是我想要的。它确实按每组计数,我想计算group by 子句的结果行

【问题讨论】:

    标签: java hibernate criteria hibernate-criteria


    【解决方案1】:

    我只是花了几个小时试图找到一种方法并最终让它发挥作用。

    免责声明

    使用普通标准 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?
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-06-30
      • 1970-01-01
      • 2017-12-25
      • 1970-01-01
      • 1970-01-01
      • 2012-12-21
      • 1970-01-01
      • 2011-07-14
      相关资源
      最近更新 更多