JPA: come convertire un set di risultati della query nativa in raccolta di classi POJO


174

Sto usando JPA nel mio progetto.

Sono arrivato a una query in cui ho bisogno di eseguire un'operazione di join su cinque tabelle. Quindi ho creato una query nativa che restituisce cinque campi.

Ora voglio convertire l'oggetto risultato in classe POJO Java che contiene le stesse cinque stringhe.

Esiste un modo in JPA per trasmettere direttamente quel risultato all'elenco degli oggetti POJO ??

Sono arrivato alla seguente soluzione ..

@NamedNativeQueries({  
    @NamedNativeQuery(  
        name = "nativeSQL",  
        query = "SELECT * FROM Actors",  
        resultClass = db.Actor.class),  
    @NamedNativeQuery(  
        name = "nativeSQL2",  
        query = "SELECT COUNT(*) FROM Actors",  
        resultClass = XXXXX) // <--------------- problem  
})  

Ora qui in resultClass, dobbiamo fornire una classe che è l'entità JPA effettiva? O possiamo convertirlo in qualsiasi classe POJO JAVA che contiene gli stessi nomi di colonna?


Controlla questa risposta. Ha risposta completa: stackoverflow.com/a/50365522/3073945
. Md Sajedul Karim

sta usando jpa, non in primavera
lui il

Risposte:


103

JPA fornisce un metodo SqlResultSetMappingche consente di mappare qualsiasi entità restituita dalla query nativa in un'entitào una classe personalizzata.

EDIT JPA 1.0 non consente il mapping su classi non entità. Solo in JPA 2.1 è stato aggiunto un ConstructorResult per mappare i valori di ritorno di una classe java.

Inoltre, per il problema di OP con il conteggio dovrebbe essere sufficiente definire una mappatura del set di risultati con un singolo ColumnResult


1
Grazie per la risposta. Qui stiamo mappando il nostro risultato con l'entità con la classe di entità java con annotazioni "@EntityResult" e "@FieldResult". Va bene. Ma qui ho bisogno di più chiarezza. È richiesto che la classe che stiamo mappando con il risultato sia una classe di entità JPA? O possiamo usare una semplice classe POJO che non è un acquisto di entità che ha tutte le variabili richieste come colonne nel set di risultati.
Gunjan Shah,

1
@GunjanShah: il modo migliore per sapere è provarlo :) Inoltre, un'entità è lo stesso pojo, solo con alcune annotazioni. fintanto che non stai cercando di insistere, rimarrà un pojo.
Denis Tulskiy,

2
Quando ho provato questo ho avuto un errore che la classe non era un'entità conosciuta. Ho finito per usare questo approccio stackoverflow.com/questions/5024533/… invece di provare a utilizzare una query nativa.
FGreg

2
@EdwinDalorzo: esatto per jpa 1.0. in jpa 2.1 sono stati aggiunti ConstructorResultcome uno dei parametri SqlResultSetMappingche consente di usare un pojo con tutti i campi impostati nel costruttore. Aggiornerò la risposta.
Denis Tulskiy,

4
Vedo un'altra amara verità: ConstructorResult può mappare su un POJO .. MA ConstructorResult stesso deve essere nella classe Entity, quindi Entity che non puoi evitare ... e quindi il fatto più grande: hai bisogno di un risultato senza preoccuparti a chiave primaria - devi comunque avere @Id in Entità ... ridicolo vero?
Arnab Dutta,

210

Ho trovato un paio di soluzioni a questo.

Utilizzo di entità mappate (JPA 2.0)

Utilizzando JPA 2.0 non è possibile mappare una query nativa a un POJO, può essere eseguita solo con un'entità.

Per esempio:

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

Ma in questo caso, Jedideve essere una classe entità mappata.

Un'alternativa per evitare l'avviso non selezionato qui, sarebbe quella di utilizzare una query nativa denominata. Quindi, se dichiariamo la query nativa in un'entità

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

Quindi, possiamo semplicemente fare:

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

Questo è più sicuro, ma siamo ancora limitati a utilizzare un'entità mappata.

Mappatura manuale

Una soluzione che ho sperimentato un po '(prima dell'arrivo di JPA 2.1) è stata quella di mappare un costruttore POJO usando un po' di riflessione.

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

