JPA: So konvertieren Sie eine native Abfrageergebnismenge in eine POJO-Klassensammlung


174

Ich verwende JPA in meinem Projekt.

Ich kam zu einer Abfrage, bei der ich eine Verknüpfungsoperation für fünf Tabellen ausführen muss. Also habe ich eine native Abfrage erstellt, die fünf Felder zurückgibt.

Jetzt möchte ich das Ergebnisobjekt in eine 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 kam zu folgender Lösung ..

@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 hier in resultClass eine Klasse bereitstellen, die eine tatsächliche JPA-Entität ist? ODER Wir können es in jede JAVA POJO-Klasse konvertieren, die dieselben Spaltennamen enthält.


Überprüfen Sie diese Antwort. Es hat vollständige Antwort: stackoverflow.com/a/50365522/3073945
Md. Sajedul Karim

er benutzt jpa, nicht spring
er

Antworten:


103

JPA bietet eine SqlResultSetMapping, mit der Sie alle Ergebnisse Ihrer nativen Abfrage einer Entität zuordnen könnenoder eine benutzerdefinierte Klasse.

EDIT JPA 1.0 erlaubt keine Zuordnung zu Nicht-Entitätsklassen. Nur in JPA 2.1 wurde ein ConstructorResult hinzugefügt, um Rückgabewerte einer Java-Klasse zuzuordnen.

Für das Problem von OP mit dem Abrufen der Zählung sollte es außerdem ausreichen, eine Ergebnismengenzuordnung mit einer einzelnen zu definieren ColumnResult


1
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 ist? ODER können wir eine einfache POJO-Klasse verwenden, bei der es sich nicht um einen Entitätskauf handelt, der alle erforderlichen Variablen als Spalten in der Ergebnismenge enthält.
Gunjan Shah

1
@GunjanShah: Der beste Weg, dies zu wissen, besteht darin, es auszuprobieren :) Außerdem ist eine Entität genau das gleiche Pojo, nur mit einigen Anmerkungen. Solange Sie nicht versuchen, es aufrechtzuerhalten, bleibt es ein Pojo.
Denis Tulskiy

2
Als ich dies versuchte, bekam ich die Fehlermeldung, dass die Klasse keine bekannte Entität war. Am Ende habe ich diesen Ansatz stackoverflow.com/questions/5024533/… verwendet, anstatt zu versuchen, eine native Abfrage zu verwenden.
FGreg

2
@ EdwinDalorzo: das ist richtig für jpa 1.0. in jpa 2.1 haben sie ConstructorResulteinen der Parameter hinzugefügt SqlResultSetMapping, mit denen ein Pojo mit allen im Konstruktor festgelegten Feldern verwendet werden kann. Ich werde die Antwort aktualisieren.
Denis Tulskiy

4
Ich sehe eine andere bittere Wahrheit: ConstructorResult kann einem POJO zugeordnet werden. ABER ConstructorResult selbst muss in der Entity-Klasse sein, damit Entity Sie nicht vermeiden können ... und daher die größere harte Tatsache: Sie brauchen ein Ergebnis, das Ihnen egal ist zum Primärschlüssel - trotzdem muss man @Id in Entity haben ... lächerlich, oder?
Arnab Dutta

210

Ich habe ein paar Lösungen dafür gefunden.

Verwenden zugeordneter Entitäten (JPA 2.0)

Mit JPA 2.0 ist es nicht möglich, eine native Abfrage einem POJO zuzuordnen, sondern nur mit einer Entität.

Zum Beispiel:

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

In diesem Fall Jedimuss es sich jedoch um eine zugeordnete Entitätsklasse handeln.

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, wir können jedoch weiterhin eine zugeordnete Entität verwenden.

Manuelle Zuordnung

