JPA : So konvertieren Sie eine Ergebnismenge einer nativen Abfrage in eine POJO-Klassensammlung
Lesezeit: 11 Minuten
Gunjan Schah
Ich verwende JPA in meinem Projekt.
Ich bin zu einer Abfrage gekommen, in der ich eine Join-Operation für fünf Tabellen durchführen muss. Also habe ich eine native Abfrage erstellt, die fünf Felder zurückgibt.
Jetzt möchte ich das Ergebnisobjekt in die Java-POJO-Klasse konvertieren, die dieselben fünf Strings enthält.
Gibt es in JPA eine Möglichkeit, dieses Ergebnis direkt in die POJO-Objektliste umzuwandeln?
Ich bin zu folgender Lösung gekommen..
@NamedNativeQueries({
@NamedNativeQuery(
name = "nativeSQL",
query = "SELECT * FROM Actors",
resultClass = db.Actor.class),
@NamedNativeQuery(
name = "nativeSQL2",
query = "SELECT COUNT(*) FROM Actors",
resultClass = XXXXX) // <--------------- problem
})
Müssen wir jetzt hier in resultClass eine Klasse angeben, die eine tatsächliche JPA-Entität ist? ODER Wir können es in eine beliebige JAVA POJO-Klasse konvertieren, die dieselben Spaltennamen enthält.
Überprüfen Sie diese Antwort. Es hat eine vollständige Antwort: stackoverflow.com/a/50365522/3073945
– Md. Sajedul Karim
16. Mai 2018 um 8:36 Uhr
er benutzt jpa, nicht spring
– ihm
18. Oktober 2018 um 0:21 Uhr
Edwin Dalorzo
Ich habe ein paar Lösungen dafür gefunden.
Verwenden von zugeordneten Entitäten (JPA 2.0)
Mit JPA 2.0 ist es nicht möglich, eine native Abfrage einem POJO zuzuordnen, dies kann nur mit einer Entität erfolgen.
Aber in diesem Fall Jedimuss eine zugeordnete Entitätsklasse sein.
Eine Alternative, um die ungeprüfte Warnung hier zu vermeiden, wäre die Verwendung einer benannten nativen Abfrage. Wenn wir also die native Abfrage in einer Entität deklarieren
Dies ist sicherer, aber wir sind immer noch darauf beschränkt, eine zugeordnete Entität zu verwenden.
Manuelle Zuordnung
Eine Lösung, mit der ich ein wenig experimentiert habe (vor der Ankunft von JPA 2.1), war die Zuordnung zu einem POJO-Konstruktor mit ein wenig Reflexion.
public static <T> T map(Class<T> type, Object[] tuple){
List<Class<?>> tupleTypes = new ArrayList<>();
for(Object field : tuple){
tupleTypes.add(field.getClass());
}
try {
Constructor<T> ctor = type.getConstructor(tupleTypes.toArray(new Class<?>[tuple.length]));
return ctor.newInstance(tuple);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
Diese Methode nimmt grundsätzlich ein Tupel-Array (wie es von nativen Abfragen zurückgegeben wird) und ordnet es einer bereitgestellten POJO-Klasse zu, indem sie nach einem Konstruktor sucht, der dieselbe Anzahl von Feldern und denselben Typ hat.
Dann können wir bequeme Methoden verwenden wie:
public static <T> List<T> map(Class<T> type, List<Object[]> records){
List<T> result = new LinkedList<>();
for(Object[] record : records){
result.add(map(type, record));
}
return result;
}
public static <T> List<T> getResultList(Query query, Class<T> type){
@SuppressWarnings("unchecked")
List<Object[]> records = query.getResultList();
return map(type, records);
}
Und wir können diese Technik einfach wie folgt anwenden:
In diesem Fall natürlich Jedi muss keine zugeordnete Entität sein. Es kann ein normales POJO sein.
Verwenden von XML-Mapping
Ich bin einer von denen, die all dies hinzufügen @SqlResultSetMapping ziemlich invasiv in meinen Entitäten, und ich mag besonders die Definition von benannten Abfragen innerhalb von Entitäten nicht, also mache ich das alles alternativ in der META-INF/orm.xml Datei:
Und das sind alle Lösungen, die ich kenne. Die letzten beiden sind der ideale Weg, wenn wir JPA 2.1 verwenden können.
Nebenbemerkung: Ich habe gerade den JPA 2.0-Ansatz mit JPA2.1-Abhängigkeit verwendet, und es ist fehlgeschlagen. Also wahrscheinlich nicht abwärtskompatibel…
– Membersound
19. September 2014 um 9:45 Uhr
Was meinst du mit “irgendwo in einer Entität”? Mein Pojo ist keine JPA-Entität. Kann ich das @SqlResultSetMapping nicht in meinem POJO deklarieren? Ich interessiere mich für die JPA 2.1-Lösungen. Bitte etwas genauer.
– Alboz
20. April 2015 um 15:36 Uhr
@Alboz Der @SqlResultSetMapping muss in einer Entität platziert werden, da JPA die Metadaten daraus lesen wird. Sie können nicht erwarten, dass JPA Ihre POJOs überprüft. Die Entität, in der Sie die Zuordnung platzieren, ist irrelevant, vielleicht diejenige, die mehr mit Ihren POJO-Ergebnissen zusammenhängt. Alternativ könnte die Abbildung in XML ausgedrückt werden, um die Kopplung mit einer völlig unabhängigen Entität zu vermeiden.
– Edwin Dalorzo
20. April 2015 um 15:40 Uhr
Kann das Konstruktorergebnis eine Klasse verwenden, die eine verschachtelte Klasse hat?
– chrismarx
22. Oktober 2015 um 19:47 Uhr
Bei Verwendung von JPA 2.1 mit @SqlResultSetMapping Es kann erwähnenswert sein, dass die Jedi Klasse benötigt einen All-Arg-Konstruktor und die @ColumnResult Anmerkung kann die benötigen type Attribut zu Conversions hinzugefügt, die möglicherweise nicht implizit sind (ich musste hinzufügen type = ZonedDateTime.class für einige Spalten).
– Glenn
20. April 2016 um 3:49 Uhr
Denis Tulsky
JPA bietet eine SqlResultSetMapping Dadurch können Sie alle Rückgaben Ihrer nativen Abfrage einer Entität zuordnen oder eine benutzerdefinierte Klasse.
BEARBEITEN JPA 1.0 erlaubt keine Zuordnung zu Nicht-Entitätsklassen. Nur in JPA 2.1 a KonstruktorErgebnis wurde hinzugefügt, um Rückgabewerte einer Java-Klasse zuzuordnen.
Auch für das Problem von OP mit dem Abrufen der Anzahl sollte es ausreichen, eine Ergebnismengenzuordnung mit einer einzigen zu definieren ColumnResult
Danke für die Antwort. Hier ordnen wir unser Ergebnis der Entität mit der Java-Entitätsklasse mit den Annotationen „@EntityResult“ und „@FieldResult“ zu. Das ist gut. Aber hier brauche ich mehr Klarheit. Ist es erforderlich, dass die Klasse, die wir mit dem Ergebnis abbilden, eine JPA-Entitätsklasse sein muss? ODER können wir eine einfache POJO-Klasse verwenden, die kein Entitätskauf ist und alle erforderlichen Variablen als Spalten in der Ergebnismenge enthält.
– Gunjan Schah
22. Oktober 2012 um 16:52 Uhr
@GunjanShah: Der beste Weg, dies herauszufinden, ist, es auszuprobieren 🙂 Außerdem ist eine Entität genau das gleiche Pojo, nur mit einigen Anmerkungen. Solange du nicht versuchst, darauf zu bestehen, wird es ein Pojo bleiben.
– Denis Tulsky
22. Oktober 2012 um 17:04 Uhr
Als ich das versuchte, bekam ich eine Fehlermeldung, dass die Klasse keine bekannte Entität war. Am Ende habe ich diesen Ansatz verwendet stackoverflow.com/questions/5024533/… anstatt zu versuchen, eine native Abfrage zu verwenden.
– FGreg
30. Januar 2013 um 21:43 Uhr
@EdwinDalorzo: das ist richtig für jpa 1.0. in jpa 2.1 haben sie hinzugefügt ConstructorResult als einer der Parameter zu SqlResultSetMapping Dies ermöglicht die Verwendung eines Pojos mit allen im Konstruktor festgelegten Feldern. Ich werde die Antwort aktualisieren.
– Denis Tulsky
31. Januar 2014 um 16:54 Uhr
Ich sehe eine weitere bittere Wahrheit: ConstructorResult kann einem POJO zugeordnet werden zum Primärschlüssel – immer noch müssen Sie @Id in Entity haben … lächerlich, oder?
– Arnab Dutta
22. März 2018 um 20:35 Uhr
GiulioDS
Ja, mit JPA 2.1 ist es einfach. Sie haben sehr nützliche Anmerkungen. Sie vereinfachen Ihr Leben.
Deklarieren Sie zuerst Ihre native Abfrage, dann Ihre Ergebnismengenzuordnung (die die Zuordnung der von der Datenbank zurückgegebenen Daten zu Ihren POJOs definiert). Schreiben Sie Ihre POJO-Klasse, auf die Sie sich beziehen möchten (der Kürze halber hier nicht enthalten). Last but not least: Erstellen Sie eine Methode in einem DAO (z. B.), um die Abfrage aufzurufen. Dies hat bei mir in einer Dropwizard-App (1.0.0) funktioniert.
Deklarieren Sie zuerst eine native Abfrage in einer Entitätsklasse:
@NamedNativeQuery (
name = "domain.io.MyClass.myQuery",
query = "Select a.colA, a.colB from Table a",
resultSetMapping = "mappinMyNativeQuery") // must be the same name as in the SqlResultSetMapping declaration
Darunter können Sie die Resultset-Mapping-Deklaration hinzufügen:
@SqlResultSetMapping(
name = "mapppinNativeQuery", // same as resultSetMapping above in NativeQuery
classes = {
@ConstructorResult(
targetClass = domain.io.MyMapping.class,
columns = {
@ColumnResult( name = "colA", type = Long.class),
@ColumnResult( name = "colB", type = String.class)
}
)
}
)
Später in einem DAO können Sie auf die Abfrage verweisen als
public List<domain.io.MyMapping> findAll() {
return (namedQuery("domain.io.MyClass.myQuery").list());
}
Das ist es.
Schöne Antwort, aber ich glaube, Sie haben nach der ersten @ColumnResult-Anmerkung eine Klammer verpasst.
– mwasser
17. September 2017 um 13:10 Uhr
Es gibt Fehler im Code, aber leicht zu korrigieren. Beispiel: “resultSetMapping =” sollte “resultSetMapping =” sein
– Zbyszek
21. Februar 2018 um 8:12 Uhr
Ich sehe eine weitere bittere Wahrheit: NamedNativeQuery & SqlResultSetMapping muss in einer @Entity-Klasse sein
– Arnab Dutta
22. März 2018 um 20:26 Uhr
Tina
Wenn du benutzt Spring-jpa, dies ist eine Ergänzung zu den Antworten und dieser Frage. Bitte korrigieren Sie dies, falls Fehler vorhanden sind. Ich habe hauptsächlich drei Methoden verwendet, um ein “Mapping-Ergebnis” zu erzielen Object[] to a pojo”, basierend auf dem praktischen Bedürfnis, das ich erfülle:
Die eingebaute JPA-Methode reicht aus.
Die integrierte JPA-Methode reicht nicht aus, sondern eine angepasste sql mit Entity sind genug.
Die ersteren 2 sind fehlgeschlagen, und ich muss a verwenden nativeQuery. Hier sind die Beispiele. Das erwartete Pojo:
public class Antistealingdto {
private String secretKey;
private Integer successRate;
// GETTERs AND SETTERs
public Antistealingdto(String secretKey, Integer successRate) {
this.secretKey = secretKey;
this.successRate = successRate;
}
}
Methode 1: Ändern Sie das Pojo in eine Schnittstelle:
public interface Antistealingdto {
String getSecretKey();
Integer getSuccessRate();
}
@Query("select new AntistealingDTO(secretKey, successRate) from Antistealing where ....")
Antistealing whatevernamehere(conditions);
Hinweis: Die Parametersequenz des POJO-Konstruktors muss sowohl in der POJO-Definition als auch in SQL identisch sein.
Methode 3: Verwenden @SqlResultSetMapping und @NamedNativeQuery in Entity wie das Beispiel in Edwin Dalorzos Antwort.
Die ersten beiden Methoden würden viele In-the-Middle-Handler aufrufen, z. B. angepasste Konverter. Zum Beispiel, AntiStealing definiert a secretKey, bevor es beibehalten wird, wird ein Konverter eingefügt, um es zu verschlüsseln. Dies würde dazu führen, dass die ersten beiden Methoden ein konvertiertes Zurück zurückgeben secretKey was ich nicht will. Während die Methode 3 den Konverter überwinden und zurückkehren würde secretKey wäre dasselbe, wie es gespeichert ist (ein verschlüsseltes).
Die Unwrap-Prozedur kann durchgeführt werden, um Ergebnisse einer Nicht-Entität (das ist Beans/POJO) zuzuweisen. Das Verfahren ist wie folgt.
Die Verwendung dient der JPA-Hibernate-Implementierung.
beachten Sie, dass JobDTO sollte einen Standardkonstruktor haben. Oder Sie implementieren Ihren eigenen Transformator basierend auf AliasToBeanResultTransformer Implementierung.
– Lu55
14. Juni 2018 um 11:53 Uhr
Warum setParameter?
– Radhesh Khanna
29. Dezember 2020 um 15:21 Uhr
org.hibernate.Query.class).setResultTransformer zeigt die veraltete.
– Dhaval Bhoot
1. Dezember 2021 um 13:57 Uhr
Thanthu
Der einfachste Weg ist, so zu verwenden Projektionen. Es kann Abfrageergebnisse direkt Schnittstellen zuordnen und ist einfacher zu implementieren als die Verwendung von SqlResultSetMapping.
Ein Beispiel ist unten gezeigt:
@Repository
public interface PeopleRepository extends JpaRepository<People, Long> {
@Query(value = "SELECT p.name AS name, COUNT(dp.people_id) AS count " +
"FROM people p INNER JOIN dream_people dp " +
"ON p.id = dp.people_id " +
"WHERE p.user_id = :userId " +
"GROUP BY dp.people_id " +
"ORDER BY p.name", nativeQuery = true)
List<PeopleDTO> findByPeopleAndCountByUserId(@Param("userId") Long userId);
@Query(value = "SELECT p.name AS name, COUNT(dp.people_id) AS count " +
"FROM people p INNER JOIN dream_people dp " +
"ON p.id = dp.people_id " +
"WHERE p.user_id = :userId " +
"GROUP BY dp.people_id " +
"ORDER BY p.name", nativeQuery = true)
Page<PeopleDTO> findByPeopleAndCountByUserId(@Param("userId") Long userId, Pageable pageable);
}
// Interface to which result is projected
public interface PeopleDTO {
String getName();
Long getCount();
}
Die Felder der projizierten Schnittstelle müssen mit Feldern in dieser Entität übereinstimmen. Andernfalls könnte die Feldzuordnung brechen.
Auch wenn Sie verwenden SELECT table.column Notation definiert immer Aliase, die mit Namen von Entitäten übereinstimmen, wie im Beispiel gezeigt.
beachten Sie, dass JobDTO sollte einen Standardkonstruktor haben. Oder Sie implementieren Ihren eigenen Transformator basierend auf AliasToBeanResultTransformer Implementierung.
– Lu55
14. Juni 2018 um 11:53 Uhr
Warum setParameter?
– Radhesh Khanna
29. Dezember 2020 um 15:21 Uhr
org.hibernate.Query.class).setResultTransformer zeigt die veraltete.
– Dhaval Bhoot
1. Dezember 2021 um 13:57 Uhr
Yaswant raju Vysyaraju
Im Ruhezustand können Sie diesen Code verwenden, um Ihre native Abfrage einfach zuzuordnen.
private List < Map < String, Object >> getNativeQueryResultInMap() {
String mapQueryStr = "SELECT * FROM AB_SERVICE three ";
Query query = em.createNativeQuery(mapQueryStr);
NativeQueryImpl nativeQuery = (NativeQueryImpl) query;
nativeQuery.setResultTransformer(AliasToEntityMapResultTransformer.INSTANCE);
List < Map < String, Object >> result = query.getResultList();
for (Map map: result) {
System.out.println("after request ::: " + map);
}
return result;}
Dies ist eine sehr schöne und nützliche Lösung. Für mich geht das. Mit diesem List < Map < String, Object >> result kann ich alles machen. Ich kann den Wert in POJO festlegen oder in JSON oder irgendetwas konvertieren.
– Dhaval Bhoot
1. Dezember 2021 um 13:59 Uhr
9160700cookie-checkJPA : So konvertieren Sie eine Ergebnismenge einer nativen Abfrage in eine POJO-Klassensammlungyes
Überprüfen Sie diese Antwort. Es hat eine vollständige Antwort: stackoverflow.com/a/50365522/3073945
– Md. Sajedul Karim
16. Mai 2018 um 8:36 Uhr
er benutzt jpa, nicht spring
– ihm
18. Oktober 2018 um 0:21 Uhr