Questo metodo fondamentalmente prende un array di tuple (come restituito da query native) e lo mappa su una classe POJO fornita cercando un costruttore che abbia lo stesso numero di campi e dello stesso tipo.

Quindi possiamo usare metodi convenienti come:

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

E possiamo semplicemente usare questa tecnica come segue:

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

JPA 2.1 con @SqlResultSetMapping

Con l'arrivo di JPA 2.1, possiamo usare l'annotazione @SqlResultSetMapping per risolvere il problema.

Dobbiamo dichiarare un set di risultati mappato da qualche parte in un'entità:

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

E poi facciamo semplicemente:

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

Naturalmente, in questo caso Jedinon deve essere un'entità mappata. Può essere un POJO normale.

Utilizzo della mappatura XML

Sono uno di quelli che trovano l'aggiunta di tutti questi @SqlResultSetMappingpiuttosto invasivi nelle mie entità, e in particolare non mi piace la definizione di query con nome all'interno delle entità, quindi in alternativa faccio tutto questo nel META-INF/orm.xmlfile:

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

E quelle sono tutte le soluzioni che conosco. Gli ultimi due sono il modo ideale se possiamo usare JPA 2.1.


1
Sidenote: ho appena usato l'approccio JPA 2.0 con dipendenza JPA2.1 e non è riuscito. Quindi probabilmente questo non è compatibile verso il basso ...
Membersound,

1
cosa intendi con "da qualche parte in un'entità"? Il mio Pojo non è un'entità JPA non posso dichiarare il @SqlResultSetMapping nel mio POJO? Sono interessato alle soluzioni JPA 2.1. Sii un po 'più preciso.
Alboz,

3
@Alboz @SqlResultSetMappingDeve essere inserito in un'entità perché è da questo che JPA leggerà i metadati. Non puoi aspettarti che l'APP controlli i tuoi POJO. L'entità in cui si posiziona la mappatura è irrilevante, forse quella più correlata ai risultati del POJO. In alternativa, la mappatura potrebbe essere espressa in XML per evitare l'accoppiamento con un'entità totalmente non correlata.
Edwin Dalorzo,

1
È possibile per il costruttore di utilizzare una classe che ha una classe nidificata?
chrismarx,

5
Se si utilizza JPA 2.1 con @SqlResultSetMappingesso può valere la pena notare che la Jediclasse richiederà un costruttore all-arg e l' @ColumnResultannotazione potrebbe richiedere l' typeattributo aggiunto alle conversioni che potrebbe non essere implicito (dovevo aggiungere type = ZonedDateTime.classalcune colonne).
Glenn,

11

Sì, con JPA 2.1 è facile. Hai annotazioni molto utili. Semplificano la tua vita.

Dichiarare innanzitutto la query nativa, quindi la mappatura del set di risultati (che definisce la mappatura dei dati restituiti dal database ai POJO). Scrivi la tua classe POJO a cui fare riferimento (non inclusa qui per brevità). Ultimo ma non meno importante: creare un metodo in un DAO (ad esempio) per chiamare la query. Questo ha funzionato per me in un'app Dropwizard (1.0.0).

Dichiara innanzitutto una query nativa in una classe di entità:

@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

Sotto puoi aggiungere la dichiarazione di mappatura del set di risultati:

@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)
          }
      )
   } 
)

Successivamente in un DAO è possibile fare riferimento alla query come

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

Questo è tutto.


Bella risposta, ma penso che ti sia sfuggita una parentesi dopo la prima annotazione di @ColumnResult.
mwatzer,

Ci sono errori nel codice, ma facili da correggere. Ad esempio: "resulSetMapping =" dovrebbe essere "resultSetMapping ="
Zbyszek

3
Vedo un'altra amara verità: NamedNativeQuery e SqlResultSetMapping devono essere in una classe @Entity
Arnab Dutta il

10