Eine Lösung, die ich ein wenig experimentiert habe (vor der Ankunft von JPA 2.1), war das Mapping gegen einen 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 verwendet im Grunde genommen ein Tupel-Array (wie es von nativen Abfragen zurückgegeben wird) und ordnet es einer bereitgestellten POJO-Klasse zu, indem nach einem Konstruktor gesucht wird, der dieselbe Anzahl von Feldern und denselben Typ aufweist.

Dann können wir bequeme Methoden anwenden 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 eine Ergebnismengenzuordnung irgendwo in einer Entität 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 Jedimuss es sich natürlich nicht um eine zugeordnete Entität handeln. Es kann ein reguläres POJO sein.

Verwenden der XML-Zuordnung

Ich bin einer von denen, die das Hinzufügen all dieser @SqlResultSetMappingElemente in meinen Entitäten als ziemlich invasiv empfinden, und ich mag die Definition benannter Abfragen innerhalb von Entitäten besonders nicht. Alternativ mache ich das alles in der META-INF/orm.xmlDatei:

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


1
Nebenbemerkung: Ich habe gerade den JPA 2.0-Ansatz mit JPA2.1-Abhängigkeit verwendet und er ist fehlgeschlagen. Also wahrscheinlich ist dies nicht abwärtskompatibel ...
Membersound

1
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 seien Sie etwas genauer.
Alboz

3
@Alboz Das @SqlResultSetMappingmuss 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 das Mapping platzieren, ist irrelevant, möglicherweise diejenige, die mehr mit Ihren POJO-Ergebnissen zusammenhängt. Alternativ könnte die Zuordnung in XML ausgedrückt werden, um die Kopplung mit einer völlig unabhängigen Entität zu vermeiden.
Edwin Dalorzo

1
Kann das Konstruktionsergebnis eine Klasse mit einer verschachtelten Klasse verwenden?
Chrismarx

5
Wenn Sie JPA 2.1 mit verwenden, ist @SqlResultSetMappinges möglicherweise erwähnenswert, dass für die JediKlasse ein Konstruktor mit allen Argumenten erforderlich ist und für die @ColumnResultAnnotation möglicherweise das typeAttribut für Konvertierungen hinzugefügt werden muss, die möglicherweise nicht implizit sind (ich musste type = ZonedDateTime.classfür einige Spalten hinzufügen ).
Glenn

11

Ja, mit JPA 2.1 ist es einfach. Sie haben sehr nützliche Anmerkungen. Sie vereinfachen dein Leben.

