JPA : So konvertieren Sie eine Ergebnismenge einer nativen Abfrage in eine POJO-Klassensammlung

Lesezeit: 11 Minuten

JPA So konvertieren Sie eine Ergebnismenge einer nativen Abfrage
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

JPA So konvertieren Sie eine Ergebnismenge einer nativen Abfrage
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.

Zum Beispiel:

Query query = em.createNativeQuery("SELECT name,age FROM jedi_table", Jedi.class);
@SuppressWarnings("unchecked")
List<Jedi> items = (List<Jedi>) query.getResultList();

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

@NamedNativeQuery(
 name="jedisQry", 
 query = "SELECT name,age FROM jedis_table", 
 resultClass = Jedi.class)

Dann können wir einfach tun:

TypedQuery<Jedi> query = em.createNamedQuery("jedisQry", Jedi.class);
List<Jedi> items = query.getResultList();

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:

Query query = em.createNativeQuery("SELECT name,age FROM jedis_table");
List<Jedi> jedis = getResultList(query, Jedi.class);

JPA 2.1 mit @SqlResultSetMapping

Mit der Einführung von JPA 2.1 können wir die Annotation @SqlResultSetMapping verwenden, um das Problem zu lösen.

Wir müssen irgendwo in einer Entität ein Resultset-Mapping deklarieren:

@SqlResultSetMapping(name="JediResult", classes = {
    @ConstructorResult(targetClass = Jedi.class, 
    columns = {@ColumnResult(name="name"), @ColumnResult(name="age")})
})

Und dann machen wir einfach:

Query query = em.createNativeQuery("SELECT name,age FROM jedis_table", "JediResult");
@SuppressWarnings("unchecked")
List<Jedi> samples = query.getResultList();

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:

<named-native-query name="GetAllJedi" result-set-mapping="JediMapping">
    <query>SELECT name,age FROM jedi_table</query>
</named-native-query>

<sql-result-set-mapping name="JediMapping">
        <constructor-result target-class="org.answer.model.Jedi">
            <column name="name" class="java.lang.String"/>
            <column name="age" class="java.lang.Integer"/>
        </constructor-result>
    </sql-result-set-mapping>

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

1646254631 384 JPA So konvertieren Sie eine Ergebnismenge einer nativen Abfrage
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

1646254631 695 JPA So konvertieren Sie eine Ergebnismenge einer nativen Abfrage
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

1646254631 916 JPA So konvertieren Sie eine Ergebnismenge einer nativen Abfrage
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:

  1. Die eingebaute JPA-Methode reicht aus.
  2. Die integrierte JPA-Methode reicht nicht aus, sondern eine angepasste sql mit Entity sind genug.
  3. 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();
}

Und Depot:

interface AntiStealingRepository extends CrudRepository<Antistealing, Long> {
    Antistealingdto findById(Long id);
}

Methode 2: Repository:

@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.

List<JobDTO> dtoList = entityManager.createNativeQuery(sql)
        .setParameter("userId", userId)
        .unwrap(org.hibernate.Query.class).setResultTransformer(Transformers.aliasToBean(JobDTO.class)).list();

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

JPA So konvertieren Sie eine Ergebnismenge einer nativen Abfrage
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

1646254632 928 JPA So konvertieren Sie eine Ergebnismenge einer nativen Abfrage
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

916070cookie-checkJPA : So konvertieren Sie eine Ergebnismenge einer nativen Abfrage in eine POJO-Klassensammlung

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy