У меня есть объект JPA 2 под названием Хирургия. Это имеет названный transfusionUnits участника, который является Целым числом.
В базе данных существует две записи. Выполнение этого оператора JPQL:
Select s.transfusionUnits from Surgery s
приводит к ожидаемому результату:
2
3
Следующее утверждение производит ожидаемый ответ 5:
Select sum(s.transfusionUnits) from Surgery s
Я ожидаю, что ответ следующего утверждения будет 2.5, но это возвращается 2.0 вместо этого.
Select avg(s.transfusionUnits) from Surgery s
Если я выполняю оператор на другом участнике (Плавающем), результат корректен. Какие-либо идеи о том, почему это происходит? Я должен сделать своего рода бросок в JPQL? Это даже возможно? Конечно, я пропускаю что-то тривиальное здесь.
Во-первых, позвольте мне резюмировать то, что спецификация 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
как результат (Double, но не ожидаемый результат) .
Итак, я посмотрел на уровень базы данных и понял, что запрос 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) действительно возвращает значение с десятичной частью при использовании AVG в столбце INT (что имеет смысл, независимо от задокументированного поведения базы данных, которую я использовал здесь). Для других баз данных приведенное выше приведение может быть обработано на "диалекте" (например,см. HHH-5173 для Hibernate), чтобы избежать проблем с переносимостью между базами данных при использовании JPQL.