2010-05-07 22 views
5

Tengo una entidad JPA 2 llamada Cirugía. Tiene un miembro llamado transfusionUnits que es un entero.¿Funciona la función agregada promedio JPQL con enteros?

Hay dos entradas en la base de datos. Ejecutar esta sentencia JPQL:

Select s.transfusionUnits from Surgery s 

produce el resultado esperado:

2 
3 

La siguiente sentencia produce la respuesta esperada de :

Select sum(s.transfusionUnits) from Surgery s 

espero la respuesta de la siguiente declaración para ser 2.5, pero devuelve 2.0 en su lugar.

Select avg(s.transfusionUnits) from Surgery s 

Si ejecuto la declaración en un miembro diferente (Float), el resultado es correcto. ¿Alguna idea de por qué esto esta pasando? ¿Debo hacer algún tipo de elenco en JPQL? ¿Esto es posible? Seguramente me estoy perdiendo algo trivial aquí.

Respuesta

7

En primer lugar, permítanme resumir lo que la especificación JPA 2.0 dice acerca de AVG (véase la sección 4.8.5 funciones de agregado en la cláusula SELECT para el contenido completo)

La función AVG toma un camino campo de estado expresión como argumento y calcula el valor promedio del campo de estado sobre el grupo. El campo de estado debe ser numérico, y el resultado es retornar como un doble.

Así, después de una primera lectura, que estaba esperando el siguiente fragmento de pasar:

Query q = em.createQuery("select avg(s.transfusionUnits) from Surgery s"); 
Double actual = (Double) q.getSingleResult(); 
assertEquals(2.5d, actual.doubleValue()); 

pero no fue así, me estaba 2.0 como resultado (un doble, pero no el resultado esperado)

Así que miré en el nivel de la base de datos y me di cuenta de que la consulta SQL en realidad no estaba devolviendo 2.5. Y de hecho, esto es lo que dice la documentación de mi base de datos sobre AVG:

El valor promedio (promedio). Si no se seleccionan filas, el resultado es NULO. Los agregados solo se permiten en declaraciones seleccionadas. El valor devuelto es del mismo tipo de datos que el parámetro.

Estaba esperando demasiado. JPA devolverá el resultado como un doble, pero no hará magia. Si la consulta no devuelve un resultado con la precisión requerida, no lo obtendrá en el nivel de Java.

Así que sin cambiar el tipo de transfusionUnits, tenía que ejecutar esta consulta nativa para hacer las cosas de trabajo:

Query q = em.createNativeQuery("SELECT AVG(CAST(s.transfusionUnits as double)) from Surgery s"); 
Double actual = (Double) q.getSingleResult(); 
assertEquals(2.5d, actual.doubleValue()); 

Actualización: Parece ser que alguna base de datos (por ejemplo, MySQL) personas vuelven en una valor con una parte decimal cuando se usa AVG en una columna INT (lo que tiene sentido, independientemente del comportamiento documentado de la base de datos que utilicé aquí). Para otras bases de datos, el elenco anterior podría manejarse en el "dialecto" (por ejemplo, ver HHH-5173 para Hibernate) para evitar problemas de portabilidad entre la base de datos al usar JPQL.

1

Casting the promediado field to double hace el truco. No estoy seguro sobre las implicaciones de rendimiento, pero no debería ser nada malo:

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

No funciona para mí, me sale un error que debería haber) en lugar de + signo. –

+0

funcionó para mí con openjpa, parece que depende del proveedor – MarianP

Cuestiones relacionadas