【问题标题】:JPA Criteria Builder order by a sum columnJPA Criteria Builder 按总和列排序
【发布时间】:2017-09-26 08:59:23
【问题描述】:

我有以下 SQL 查询。

select colA, sum(colB) as colB from tableA group by colA order by colB desc;

我已经使用 jpa 标准生成器来动态构建查询。

List<String> selectCols = new ArrayList<>();
selectCols.add("colA");
List<String> sumColumns = new ArrayList<>();
sumColumns.add("colB");
List<String> groupByColumns = new ArrayList<>();
groupByColumns.add("colA");
List<String> orderingColumns = new ArrayList<>();
orderingColumns.add("colB");

CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery<Tuple> cQuery = builder.createQuery(Tuple.class);
Root<T> root = cQuery.from(getDomainClass());

List<Selection<?>> selections = new ArrayList<>();
for (String column : selectCols) {
    selections.add(root.get(column).alias(column));
}

if (sumColumns != null) {
    for (String sumColumn : sumColumns) {
        selections.add(builder.sum(root.get(sumColumn)).alias(sumColumn));
    }
}

cQuery.multiselect(selections);

List<Expression<?>> groupByExpressions = new LinkedList<>();

for (String column : groupByColumns) {
    groupByExpressions.add(root.get(column));
}

cQuery.groupBy(groupByExpressions);

List<Order> orders = new ArrayList<>();

for (String orderCol : orderingColumns) {

    orders.add(builder.desc(root.get(orderCol)));

}

cQuery.orderBy(orders);

entityManager.createQuery(cQuery).getResultList();

我调试并检查了正在执行的确切 sql 语句。

select colA as col_1_0, sum(colB) as col_2_0 from tableA group by colA order by colB desc;

正在执行的生成的 sql 语句没有 colB 的别名 colB。我正在使用 MySql。令人惊讶的是,当 sql 模式设置为 NO_ENGINE_SUBSTITUTION 时,该语句被执行并且我得到了正确的结果。但是当sql模式为ONLY_FULL_GROUP_BY时,​​数据库抛出错误说

ORDER BY 子句的表达式#1 不在 GROUP BY 子句中,并且 包含在功能上没有功能的非聚合列“colB” 依赖于 GROUP BY 子句中的列;这与 sql_mode=only_full_group_by

我了解在这种模式下,sql 验证查询并且我的查询失败。如何使用 jpa 标准生成器创建别名,以便执行的实际语句包含 sum 列的正确别名?

注意:我使用 spring-data-jpa,这是存储库的自定义实现。我可以使用@Query 注释手动编写查询,但查询的性质在 group by、order by、where 子句和选择列上是动态的。因此需要动态标准构建器实现。

【问题讨论】:

  • 您想通过选择列中的 sum(colB) 对结果进行排序吗?如果是这样,您是否尝试使用 builder.desc(builder.sum(root.get(orderCol))) 其中 orderCol 指向 colB ?
  • 我想使用别名来做到这一点。因为我在运行时不知道按列排序是总和列还是普通列。这就是为什么我创建了 sum 列的别名作为列名本身。
  • 好的,只是大声思考。您是否尝试将别名“colB”赋予与任何列名都不匹配的其他名称?
  • &lt;snide-remark&gt;生成一个简单的SQL语句需要这么多代码??显然,有这么多代码有助于弄错。&lt;/snide-remark&gt;
  • 这么多代码使查询动态化。

标签: java mysql jpa spring-data-jpa


【解决方案1】:

尝试对聚合进行排序。根据我的经验,列别名通常不能在查询本身中被引用。

orders.add(builder.desc(builder.sum(sumColumn)));

【讨论】:

  • 这就是我最终求助的方法。对于我的用例,我事先不知道是否应该对 orderingColumn 求和。如果我使用别名(在我的情况下,我的别名与列名相同),我可以在排序列列表中传递列名,并且如果该列在选择列中求和并具有相同的别名,我不需要告诉这个排序列应该相加。
【解决方案2】:

我认为你在orderingColumns 中有错误,你应该添加colA 而不是colB

或者改为

for (String orderCol : orderingColumns) {
    orders.add(builder.desc(root.get(orderCol)));
}

你可以使用

orders.add(builder.desc(root.get("colA")));

【讨论】:

  • 对不起@Abihabi87。我错过了声明 orderingColumns 列表。我已经编辑了问题,您现在可以查看更新的代码。
猜你喜欢
  • 2015-05-11
  • 2021-06-29
  • 2017-05-02
  • 2013-08-25
  • 1970-01-01
  • 2017-07-20
  • 2015-02-26
  • 2019-02-09
相关资源
最近更新 更多