Java 如何使用JPA标准API/Hibernate对Case语句进行分组

Java 如何使用JPA标准API/Hibernate对Case语句进行分组,java,hibernate,jpa,groovy,criteria-api,Java,Hibernate,Jpa,Groovy,Criteria Api,我正在尝试执行如下查询,通过case语句进行选择,并通过同一case语句进行分组 Select USER, (CASE WHEN value between 0 AND 2 then '0-2' WHEN value between 3 AND 4 then '3-4' ELSE '5+' END) as CASE_STATEMENT , SUM(value) ..... Group by user, CASE_STATEMENT 使用JPA2.0标准API,

我正在尝试执行如下查询,通过case语句进行选择,并通过同一case语句进行分组

Select USER, 
  (CASE
    WHEN value between 0 AND 2 then '0-2'
    WHEN value between 3 AND 4 then '3-4'
    ELSE '5+'
  END) as CASE_STATEMENT ,
SUM(value)
.....
Group by user, CASE_STATEMENT
使用JPA2.0标准API,使用Hibernate

我的测试用例看起来像

    CriteriaBuilder cb = em.getCriteriaBuilder()
    CriteriaQuery cq = cb.createQuery(Tuple)
    def root = cq.from(TestEntity)
    def userGet = root.get('user')
    def valueGet = root.get('value')
    def caseExpr =
            cb.selectCase()
                .when(cb.between(valueGet, 0, 2), '0-2')
                .when(cb.between(valueGet, 3, 4), '3-4')
                .otherwise('5+')
    def sumExpr = cb.sum(valueGet)

    cq.multiselect([userGet, caseExpr, sumExpr])
    cq.groupBy([userGet, caseExpr])
    log(typedQuery.unwrap(Query).queryString)
    List<Tuple> tuples = typedQuery.getResultList()
调用typedQuery.getResultList时,我得到以下错误语句

javax.persistence.PersistenceException: org.hibernate.exception.GenericJDBCException: could not extract ResultSet

Caused by: org.h2.jdbc.JdbcSQLException: Column "TESTENTITY0_.VALUE" must be in the GROUP BY list; SQL statement:

select testentity0_.user as col_0_0_, case when testentity0_.value between 0 and 2 then cast(? as varchar(255)) when testentity0_.value between 3 and 4 then cast(? as varchar(255)) else cast(? as varchar(255)) end as col_1_0_, sum(testentity0_.value) as col_2_0_ from test testentity0_ group by testentity0_.user , case when testentity0_.value between 0 and 2 then cast(? as varchar(255)) when testentity0_.value between 3 and 4 then cast(? as varchar(255)) else cast(? as varchar(255)) end [90016-194]
我试图通过表达式进行分组的方式是否有问题?我还尝试过按别名和数字文字1、2进行分组

是否有其他方法可以对SQL进行结构化以获得相同的结果


谢谢。

如异常消息所示,问题与DBMS级别的Group By语句有关。见:

要解决此错误,您必须

将底层DBMS的GROUPBY模式设置为MySQL允许的限制性较小的级别,但您可以尝试在jdbc连接字符串中设置Mode=MySQL

或者更好

如前所述,将select语句中的所有列添加到GROUP BY语句或聚合函数中

您应该能够构建满足GROUPBY限制的嵌套查询

为了救援,可能有一些特定于DBMS的聚合 至少在以下方面起作用。为了欺骗JPA和Hibernate理解这些,有 实现此目的的几种方法,如中所述 和

编辑

除上述声明外,经讨论后得出的结论如下:

该异常是由org.h2.expression.ExpressionColumn类中的h2驱动程序在验证查询语法时引发的 解决方案需要在case语句或子查询的查询中设置和引用别名,这在Criteria API中目前是不可能的,请参阅 解决方法是创建一个NativeQuery,如下所示: 列表元组=em.createNativeQuery 选择GenerateDias0.USER+ 案例+ 当generatedAlias0.value介于0和2之间时,则将:param0强制转换为VARCHAR+ 当generatedAlias0.value介于3和4之间时,则将:param1强制转换为VARCHAR+ ELSE Cast:param2作为VARCHAR+ (丙)+ SumgeneratedAlias0.value作为sumvalue+ 根据生成的测试ALIAS0+ 按GenerateDias0.USER分组,c .setParameterparam0,0-2 .setParameterparam1,3-4 .setParameterparam2,5+ .getResultList;
现在看到同样的问题,却找不到合适的解决方案。在问题中添加了悬赏,希望有人能对此进行调查,并解释为什么案例中的列需要在组中,从而完全消除了计数的目的。在H2中直接运行完全相同的查询似乎成功了。。。所以这可能是JPA的错误?可以肯定的是,您在SQL中遇到了问题,因为您有错误:org.h2.jdbc.JdbcSQLException:Column TESTENTITY0_389;。值必须在GROUP BY LIST中。我相信这是因为您参数化了case语句。你能试试子查询吗?选择user,case_语句,sumvalue作为select user的sum_值,case当值在0和2之间时,然后是'0-2'当值在3和4之间时,然后是'3-4'否则'5+'结束为case_语句,test中的值作为用户的t1组,case_语句作为@daggett建议的替代方法,尝试使用cb.literal'0-2',cb.literal'3-4',cb.literal'5+'代替谢谢你的建议。您能解释一下为什么查询在H2中使用相同的数据库连接设置时可以正常工作吗?我可以通过SELECT语句中的语句进行分组,而不必声明组中给定语句的所有属性。@Frame91实际上,这种行为似乎很奇怪。在我看来,它在任何情况下都不应该起作用,除了兼容MySQL系统。是否可能在应用程序启动期间覆盖连接设置(如SQL模式)?你在日志级别调试时检查日志了吗?@Frame91是的,我知道了。在给定的示例中,我发现异常是由org.h2.expression.ExpressionColumn类中的h2驱动程序引发的,该类正在检查上面提到的group by限制。我还检查了h2控制台,它显示h2 db可以容忍这种违规行为。总之,为了实现兼容性,您必须为case语句设置别名,或创建一个子查询,如您提到的问题中所述。criteria api不支持FROM语句中的子查询。。。我试图给case语句加上别名,但结果也是一样的exception@Frame91如前所述,子查询可以通过使用getSelection实现。我已经对此进行了测试,但不幸的是,导致了一组异常。为subselect语句分配和使用别名也不起作用。因此,现在,我建议不要使用CriteriaAPI,而是构建一个像Vlad Mihalcea所描述的NativeQuery。这仅仅是一个关于CriteriaAPI的变通方法,但至少是一个可行的解决方案。
javax.persistence.PersistenceException: org.hibernate.exception.GenericJDBCException: could not extract ResultSet

Caused by: org.h2.jdbc.JdbcSQLException: Column "TESTENTITY0_.VALUE" must be in the GROUP BY list; SQL statement:

select testentity0_.user as col_0_0_, case when testentity0_.value between 0 and 2 then cast(? as varchar(255)) when testentity0_.value between 3 and 4 then cast(? as varchar(255)) else cast(? as varchar(255)) end as col_1_0_, sum(testentity0_.value) as col_2_0_ from test testentity0_ group by testentity0_.user , case when testentity0_.value between 0 and 2 then cast(? as varchar(255)) when testentity0_.value between 3 and 4 then cast(? as varchar(255)) else cast(? as varchar(255)) end [90016-194]