Deklarieren Sie zuerst Ihre native Abfrage und dann Ihre Ergebnismengenzuordnung (die die Zuordnung der von der Datenbank an Ihre POJOs zurückgegebenen Daten 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 beispielsweise eine Methode in einem DAO, um die Abfrage aufzurufen. Dies funktionierte bei mir in einer Dropwizard (1.0.0) App.

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 Deklaration der Ergebnismengenzuordnung 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 als verweisen

public List<domain.io.MyMapping> findAll() {
        return (namedQuery("domain.io.MyClass.myQuery").list());
    }

Das ist es.


Schöne Antwort, aber ich denke, Sie haben nach der ersten Annotation @ColumnResult eine Klammer übersehen.
mwatzer

Der Code enthält Fehler, die jedoch leicht zu korrigieren sind. Zum Beispiel: "resulSetMapping =" sollte "resultSetMapping =" sein
Zbyszek

3
Ich sehe eine andere bittere Wahrheit: NamedNativeQuery & SqlResultSetMapping muss in einer @ Entity-Klasse sein
Arnab Dutta

10

Wenn Sie verwenden Spring-jpa, ist dies eine Ergänzung zu den Antworten und dieser Frage. Bitte korrigieren Sie dies bei Fehlern. Ich habe hauptsächlich drei Methoden verwendet, um ein "Mapping-Ergebnis Object[]für ein Pojo" zu erzielen, basierend auf dem praktischen Bedarf, den ich erfülle:

  1. JPA eingebaute Methode ist genug.
  2. JPA eingebaute Methode ist nicht genug, aber eine angepasste sqlmit seiner Entitysind genug.
  3. Die ersteren 2 sind fehlgeschlagen, und ich muss a verwenden nativeQuery. Hier sind die Beispiele. Das Pojo erwartet:

    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 Repository:

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 Parameterfolge des POJO-Konstruktors muss sowohl in der POJO-Definition als auch in SQL identisch sein.

Methode 3 : Verwenden Sie @SqlResultSetMappingund @NamedNativeQueryin Entityals Beispiel in Edwin Dalorzos Antwort.

Die ersten beiden Methoden rufen viele In-the-Middle-Handler auf, z. B. angepasste Konverter. Definiert beispielsweise AntiStealinga 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 Back zurückgeben, secretKeywas nicht das ist, was ich will. Während die Methode 3 den Konverter überwinden und zurückgegeben secretKeywürde, wäre sie dieselbe wie die gespeicherte (eine verschlüsselte).


Die Methode 1 benötigt eigentlich keine Feder und funktioniert mit reinem Ruhezustand.
Martin Vysny

@ MartinVysny ja, M1 ist JPQL. Alle Projekte, die JPQL implementieren, sollten dies unterstützen. Auf diese Weise wird M2 vielleicht auch weitgehend unterstützt?
Tiina

8

Das Entpacken kann durchgeführt werden, um Ergebnisse einer Nicht-Entität (dh 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 erfolgt für die Implementierung von JPA-Hibernate.


Beachten Sie, JobDTOdass der Standardkonstruktor vorhanden sein sollte. Oder Sie implementieren Ihren eigenen Transformator basierend auf der AliasToBeanResultTransformerImplementierung.
Lu55

4

Deklarieren Sie zunächst folgende Anmerkungen:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface NativeQueryResultEntity {
}

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface NativeQueryResultColumn {
    int index();
}

Kommentieren Sie dann Ihr POJO wie folgt:

@NativeQueryResultEntity
public class ClassX {
    @NativeQueryResultColumn(index=0)
    private String a;

    @NativeQueryResultColumn(index=1)
    private String b;
}

Dann schreiben Sie Annotation Processor:

public class NativeQueryResultsMapper {

    private static Logger log = LoggerFactory.getLogger(NativeQueryResultsMapper.class);

    public static <T> List<T> map(List<Object[]> objectArrayList, Class<T> genericType) {
        List<T> ret = new ArrayList<T>();
        List<Field> mappingFields = getNativeQueryResultColumnAnnotatedFields(genericType);
        try {
            for (Object[] objectArr : objectArrayList) {
                T t = genericType.newInstance();
                for (int i = 0; i < objectArr.length; i++) {
                    BeanUtils.setProperty(t, mappingFields.get(i).getName(), objectArr[i]);
                }
                ret.add(t);
            }
        } catch (InstantiationException ie) {
            log.debug("Cannot instantiate: ", ie);
            ret.clear();
        } catch (IllegalAccessException iae) {
            log.debug("Illegal access: ", iae);
            ret.clear();
        } catch (InvocationTargetException ite) {
            log.debug("Cannot invoke method: ", ite);
            ret.clear();
        }
        return ret;
    }

    // Get ordered list of fields
    private static <T> List<Field> getNativeQueryResultColumnAnnotatedFields(Class<T> genericType) {
        Field[] fields = genericType.getDeclaredFields();
        List<Field> orderedFields = Arrays.asList(new Field[fields.length]);
        for (int i = 0; i < fields.length; i++) {
            if (fields[i].isAnnotationPresent(NativeQueryResultColumn.class)) {
                NativeQueryResultColumn nqrc = fields[i].getAnnotation(NativeQueryResultColumn.class);
                orderedFields.set(nqrc.index(), fields[i]);
            }
        }
        return orderedFields;
    }
}

Verwenden Sie das obige Framework wie folgt:

String sql = "select a,b from x order by a";
Query q = entityManager.createNativeQuery(sql);

List<ClassX> results = NativeQueryResultsMapper.map(q.getResultList(), ClassX.class);

In welchem ​​Paket ist BeanUtils?
Harish

4

Der einfachste Weg ist, solche Projektionen zu verwenden . Es kann Abfrageergebnisse direkt Schnittstellen zuordnen und ist einfacher zu implementieren als 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 den Feldern in dieser Entität übereinstimmen. Andernfalls kann die Feldzuordnung unterbrochen werden.

Wenn Sie die SELECT table.columnNotation verwenden, definieren Sie immer Aliase, die mit den Namen der Entität übereinstimmen, wie im Beispiel gezeigt.


1
native Abfragen und Projektionen passen nicht gut zusammen.
Kevin Rave

1
Ich konnte die
Feldzuordnung

4

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;}

2

Verwenden des Ruhezustands:

@Transactional(readOnly=true)
public void accessUser() {
EntityManager em = repo.getEntityManager();
    org.hibernate.Session session = em.unwrap(org.hibernate.Session.class);
    org.hibernate.SQLQuery q = (org.hibernate.SQLQuery) session.createSQLQuery("SELECT u.username, u.name, u.email, 'blabla' as passe, login_type as loginType FROM users u").addScalar("username", StringType.INSTANCE).addScalar("name", StringType.INSTANCE).addScalar("email", StringType.INSTANCE).addScalar("passe", StringType.INSTANCE).addScalar("loginType", IntegerType.INSTANCE)
        .setResultTransformer(Transformers.aliasToBean(User2DTO.class));

    List<User2DTO> userList = q.list();
}

2

Alter Stil mit ResultSet

@Transactional(readOnly=true)
public void accessUser() {
    EntityManager em = this.getEntityManager();
    org.hibernate.Session session = em.unwrap(org.hibernate.Session.class);
    session.doWork(new Work() {
        @Override
        public void execute(Connection con) throws SQLException {
            try (PreparedStatement stmt = con.prepareStatement(
                    "SELECT u.username, u.name, u.email, 'blabla' as passe, login_type as loginType FROM users u")) {
                ResultSet rs = stmt.executeQuery();
                ResultSetMetaData rsmd = rs.getMetaData();
                for (int i = 1; i <= rsmd.getColumnCount(); i++) {
                    System.out.print(rsmd.getColumnName(i) + " (" + rsmd.getColumnTypeName(i) + ") / ");
                }
                System.out.println("");
                while (rs.next()) {
                    System.out.println("Found username " + rs.getString("USERNAME") + " name " + rs.getString("NAME") + " email " + rs.getString("EMAIL") + " passe " + rs.getString("PASSE") + " email " + rs.getInt("LOGIN_TYPE"));
                }
            }
        }
    });
}

1

Da andere bereits alle möglichen Lösungen erwähnt haben, teile ich meine Problemumgehungslösung.

In meiner Situation mit Postgres 9.4, während ich arbeite mit Jackson,

//Convert it to named native query.
List<String> list = em.createNativeQuery("select cast(array_to_json(array_agg(row_to_json(a))) as text) from myschema.actors a")
                   .getResultList();

List<ActorProxy> map = new ObjectMapper().readValue(list.get(0), new TypeReference<List<ActorProxy>>() {});

Ich bin sicher, dass Sie dasselbe für andere Datenbanken finden können.

Auch FYI, JPA 2.0 native Abfrageergebnisse als Karte


1

Ich bin mir nicht sicher, ob dies hier passt, aber ich hatte eine ähnliche Frage und fand folgende einfache Lösung / Beispiel für mich:

private EntityManager entityManager;
...
    final String sql = " SELECT * FROM STORE "; // select from the table STORE
    final Query sqlQuery = entityManager.createNativeQuery(sql, Store.class);

    @SuppressWarnings("unchecked")
    List<Store> results = (List<Store>) sqlQuery.getResultList();

In meinem Fall musste ich SQL-Teile verwenden, die in Strings an einer anderen Stelle definiert waren, damit ich nicht nur NamedNativeQuery verwenden konnte.


sobald wir Entität zurückgeben. Nichts Besonderes. Problem ist, wenn Sie versuchen, das Ergebnis einem nicht verwalteten POJO zuzuordnen.
Olgun Kaya

1

Alter Stil mit Resultset

@Transactional(readOnly=true)
public void accessUser() {
    EntityManager em = this.getEntityManager();
    org.hibernate.Session session = em.unwrap(org.hibernate.Session.class);
    session.doWork(new Work() {
        @Override
        public void execute(Connection con) throws SQLException {
            try (PreparedStatement stmt = con.prepareStatement(
                    "SELECT u.username, u.name, u.email, 'blabla' as passe, login_type as loginType FROM users u")) {
                ResultSet rs = stmt.executeQuery();
                ResultSetMetaData rsmd = rs.getMetaData();
                for (int i = 1; i <= rsmd.getColumnCount(); i++) {
                    System.out.print(rsmd.getColumnName(i) + " (" + rsmd.getColumnTypeName(i) + ") / ");
                }
                System.out.println("");
                while (rs.next()) {
                    System.out.println("Found username " + rs.getString("USERNAME") + " name " + rs.getString("NAME") + " email " + rs.getString("EMAIL") + " passe " + rs.getString("PASSE") + " email " + rs.getInt("LOGIN_TYPE"));
                }
            }
        }
    });
}

1

Wir haben das Problem folgendermaßen gelöst:

   //Add actual table name here in Query
    final String sqlQuery = "Select a.* from ACTORS a"
    // add your entity manager here 
    Query query = entityManager.createNativeQuery(sqlQuery,Actors.class);
    //List contains the mapped entity data.
    List<Actors> list = (List<Actors>) query.getResultList();

0

Im folgenden Beispiel wird ein POJO als Pseudoentität zum Abrufen von Ergebnissen aus nativen Abfragen ohne Verwendung von komplexem SqlResultSetMapping verwendet. Benötigen Sie nur zwei Anmerkungen, eine nackte @Enity und eine Dummy-@Id in Ihrem POJO. @Id kann für jedes Feld Ihrer Wahl verwendet werden. Ein @ ID-Feld kann doppelte Schlüssel, aber keine Nullwerte enthalten.

Da @Enity keiner physischen Tabelle zugeordnet ist, wird dieses POJO als Pseudoentität bezeichnet.

Umgebung: eclipselink 2.5.0-RC1, jpa-2.1.0, mysql-connector-java-5.1.14

Sie können das komplette Maven-Projekt hier herunterladen

Die native Abfrage basiert auf MySQL-Beispielmitarbeitern db http://dev.mysql.com/doc/employee/en/employees-installation.html

persistence.xml

<?xml version="1.0" encoding="UTF-8"?><persistence xmlns="http://xmlns.jcp.org/xml/ns/persistence" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.1" 
    xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">
<persistence-unit name="jpa-mysql" transaction-type="RESOURCE_LOCAL">
    <class>org.moonwave.jpa.model.pojo.Employee</class>
    <properties>
        <property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/employees" />
        <property name="javax.persistence.jdbc.user" value="user" />
        <property name="javax.persistence.jdbc.password" value="***" />
        <property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver" />
    </properties>
</persistence-unit>

Employee.java

package org.moonwave.jpa.model.pojo;

@Entity
public class Employee {

@Id
protected Long empNo;

protected String firstName;
protected String lastName;
protected String title;

public Long getEmpNo() {
    return empNo;
}
public void setEmpNo(Long empNo) {
    this.empNo = empNo;
}
public String getFirstName() {
    return firstName;
}
public void setFirstName(String firstName) {
    this.firstName = firstName;
}
public String getLastName() {
    return lastName;
}
public void setLastName(String lastName) {
    this.lastName = lastName;
}   
public String getTitle() {
    return title;
}
public void setTitle(String title) {
    this.title = title;
}
public String toString() {
    StringBuilder sb = new StringBuilder();
    sb.append("empNo: ").append(empNo);
    sb.append(", firstName: ").append(firstName);
    sb.append(", lastName: ").append(lastName);
    sb.append(", title: ").append(title);
    return sb.toString();
}
}

EmployeeNativeQuery.java

public class EmployeeNativeQuery {
private EntityManager em;
private EntityManagerFactory emf;

public void setUp() throws Exception {
    emf=Persistence.createEntityManagerFactory("jpa-mysql");
    em=emf.createEntityManager();
}
public void tearDown()throws Exception {
    em.close();
    emf.close();
}

@SuppressWarnings("unchecked")
public void query() {
    Query query = em.createNativeQuery("select e.emp_no as empNo, e.first_name as firstName, e.last_name as lastName," + 
            "t.title from employees e join titles t on e.emp_no = t.emp_no", Employee.class);
    query.setMaxResults(30);
    List<Employee> list = (List<Employee>) query.getResultList();
    int i = 0;
    for (Object emp : list) {
        System.out.println(++i + ": " + emp.toString());
    }
}

public static void main( String[] args ) {
    EmployeeNativeQuery test = new EmployeeNativeQuery();
    try {
        test.setUp();
        test.query();
        test.tearDown();
    } catch (Exception e) {
        System.out.println(e);
    }
}
}

1
Da listes sich angeblich um eine Liste handelt Employee, warum iteriert Ihre for-each-Schleife über einen Typ Object? Wenn Sie Ihre for-each-Schleife so schreiben, als würden for(Employee emp : list)Sie feststellen, dass Ihre Antwort falsch ist und der Inhalt Ihrer Liste keine Mitarbeiter sind und dass diese von Ihnen unterdrückte Warnung den Zweck hatte, Sie auf diesen möglichen Fehler aufmerksam zu machen.
Edwin Dalorzo

@SuppressWarnings ("nicht markiert") wird verwendet, um die Warnung zu unterdrücken, dass " List<Employee> list = (List<Employee>) query.getResultList();Ändern for (Object emp : list)in" for (Employee emp : list)besser ist, aber keine Fehler, wenn sie beibehalten wird, Object empda "Liste" eine Instanz von ist List<Employee>. Ich habe den Code in Git Project geändert, aber nicht hier, um Ihren Kommentar für den ursprünglichen Beitrag relevant zu halten
Jonathan L

Das Problem ist, dass Ihre Abfrage keine Liste der Beschäftigten, sondern ein Array von Objekten zurückgibt. Ihre unterdrückte Warnung verbirgt das. In dem Moment, in dem Sie versuchen, einen dieser Mitarbeiter in einen Mitarbeiter umzuwandeln, wird ein Fehler angezeigt, eine Besetzungsausnahme.
Edwin Dalorzo

Schauen Sie sich Query query = em.createNativeQuery("select * ...", Employee.class);und persistence.xml an, die native Abfrage gibt eine Liste der Mitarbeiter zurück. Ich habe gerade das Projekt ohne Problem ausgecheckt und ausgeführt. Wenn Sie MySQL-Beispielmitarbeiter db lokal einrichten, sollten Sie auch in der Lage sein, das Projekt auszuführen
Jonathan L

Oh, ich verstehe, was du jetzt meinst. In diesem Fall erfüllt Ihre Antwort die Frage jedoch nicht, da es sich um die Verwendung eines regulären POJO als Zielobjekt handelte und Ihre Antwort Employeeeine Entität ist, von der ich annehme, dass sie eine Entität ist. Ist es nicht?
Edwin Dalorzo

0

Wenn Sie Spring verwenden, können Sie verwenden org.springframework.jdbc.core.RowMapper

Hier ist ein Beispiel:

public List query(String objectType, String namedQuery)
{
  String rowMapper = objectType + "RowMapper";
  // then by reflection you can instantiate and use. The RowMapper classes need to follow the naming specific convention to follow such implementation.
} 

0

Verwenden des Ruhezustands:

@Transactional(readOnly=true)
public void accessUser() {
    EntityManager em = repo.getEntityManager();
    org.hibernate.Session session = em.unwrap(org.hibernate.Session.class);
    org.hibernate.SQLQuery q = (org.hibernate.SQLQuery) session.createSQLQuery("SELECT u.username, u.name, u.email, 'blabla' as passe, login_type as loginType FROM users u")
        .addScalar("username", StringType.INSTANCE).addScalar("name", StringType.INSTANCE)
        .addScalar("email", StringType.INSTANCE).addScalar("passe", StringType.INSTANCE)
        .addScalar("loginType", IntegerType.INSTANCE)
        .setResultTransformer(Transformers.aliasToBean(User2DTO.class));

    List<User2DTO> userList = q.list();
}

-1

Einfache Möglichkeit zum Konvertieren einer SQL-Abfrage in eine POJO-Klassensammlung.

Query query = getCurrentSession().createSQLQuery(sqlQuery).addEntity(Actors.class);
List<Actors> list = (List<Actors>) query.list();
return list;

-1

Sie benötigen lediglich ein DTO mit einem Konstruktor:

public class User2DTO implements Serializable {

    /** pode ser email ou id do Google comecando com G ou F para Facebook */
    private String username;

    private String password;

    private String email;

    private String name;

    private Integer loginType;

    public User2DTO(Object...fields) {
        super();
        this.username = (String) fields[0];
        this.name = (String) fields[1];
        this.email = (String) fields[2];
        this.password = (String) fields[3];
        this.loginType = (Integer) fields[4];
    }

und nenne es:

EntityManager em = repo.getEntityManager();
        Query q = em.createNativeQuery("SELECT u.username, u.name, u.email, 'blabla' as passe, login_type as loginType FROM users u");
        List<Object[]> objList = q.getResultList();
        List<User2DTO> ooBj = objList.stream().map(User2DTO::new).collect(Collectors.toList());

Wenn Sie eine neue Spalte hinzufügen, wird der Code unterbrochen.
Dish

-2

Verwenden Sie DTO Design Pattern. Es wurde in verwendet EJB 2.0. Die Entität wurde von einem Container verwaltet. DTO Design Patternwird verwendet, um dieses Problem zu lösen. Es kann jedoch jetzt verwendet werden, wenn die Anwendung entwickelt Server Sideund Client Sideseparat erstellt wird. DTOwird verwendet, wenn Server sidenicht Entitymit Annotation an übergeben / zurückgegeben werden soll Client Side.

DTO-Beispiel:

PersonEntity.java

@Entity
public class PersonEntity {
    @Id
    private String id;
    private String address;

    public PersonEntity(){

    }
    public PersonEntity(String id, String address) {
        this.id = id;
        this.address = address;
    }
    //getter and setter

}

PersonDTO.java

public class PersonDTO {
    private String id;
    private String address;

    public PersonDTO() {
    }
    public PersonDTO(String id, String address) {
        this.id = id;
        this.address = address;
    }

    //getter and setter 
}

DTOBuilder.java

public class DTOBuilder() {
    public static PersonDTO buildPersonDTO(PersonEntity person) {
        return new PersonDTO(person.getId(). person.getAddress());
    }
}

EntityBuilder.java <- es muss sein

public class EntityBuilder() {
    public static PersonEntity buildPersonEntity(PersonDTO person) {
        return new PersonEntity(person.getId(). person.getAddress());
    }
}

4
Vielen Dank für die Antwort. Hier brauche ich kein DTO-Muster. Meine Anforderung besteht darin, die Anmerkungsdetails nicht vor dem Client zu verbergen. Ich muss also kein weiteres POJO in meiner App erstellen. Meine Anforderung besteht darin, die Ergebnismenge in qa pojo umzuwandeln, das keine JAVA-Entität ist, sondern eine einfache POJO-Klasse, die dieselben Felder wie Ergebnismengenspalten hat.
Gunjan Shah
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.