首先,让我总结一下 JPA 2.0 规范对 AVG 的规定(完整内容请参见SELECT 子句中的 4.8.5 聚合函数部分)
AVG 函数将状态字段路径表达式作为参数,并计算组上状态字段的平均值。state 字段必须是数字,结果以 Double 形式返回。
所以,在第一次阅读之后,我期待下面的代码片段通过:
Query q = em.createQuery("select avg(s.transfusionUnits) from Surgery s");
Double actual = (Double) q.getSingleResult();
assertEquals(2.5d, actual.doubleValue());
但事实并非如此,我得到2.0
了结果(双倍,但不是预期的结果)。
所以我查看了数据库级别,意识到 SQL 查询实际上没有返回2.5
。事实上,这就是我的数据库文档中关于 AVG 的内容:
平均值(平均值)。如果未选择任何行,则结果为 NULL。只允许在 select 语句中使用聚合。返回值与参数的数据类型相同。
我期待太多了。JPA 会以 Double 形式返回结果,但它不会产生任何魔力。如果查询没有返回具有所需精度的结果,您将无法在 Java 级别获得它。
因此,在不更改 的类型的情况下transfusionUnits
,我必须运行此本机查询才能使事情正常运行:
Query q = em.createNativeQuery("SELECT AVG(CAST(s.transfusionUnits as double)) from Surgery s");
Double actual = (Double) q.getSingleResult();
assertEquals(2.5d, actual.doubleValue());
更新:似乎某些数据库(例如 MySQL)在 INT 列上使用 AVG 时确实返回了一个带小数部分的值(这是有道理的,无论我在这里使用的数据库的记录行为如何)。对于其他数据库,可以在“方言”中处理上述转换(例如,请参阅Hibernate的 HHH-5173)以避免使用 JPQL 时数据库之间的可移植性问题。