【问题标题】:Is it possible to add count(*) and groupBy to Spring Data Jpa Specification?是否可以将 count(*) 和 groupBy 添加到 Spring Data Jpa 规范?
【发布时间】:2020-03-01 19:34:53
【问题描述】:

我正在使用 Spring Boot 和 Spring Data Jpa 编写应用程序。

我有一个使用 RSQL(我使用 this 库)和规范的方法,通过 where 子句对 db 进行过滤器调用。

我的仓库:

public interface BaseRepository<M extends BaseModel> extends JpaRepository<M, Long>, JpaSpecificationExecutor<M>{

}

在服务中我调用了存储库:

String filter = "createdDate > 2019-09-04T11:52:59.449";
return repository.findAll(toSpecification(filter), pageable).getContent();  

所以我使用上面的库来生成规范并将其传递给存储库。

现在我需要按某些字段对结果进行分组。

如何在规范中添加 countgroupBy 的问题?或者我可能需要创建一个谓词,在其中定义这个 countgroupBy (不知何故)并以此制定规范?有什么更好的方法?

更新 #0

我已经设法将一个组按部分添加到结果 sql 查询中。我创建了一个新的规范对象,在其中按参数定义所有组,然后将此规范与原始规范结合起来。

String filter = "createdDate > 2019-09-04T11:52:59.449";

Specification<M> groupBySpec = new Specification<M>() {

            @Override
            public Predicate toPredicate(Root<M> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {

                List<Expression<?>> groupByParams = new ArrayList<>();

                groupByParams.add(root.get("param1"));
                groupByParams.add(root.get("param2"));

                query.groupBy(groupByParams);

                return query.getGroupRestriction();
            }
        };


return repository.findAll(groupBySpec.and(toSpecification(filter)), pageable).getContent();     

问题是它总是选择所有列,但我只需要选择那些按部分分组的列。

结果我得到一个 sql 异常:ERROR: column "columnName" must appear in the GROUP BY clause or be used in an aggregate function

我尝试过像 criteriaQuery.multiselect(root.get("column1"), root.get("column2")).distinct(true).getGroupRestriction(); 这样使用多选,但它还是选择了所有列。

如果我使用 @Query("query") 来定义要选择的列,但我需要它使用 Criteria Api/Specification 才能工作。

【问题讨论】:

  • 你的分组依据是什么,你能给我们一个你正在查询的类的 sn-p。
  • @locus2k 应该可以按从请求 http 参数中获取的不同字段进行分组(例如 orderBy = field1, field2, field3)。这实际上就是过滤器的工作原理(我有一个 http param filter = field1, field2.... 我解析),它用于为 sql 查询创建 where 子句(问题中的静态字符串仅用于示例)。而且我要查询的类非常简单:它只包含一些字段,并且与其他模型没有任何关系。
  • 我问是因为看起来 RSQL 仅适用于 WHERE 子句进行过滤,而不适用于分组。 Spring 可以做到groupBy,但你必须为它编写查询,这需要知道要查询的字段
  • @locus2k 是的,我知道 RSQL 仅适用于 WHERE。问题是如何将 group by 添加到库已经生成的结果规范中。你到底在说什么类型的查询?
  • 我现在明白了。可以查看spring.io/blog/2011/04/26/…,看看它是否提供了如何做你想做的事。

标签: java hibernate spring-data-jpa spring-data spring-data-rest


【解决方案1】:

您可以创建自己的 GroupBySpecification 类,将 groupBy 表达式添加到其他规范:

public class GroupBySpecification implements Specification {
  Specification original;
  Parameters parameters; // whatever data needed for calculating groupBy expression

  public GroupBySpecification(Specification original, Parameters parameters){
      this.original = original;
      this.parameters = parameters;
  }
  public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {

      Expression<?> expression = ...; // whatever logic needed to be used on parameters...
      criteriaBuilder.groupBy(expression);
      return original.toPredicate(root, query, criteriabuilder);
  }

}

那么你可以这样使用它:

String filter = "createdDate > 2019-09-04T11:52:59.449";
return repository.findAll(new GroupBySpecification(toSpecification(filter),parameters),
   pageable).getContent();  

如果您需要 count,则调用相应的存储库方法:

repository.count(new GroupBySpecification(toSpecification(filter),parameters));  

【讨论】:

  • 感谢您的回答。实际上我也做了同样的事情,但我没有为规范创建一个单独的类,而是在我的服务中创建了一个新的规范对象,然后使用规范类中的 and 方法将它组合起来 (specObject.and(anotherSpecObject)) .将更新我的问题以包括此。关于 repository.count() 方法。不确定它是否是我需要的,因为它只返回结果列表中的一些模型,我需要它作为 聚合函数 工作(如 count(*) 并且它在结果中再附加一列设置)。
  • 虽然我在使用这种方法时遇到了另一个问题。尽管我已将 group by 添加到结果 sql 查询中,但我需要指定要返回的特定列,这些列在 group by 部分中使用,否则我会得到一个 sql 异常ERROR: column "columnName" must appear in the GROUP BY clause or be used in an aggregate function。我尝试使用 CriteriaQuery 的方法 multiselect 但它还是选择了所有列。因此需要了解如何使用 Criteria Api 选择特定列,因为如果我使用 @Query("query") 它可以正常工作,但那不是我的情况。
  • @Coffemanz 我处于同样的情况,我需要一个带有 count() 的聚合自定义对象,但这种方式似乎是不可能的。我不知道这甚至可能
猜你喜欢
  • 2016-07-22
  • 2017-01-12
  • 2016-02-17
  • 2017-10-28
  • 1970-01-01
  • 2019-05-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多