Hibernate JPQL/HQL:聚合函数的错误显示错误的表/实体连接两次(仅使用 HSQLDB)的结果?

Posted

技术标签:

【中文标题】Hibernate JPQL/HQL:聚合函数的错误显示错误的表/实体连接两次(仅使用 HSQLDB)的结果?【英文标题】:Hibernate JPQL/HQL: bug with aggregate functions showing results of wrong table/entity joined twice (using HSQLDB only)? 【发布时间】:2010-12-07 12:46:15 【问题描述】:

我有以下表格:

CREATE TABLE Rosters
(
  id INTEGER NOT NULL,
  club_abbr VARCHAR(10) NOT NULL,
  ordinal_nbr SMALLINT,
  PRIMARY KEY (id)
);

CREATE TABLE Games
(
  id INTEGER NOT NULL,
  scheduled_tipoff DATETIME NOT NULL,
  PRIMARY KEY (id)
);

-- join table
CREATE TABLE Scores
(
  game_id INTEGER NOT NULL,
  is_home BOOLEAN NOT NULL,
  roster_id INTEGER NOT NULL,
  final_score SMALLINT DEFAULT NULL NULL,
  PRIMARY KEY (game_id, is_home),
  FOREIGN KEY (game_id) REFERENCES Games (id),
  FOREIGN KEY (roster_id) REFERENCES Rosters (id)
);

简单的逻辑,一场比赛有两个比分,主场和客场(在PK中通过is_home),它们与一个花名册ID相关联。分数表基本上是游戏和花名册之间的连接表。我相应地映射了类(这里没有问题):

这是我接下来要汇总的数据(14 场比赛,28 场得分,[sf] 的 14 场得分,[sa] 的 14 场得分,以及 2 场空的未玩游戏):

|sf.roster.id|ga.id|sf.finalScore|sa.finalScore|
|------------|-----|-------------|-------------|
|           1|    3|         null|         null|
|           1|    5|           71|           93|
|           1|   11|           77|           80|
|           1|   13|           65|           71|
|           1|   16|           88|           90|
|           1|   22|           58|           51|
|           1|   23|           71|           75|
|           1|   30|         null|         null|
|           1|   32|           89|           86|
|           1|   40|           62|           71|
|           1|   42|           64|           60|
|           1|   46|           73|          101|
|           1|   48|           50|           43|
|           1|   51|           88|           60|

总得分为 856,总得分为 881。打了 12 场比赛。平均得分为 71.33333333333333,平均得分为 71.4166666666666。

我正在使用 JPQL 语句:

SELECT NEW tld.jpqlsum.view.StringLine(
    SUM(sf.finalScore)
  , SUM(sa.finalScore)
  , AVG(sf.finalScore)
  , AVG(sa.finalScore)
  , MIN(sf.finalScore)
  , MIN(sa.finalScore)
  , MAX(sf.finalScore)
  , MAX(sa.finalScore)
  )
FROM Game ga
  JOIN ga.scores sf
  JOIN ga.scores sa
WHERE ga.id <> 57 AND sf.roster.id = 1 AND sa.roster.id <> 1
GROUP BY sf.roster.id

这应该会产生一个团队(名册)所有已玩游戏的累积视图。 Hibernate(HSQLDB 和 HSQLDialect)生成:

select
  sum(scores1_.final_score) as col_0_0_,
  sum(scores2_.final_score) as col_1_0_,
  avg(cast(scores1_.final_score as double)) as col_2_0_,
  avg(cast(scores2_.final_score as double)) as col_3_0_,
  min(scores1_.final_score) as col_4_0_,
  min(scores2_.final_score) as col_5_0_,
  max(scores1_.final_score) as col_6_0_,
  max(scores2_.final_score) as col_7_0_
from
  Games game0_
inner join
  Scores scores1_
      on game0_.id=scores1_.game_id
inner join
  Scores scores2_
      on game0_.id=scores2_.game_id