Se usi Spring-jpa, questo è un supplemento alle risposte e a questa domanda. Si prega di correggere questo in caso di difetti. Ho principalmente utilizzato tre metodi per ottenere il "risultato della mappatura Object[]su un pojo" in base alle esigenze pratiche che incontro:

  1. Il metodo integrato JPA è sufficiente.
  2. JPA costruita nel metodo non è sufficiente, ma una personalizzata sqlcon il suo Entitysono sufficienti.
  3. Il precedente 2 non è riuscito e devo usare a nativeQuery. Ecco gli esempi. Il pojo si aspettava:

    public class Antistealingdto {
    
        private String secretKey;
    
        private Integer successRate;
    
        // GETTERs AND SETTERs
    
        public Antistealingdto(String secretKey, Integer successRate) {
            this.secretKey = secretKey;
            this.successRate = successRate;
        }
    }

Metodo 1 : Cambia il pojo in un'interfaccia:

public interface Antistealingdto {
    String getSecretKey();
    Integer getSuccessRate();
}

E repository:

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

Metodo 2 : repository:

@Query("select new AntistealingDTO(secretKey, successRate) from Antistealing where ....")
Antistealing whatevernamehere(conditions);

Nota: la sequenza di parametri del costruttore POJO deve essere identica sia nella definizione POJO che in sql.

Metodo 3 : Utilizzo @SqlResultSetMappinge @NamedNativeQueryin Entitycome l'esempio nella risposta di Edwin Dalorzo.

I primi due metodi chiamerebbero molti gestori nel mezzo, come convertitori personalizzati. Ad esempio, AntiStealingdefinisce a secretKey, prima che sia persistente, viene inserito un convertitore per crittografarlo. Ciò comporterebbe i primi 2 metodi che restituiscono un ritorno convertito secretKeyche non è quello che voglio. Mentre il metodo 3 supererebbe il convertitore, e restituito secretKeysarebbe lo stesso di quello memorizzato (uno crittografato).


Il metodo 1 in realtà non richiede Spring e funziona con Hibernate puro.
Martin Vysny,

@MartinVysny sì, M1 è JPQL. qualsiasi progetto implementa JPQL dovrebbe supportarlo. In questo modo, forse anche M2 è ampiamente supportato?
Tiina,

8

La procedura Unwrap può essere eseguita per assegnare i risultati a una non entità (ovvero Beans / POJO). La procedura è la seguente.

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

L'utilizzo è per l'implementazione di JPA-Hibernate.


nota che JobDTOdovrebbe avere un costruttore predefinito. Oppure potresti implementare il tuo trasformatore in base AliasToBeanResultTransformerall'implementazione.
Lu55,

4

Dichiarare innanzitutto le seguenti annotazioni:

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

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

Quindi annota il POJO come segue:

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

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

Quindi scrivere il processore delle annotazioni:

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

Utilizzare il framework sopra come segue:

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 quale pacchetto si trova BeanUtils?
Harish

4

Il modo più semplice è usare così le proiezioni . Può mappare i risultati delle query direttamente alle interfacce ed è più facile da implementare rispetto all'utilizzo di SqlResultSetMapping.

Di seguito è mostrato un esempio:

@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();

}

I campi dell'interfaccia proiettata devono corrispondere ai campi in questa entità. Altrimenti la mappatura dei campi potrebbe interrompersi.

Inoltre, se si utilizza la SELECT table.columnnotazione, definire sempre gli alias corrispondenti ai nomi dall'entità, come mostrato nell'esempio.


1
query e proiezioni native non vanno bene insieme.
Kevin Rave,

1
Non riuscivo a far funzionare correttamente la mappatura dei campi - la maggior parte dei valori continuava a tornare come nulla
ayang

4

In ibernazione è possibile utilizzare questo codice per mappare facilmente la query nativa.

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

Utilizzando Hibernate:

@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

Vecchio stile usando 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

Poiché altri hanno già menzionato tutte le possibili soluzioni, sto condividendo la mia soluzione alternativa.

Nella mia situazione con Postgres 9.4, mentre lavoro con 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>>() {});

Sono sicuro che puoi trovare lo stesso per altri database.

Cordiali saluti, risultati delle query native di JPA 2.0 come mappa


1

Non sono sicuro se questo si adatta qui, ma ho avuto una domanda simile e ho trovato la seguente soluzione / esempio semplice per me:

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();

