Во-первых, позвольте мне обобщить, что спецификация JPA 2.0 говорит об AVG (см. Раздел 4.8.5 Агрегатные функции в предложении SELECT для полного содержимого)
Функция AVG принимает в качестве аргумента выражение пути поля состояния и вычисляет среднее значение поля состояния по группе. Поле состояния должно быть числовым, а результат возвращается в виде 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. Агрегаты разрешены только в избранных утверждениях. Возвращаемое значение того же типа, что и параметр .
Я ожидал слишком многого. 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) возвращают значение с десятичной частью при использовании AVG для столбца INT (что имеет смысл, независимо от задокументированного поведения базы данных, которую я использовал Вот). Для других баз данных приведенное выше приведение может быть обработано
в «диалекте» (например, см. HHH-5173 для Hibernate), чтобы избежать проблем переносимости между базами данных при использовании JPQL.