Lösung für JPQL-Abfragen
Dies wird für JPQL-Abfragen innerhalb der JPA-Spezifikation unterstützt .
Schritt 1 : Deklarieren Sie eine einfache Bean-Klasse
package com.path.to;
public class SurveyAnswerStatistics {
private String answer;
private Long cnt;
public SurveyAnswerStatistics(String answer, Long cnt) {
this.answer = answer;
this.count = cnt;
}
}
Schritt 2 : Bean-Instanzen von der Repository-Methode zurückgeben
public interface SurveyRepository extends CrudRepository<Survey, Long> {
@Query("SELECT " +
" new com.path.to.SurveyAnswerStatistics(v.answer, COUNT(v)) " +
"FROM " +
" Survey v " +
"GROUP BY " +
" v.answer")
List<SurveyAnswerStatistics> findSurveyCount();
}
Wichtige Notizen
- Stellen Sie sicher, dass Sie den vollständig qualifizierten Pfad zur Bean-Klasse einschließlich des Paketnamens angeben. Wenn beispielsweise die Bean-Klasse aufgerufen wird
MyBean
und sich im Paket befindet com.path.to
, lautet der vollständig qualifizierte Pfad zur Bean com.path.to.MyBean
. Einfach die Bereitstellung MyBean
nicht funktionieren (es sei denn , die Bean - Klasse im Standardpaket ist).
- Stellen Sie sicher, dass Sie den Bean-Klassenkonstruktor mit dem
new
Schlüsselwort aufrufen . SELECT new com.path.to.MyBean(...)
wird funktionieren, während SELECT com.path.to.MyBean(...)
nicht.
- Stellen Sie sicher, dass die Attribute in genau der Reihenfolge übergeben werden, die im Bean-Konstruktor erwartet wird. Der Versuch, Attribute in einer anderen Reihenfolge zu übergeben, führt zu einer Ausnahme.
- Stellen Sie sicher, dass die Abfrage eine gültige JPA-Abfrage ist, dh keine native Abfrage.
@Query("SELECT ...")
, oder @Query(value = "SELECT ...")
, oder @Query(value = "SELECT ...", nativeQuery = false)
wird funktionieren, während @Query(value = "SELECT ...", nativeQuery = true)
wird nicht funktionieren. Dies liegt daran, dass native Abfragen ohne Änderungen an den JPA-Anbieter übergeben und für das zugrunde liegende RDBMS als solches ausgeführt werden. Da new
und com.path.to.MyBean
sind keine gültigen SQL-Schlüsselwörter, löst das RDBMS dann eine Ausnahme aus.
Lösung für native Abfragen
Wie oben erwähnt, ist die new ...
Syntax ein JPA-unterstützter Mechanismus und funktioniert mit allen JPA-Anbietern. Wenn es sich bei der Abfrage selbst jedoch nicht um eine JPA-Abfrage handelt, dh um eine native Abfrage, new ...
funktioniert die Syntax nicht, da die Abfrage direkt an das zugrunde liegende RDBMS weitergeleitet wird, das das new
Schlüsselwort nicht versteht, da es nicht Teil von ist der SQL-Standard.
In solchen Situationen müssen Bean-Klassen durch Spring Data Projection- Schnittstellen ersetzt werden.
Schritt 1 : Deklarieren Sie eine Projektionsschnittstelle
package com.path.to;
public interface SurveyAnswerStatistics {
String getAnswer();
int getCnt();
}
Schritt 2 : Geben Sie die projizierten Eigenschaften aus der Abfrage zurück
public interface SurveyRepository extends CrudRepository<Survey, Long> {
@Query(nativeQuery = true, value =
"SELECT " +
" v.answer AS answer, COUNT(v) AS cnt " +
"FROM " +
" Survey v " +
"GROUP BY " +
" v.answer")
List<SurveyAnswerStatistics> findSurveyCount();
}
Verwenden Sie das AS
Schlüsselwort SQL , um Ergebnisfelder Projektionseigenschaften für eine eindeutige Zuordnung zuzuordnen.
Caused by: java.lang.IllegalArgumentException: org.hibernate.hql.internal.ast.QuerySyntaxException: Unable to locate class [SurveyAnswerReport] [select new SurveyAnswerReport(v.answer,count(v.id)) from com.furniturepool.domain.Survey v group by v.answer] at org.hibernate.jpa.spi.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1750) at org.hibernate.jpa.spi.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1677) at org.hibernate.jpa.spi.AbstractEnti..........
SurveyAnswerReport
in Ihrer Ausgabe. Ich nehme an, Sie haben durchSurveyAnswerStatistics
Ihre eigene Klasse ersetztSurveyAnswerReport
. Sie müssen den vollständig qualifizierten Klassennamen angeben.com.domain.dto.SurveyAnswerReport
.JpaRepository
? Ist eine Konfiguration, die ich verpasst habe?Diese SQL-Abfrage würde List <Object []> zurückgeben.
Sie können es so machen:
quelle
Ich weiß, dass dies eine alte Frage ist und bereits beantwortet wurde, aber hier ist ein anderer Ansatz:
quelle
Über Schnittstellen können Sie einfacheren Code erhalten. Konstruktoren müssen nicht erstellt und manuell aufgerufen werden
Schritt 1 : Deklarieren Sie intefrace mit den erforderlichen Feldern:
Schritt 2 : Wählen Sie Spalten mit demselben Namen wie Getter in der Schnittstelle aus und geben Sie intefrace von der Repository-Methode zurück:
quelle
Definieren Sie eine benutzerdefinierte Pojo-Klasse, sagen Sie sureveyQueryAnalytics, und speichern Sie den zurückgegebenen Abfragewert in Ihrer benutzerdefinierten Pojo-Klasse
quelle
Ich mag keine Java-Typnamen in Abfragezeichenfolgen und behandle sie mit einem bestimmten Konstruktor. Spring JPA ruft implizit den Konstruktor mit dem Abfrageergebnis im HashMap-Parameter auf:
Code benötigt Lombok zum Auflösen von @Getter
quelle
Ich habe gerade dieses Problem gelöst:
@Query(value = "SELECT ...", nativeQuery = true
)). Ich empfehle daher, ein benutzerdefiniertes DTO über die Schnittstelle zu definieren.quelle
Ich habe ein benutzerdefiniertes DTO (Schnittstelle) verwendet, um eine native Abfrage zuzuordnen - dem flexibelsten Ansatz und Refactoring-sicher.
Das Problem, das ich damit hatte - dass überraschenderweise die Reihenfolge der Felder in der Schnittstelle und der Spalten in der Abfrage von Bedeutung ist. Ich habe es zum Laufen gebracht, indem ich Interface-Getter alphabetisch sortiert und dann die Spalten in der Abfrage auf die gleiche Weise sortiert habe.
quelle
Der obige Code hat bei mir funktioniert.
quelle