2010-05-07 4 views
5

У меня есть JPA 2 Сущность по имени Хирургия. Он имеет член по имени transfusionUnits, который является Integer.Работает ли функция агрегации JPQL с интеграторами?

Это двух записей в базе данных. Выполнение этого JPQL заявление:

Select s.transfusionUnits from Surgery s 

производит ожидаемый результат:

2 
3 

Следующий оператор производит ожидаемый ответ :

Select sum(s.transfusionUnits) from Surgery s 

я ожидаю ответа из следующих заявление должно быть 2,5, но оно возвращает 2.0.

Select avg(s.transfusionUnits) from Surgery s 

Если я выполняю инструкцию на другом (Float), результат правильный. Любые идеи о том, почему это происходит? Нужно ли мне делать какие-то роли в JPQL? Возможно ли это? Наверняка, мне здесь не хватает чего-то тривиального.

ответ

7

Во-первых, позвольте мне подвести итог, что спецификации JPA 2.0 говорит о AVG (смотрите раздел 4.8.5 агрегатные функции в SELECT, п для полного содержания)

Функция AVG принимает путь состояния поля выражение в качестве аргумента и вычисляет среднее значение поля sate над группой. Поле состояния должно быть числовым, а результат - returnd как 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 вернет результат как Двойной, но он не сделает никакой магии. Если запрос не возвращает результат с требуемой точностью, вы не получите его на уровне 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()); 

Update: Оказывается, что некоторые базы данных (например, MySQL) действительно возвращают значение с десятичной частью при использовании AVG в столбце INT (что имеет смысл, независимо от документированного поведения базы данных, которую я использовал здесь). Для других баз данных приведенный выше листинг может обрабатываться в диалекте (например, см. HHH-5173 для Hibernate), чтобы избежать проблем с переносимостью между базой данных при использовании JPQL.

1

Кастинг усредненного поля, чтобы удвоить трюк. Не уверен в значениях производительности, но не должен быть ничем злым:

Select avg(s.transfusionUnits + 0.0) from Surgery s 
+0

Это не работает для меня, я получаю ошибку, которая должна быть), а не знак +. –

+0

работал для меня с openjpa, кажется, что он зависит от поставщика – MarianP