Nel mio caso ho dovuto usare parti SQL definite in Stringhe da qualche altra parte, quindi non potevo semplicemente usare NamedNativeQuery.


non appena ritorniamo entità. nulla di bello. il problema è quando si tenta di mappare il risultato a un POJO non gestito.
Olgun Kaya

1

Vecchio stile usando 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

Abbiamo risolto il problema nel modo seguente:

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

Vedere l'esempio seguente per l'utilizzo di un POJO come pseudo entità per recuperare il risultato dalla query nativa senza utilizzare SqlResultSetMapping complesso. Hai solo bisogno di due annotazioni, una @Enity nuda e una @Id fittizia nel tuo POJO. @Id può essere utilizzato su qualsiasi campo di tua scelta, un campo @Id può avere chiavi duplicate ma non valori nulli.

Poiché @Enity non si associa a nessuna tabella fisica, questo POJO è chiamato entità pseudo.

Ambiente: eclipselink 2.5.0-RC1, jpa-2.1.0, mysql-connettore-java-5.1.14

Puoi scaricare il progetto completo maven qui

La query nativa si basa sui dipendenti di esempio mysql 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
Dal momento che il tuo listè, presumibilmente, un elenco di Employee, perché il tuo ciclo for-each sta iterando su un tipo Object? Se scrivi il tuo ciclo per ogni come for(Employee emp : list)allora scopriresti che la tua risposta è sbagliata e che i contenuti del tuo elenco non sono impiegati e che quell'avvertimento che hai soppresso aveva lo scopo di avvisarti di questo potenziale errore.
Edwin Dalorzo,

@SuppressWarnings ("deselezionato") viene utilizzato per sopprimere l'avviso per List<Employee> list = (List<Employee>) query.getResultList();Modifica for (Object emp : list)in for (Employee emp : list)è migliore, ma nessun errore se conservato Object emppoiché l'elenco è un'istanza di List<Employee>. Ho cambiato il codice nel progetto git ma non qui per mantenere il tuo commento pertinente al post originale
Jonathan L

il problema è che la tua query non restituisce un elenco di dipendenti, ma una matrice di oggetti. Il tuo avviso represso lo nasconde. Nel momento in cui provi a convertire qualcuno di questi in un dipendente, otterrai un errore, un'eccezione di cast.
Edwin Dalorzo,

Guarda Query query = em.createNativeQuery("select * ...", Employee.class);e persistence.xml, la query nativa restituisce un elenco di Employee. Ho appena verificato ed eseguito il progetto senza problema. Se installi i dipendenti mysql db localmente, dovresti essere in grado di eseguire anche il progetto
Jonathan L

Oh capisco cosa intendi adesso. Ma in quel caso la tua risposta non soddisfa la domanda, perché si trattava di usare un POJO normale come oggetto target, e la tua risposta sta usando quella Employeeche presumo sia un'entità. No?
Edwin Dalorzo,

0

se stai usando Spring, puoi usare org.springframework.jdbc.core.RowMapper

Ecco un esempio:

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

Utilizzando Hibernate:

@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

Modo semplice per convertire query SQL in raccolta di classi POJO,

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

-1

Tutto ciò che serve è un DTO con un costruttore:

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

e chiamalo:

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());

Aggiungi una nuova colonna e il codice si interromperà.
Piatto

-2

Usa DTO Design Pattern. È stato utilizzato in EJB 2.0. L'entità era gestita dal container. DTO Design Patternè usato per risolvere questo problema. Ma potrebbe essere usato ora, quando l'applicazione è sviluppata Server Sidee Client Sideseparatamente. DTOviene utilizzato quando Server sidenon si desidera passare / tornare Entitycon l'annotazione a Client Side.

Esempio DTO:

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 <- potrebbe essere necessario

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

4
Grazie per la risposta. Qui non ho bisogno del modello DTO. Il mio requisito non è quello di nascondere i dettagli dell'annotazione dal client. Quindi non ho bisogno di creare un altro POJO nella mia app. Il mio requisito è quello di trasmettere il set di risultati a qa pojo che non è un'entità JAVA ma una semplice classe POJO con gli stessi campi delle colonne del set di risultati.
Gunjan Shah,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.