2010-05-07 8 views
5

Ich habe eine JPA 2 Entität mit dem Namen Chirurgie. Es hat ein Mitglied mit dem Namen transfusionUnits das ist ein Integer.Funktioniert die JPQL avg-Aggregatfunktion mit Ganzzahlen?

Es gibt zwei Einträge in der Datenbank. Die Ausführung dieses JPQL Anweisung:

Select s.transfusionUnits from Surgery s 

erzeugt das erwartete Ergebnis:

2 
3 

Die folgende Anweisung erzeugt die erwartete Antwort von :

Select sum(s.transfusionUnits) from Surgery s 

Ich erwarte, dass die Antwort der folgenden Anweisung zu sein 2,5, aber es gibt 2.0 zurück stattdessen.

Select avg(s.transfusionUnits) from Surgery s 

Wenn ich die Anweisung auf einem anderen (Float) Mitglied ausführen, ist das Ergebnis korrekt. Irgendwelche Ideen, warum das passiert? Muss ich in JPQL eine Art von Cast machen? Ist das überhaupt möglich? Sicherlich vermisse ich hier etwas Triviales.

Antwort

7

Zuerst lassen Sie mich zusammenfassen, was die JPA 2.0-Spezifikation sagt über AVG

Die AVG-Funktion nimmt einen Zustand Feldbahn (Abschnitt 4.8.5 Aggregatfunktionen in der SELECT-Klausel für den vollständigen Inhalt sehen) Ausdruck als Argument und berechnet den Durchschnittswert des sate-Feldes über die Gruppe. Das Statusfeld muss numerisch sein und das Ergebnis wird als Double zurückgegeben.

So, nach einem ersten Lese, erwarte ich das folgende Snippet passieren:

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

Aber es ging nicht, ich war 2.0 als Ergebnis zu erzielen (ein Double, aber nicht das erwartete Ergebnis).

Also schaute ich auf die Datenbank-Ebene und erkannte, dass die SQL-Abfrage tatsächlich 2.5 nicht zurückgegeben wurde. Und tatsächlich, das ist, was die Dokumentation meiner Datenbank über AVG sagt:

Der durchschnittliche (Mittelwert) Wert. Wenn keine Zeilen ausgewählt sind, ist das Ergebnis NULL. Aggregate sind nur in ausgewählten Anweisungen zulässig. Der zurückgegebene Wert hat denselben Datentyp wie der Parameter.

Ich habe zu viel erwartet. JPA wird das Ergebnis als Double zurückgeben, aber es wird keine Magie wirken. Wenn die Abfrage kein Ergebnis mit der erforderlichen Genauigkeit zurückgibt, erhalten Sie sie nicht auf Java-Ebene.

Also ohne die Art der transfusionUnits zu ändern, hatte ich diese nativen Abfrage auszuführen Dinge zum Laufen zu bringen:

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: Es scheint, dass einige Datenbank (zB MySQL) ein kehre Wert mit einem Dezimalteil, wenn AVG für eine INT-Spalte verwendet wird (was sinnvoll ist, unabhängig vom dokumentierten Verhalten der Datenbank, die ich hier verwendet habe). Für andere Datenbanken könnte die obige Umwandlung im "Dialekt" gehandhabt werden (z. B. siehe HHH-5173 für Hibernate), um Portabilitätsprobleme zwischen Datenbanken bei der Verwendung von JPQL zu vermeiden.

1

Wenn Sie das gemittelte Feld zu double machen, ist das der Trick. Nicht sicher über die Auswirkungen auf die Leistung, aber sollte nichts Böses sein:

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

Es funktioniert nicht für mich, ich bekomme Fehler, dass es sein sollte), anstatt + Zeichen. –

+0

arbeitete für mich mit openjpa, scheint es Provider abhängig – MarianP