where
  game0_.id<>57
  and scores1_.roster_id=1
  and scores2_.roster_id<>1
group by
  scores1_.roster_id

如您所见,Hibernate 在 select 子句中正确生成了交替的 scores1 和 scores2,但显然只显示了 score1 的累积值:

|SUM(sf.finalScore)|SUM(sa.finalScore)|AVG(sf.finalScore)|AVG(sa.finalScore)|MIN(sf.finalScore)|MIN(sa.finalScore)|MAX(sf.finalScore)|MAX(sa.finalScore)|
|------------------|------------------|------------------|------------------|------------------|------------------|------------------|------------------|
|               856|               856| 71.33333333333333| 71.33333333333333|                50|                50|                89|                89|

然后我尝试了 mysql 和适当的 MySQLDialect,它们生成完全相同的代码,除了 AVG 函数强制转换为 double:

select
  sum(scores1_.final_score) as col_0_0_,
  sum(scores2_.final_score) as col_1_0_,
  avg(scores1_.final_score) as col_2_0_,
  avg(scores2_.final_score) as col_3_0_,
  min(scores1_.final_score) as col_4_0_,
  min(scores2_.final_score) as col_5_0_,
  max(scores1_.final_score) as col_6_0_,
  max(scores2_.final_score) as col_7_0_
from
  Games game0_
inner join
  Scores scores1_
      on game0_.id=scores1_.game_id
inner join
  Scores scores2_
      on game0_.id=scores2_.game_id
where
  game0_.id<>57
  and scores1_.roster_id=1
  and scores2_.roster_id<>1
group by
  scores1_.roster_id

Hibernate on MySQL 然后产生正确的输出:

|SUM(sf.finalScore)|SUM(sa.finalScore)|AVG(sf.finalScore)|AVG(sa.finalScore)|MIN(sf.finalScore)|MIN(sa.finalScore)|MAX(sf.finalScore)|MAX(sa.finalScore)|
|------------------|------------------|------------------|------------------|------------------|------------------|------------------|------------------|
|               856|               881|           71.3333|           73.4167|                50|                43|                89|               101|

对我来说看起来像一个错误,但只在 HSQLDB 中,这很奇怪。这里可能是什么问题? Hibernate 的哪个组件可能导致问题?我的意思是 MySQL 和 HSQLDB 代码仅对在 HSQLDB 上生成 cast(... as double) 的 AVG 函数有所不同,但这是否会混淆结果集,如图所示?

这是一个 SSCCE(JavaSE、Hibernate、HSQLDB、Ant): http://www.kawoolutions.com/media/jpqlsum-hib-hsqldb-broken.zip

只需在 shell 中输入“ant run”即可。

如果您还有 MySQL,则 xml/persistence.xml 包含 MySQL 的注释代码,因此您可以轻松切换 DBMS。还要查看 DB 目录,其中包含设计 PDF 和 ISO/ANSI DDL 和 INSERT 脚本。

注意,我还测试了有和没有方言的 HSQLDB 以及有和没有方言的 MySQL(在 persistence.xml 中设置)。无论有无都显示相同的结果,HSQLDB 都显示错误,MySQL 都显示正确。

谁能确认这个错误?然后我会提交一个错误报告...

卡斯滕

【问题讨论】:

【参考方案1】:

HSQLDB 中存在一个错误,它从同一个表的两个版本中为同一列生成相同的聚合结果。这已在最新的 2.0.1 jar 中修复。

【讨论】:

以上是关于Hibernate JPQL/HQL:聚合函数的错误显示错误的表/实体连接两次(仅使用 HSQLDB)的结果?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 IntelliJ 12 中启用 HQL/JPQL 自动完成

使用 JPQL/HQL 在 JPA 中订购连接获取的集合

正确的表达式不是 JPQL/HQL 查询中的有效表达式

Atitit oodbms的查询,面向对象的sql查询jpa jpql hql

这可能吗:JPA/Hibernate query with list property in result?

hibernate查询之聚合函数