JPA: Cara mengonversi hasil kueri asli yang disetel ke koleksi kelas POJO

174

Saya menggunakan JPA dalam proyek saya.

Saya sampai pada permintaan di mana saya perlu membuat operasi gabungan di lima tabel. Jadi saya membuat kueri asli yang mengembalikan lima bidang.

Sekarang saya ingin mengonversi objek hasil ke kelas java POJO yang berisi lima Strings yang sama.

Apakah ada cara di JPA untuk secara langsung melemparkan hasil itu ke daftar objek POJO ??

Saya datang ke solusi berikut ..

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

Sekarang di sini di resultClass, apakah kita perlu menyediakan kelas yang merupakan entitas JPA yang sebenarnya? ATAU Kami dapat mengonversinya ke kelas JAVA POJO yang berisi nama kolom yang sama?

Gunjan Shah
sumber
Periksa jawaban ini. Ini memiliki jawaban lengkap: stackoverflow.com/a/50365522/3073945
Md. Sajedul Karim
dia menggunakan jpa, bukan musim semi
dia

Jawaban:

103

JPA menyediakan suatu SqlResultSetMappingyang memungkinkan Anda untuk memetakan apa pun pengembalian dari permintaan asli Anda ke Entitasatau kelas khusus.

EDIT JPA 1.0 tidak memungkinkan pemetaan untuk kelas non-entitas. Hanya di JPA 2.1 ConstructorResult telah ditambahkan untuk memetakan nilai pengembalian kelas java.

Juga, untuk masalah OP dengan penghitungan, itu harus cukup untuk mendefinisikan pemetaan set hasil dengan satu ColumnResult

Denis Tulskiy
sumber
1
Terima kasih balasannya. Di sini kita memetakan hasil kita dengan entitas dengan kelas entitas java dengan anotasi "@EntityResult" dan "@FieldResult". Tidak apa-apa. Tapi di sini saya perlu lebih jelas. Apakah diperlukan bahwa kelas yang kita pemetaan dengan hasil harus kelas entitas JPA? ATAU bisakah kita menggunakan kelas POJO sederhana yang bukan merupakan entitas beli yang memiliki semua variabel yang diperlukan sebagai kolom di set hasil.
Gunjan Shah
1
@ GunjanShah: cara terbaik untuk mengetahui adalah mencobanya :) juga, entitas adalah pojo yang sama, hanya dengan beberapa penjelasan. selama Anda tidak mencoba untuk bertahan, itu akan tetap menjadi pojo.
Denis Tulskiy
2
Ketika saya mencoba ini saya mendapat kesalahan bahwa kelas itu bukan Entitas yang dikenal. Saya akhirnya menggunakan pendekatan ini stackoverflow.com/questions/5024533/... alih-alih mencoba menggunakan kueri asli.
FGreg
2
@ EdwinDalorzo: itu tepat untuk jpa 1.0. di jpa 2.1 mereka telah menambahkan ConstructorResultsebagai salah satu parameter SqlResultSetMappingyang memungkinkan untuk menggunakan pojo dengan semua bidang yang ditetapkan dalam konstruktor. Saya akan memperbarui jawabannya.
Denis Tulskiy
4
Saya melihat kebenaran pahit lain: ConstructorResult dapat memetakan ke POJO .. TETAPI ConstructorResult itu sendiri harus berada di kelas Entity sehingga Entity Anda tidak dapat menghindari ... dan karenanya fakta keras yang lebih besar: Anda butuh hasil dengan tidak peduli ke primary-key - masih Anda harus memiliki @ ID di Entity ... konyol kan?
Arnab Dutta
210

Saya telah menemukan beberapa solusi untuk ini.

Menggunakan Entitas yang Dipetakan (JPA 2.0)

Menggunakan JPA 2.0 tidak mungkin untuk memetakan permintaan asli ke POJO, itu hanya dapat dilakukan dengan suatu entitas.

Misalnya:

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

Tetapi dalam kasus ini Jedi,, harus kelas entitas yang dipetakan.

Alternatif untuk menghindari peringatan yang tidak dicentang di sini, adalah dengan menggunakan kueri asli yang bernama. Jadi jika kita mendeklarasikan kueri asli dalam suatu entitas

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

Kemudian, kita cukup melakukan:

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

Ini lebih aman, tetapi kami masih dibatasi untuk menggunakan entitas yang dipetakan.

Pemetaan Manual

Sebuah solusi yang saya coba sedikit (sebelum kedatangan JPA 2.1) melakukan pemetaan terhadap konstruktor POJO menggunakan sedikit refleksi.

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

Metode ini pada dasarnya mengambil array tuple (seperti yang dikembalikan oleh query asli) dan memetakannya terhadap kelas POJO yang disediakan dengan mencari konstruktor yang memiliki jumlah bidang yang sama dan jenis yang sama.

Kemudian kita dapat menggunakan metode yang mudah digunakan seperti:

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

Dan kita cukup menggunakan teknik ini sebagai berikut:

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

JPA 2.1 dengan @SqlResultSetMapping

Dengan kedatangan JPA 2.1, kita dapat menggunakan anotasi @SqlResultSetMapping untuk menyelesaikan masalah.

Kita perlu mendeklarasikan pemetaan kumpulan hasil di suatu tempat di suatu entitas:

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

Dan kemudian kita lakukan:

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

Tentu saja, dalam hal ini Jeditidak perlu menjadi entitas yang dipetakan. Ini bisa menjadi POJO biasa.

Menggunakan Pemetaan XML

Saya adalah salah satu dari mereka yang menemukan menambahkan semua ini @SqlResultSetMappingcukup invasif di entitas saya, dan saya sangat tidak suka definisi nama query dalam entitas, jadi saya melakukan semua ini dalam 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>

Dan itu semua solusi yang saya tahu. Dua yang terakhir adalah cara yang ideal jika kita dapat menggunakan JPA 2.1.

Edwin Dalorzo
sumber
1
Sidenote: Saya baru saja menggunakan pendekatan JPA 2.0 dengan ketergantungan JPA2.1, dan gagal. Jadi mungkin ini tidak kompatibel ke bawah ...
memberound
1
apa yang Anda maksud dengan "suatu tempat di suatu entitas"? Pojo saya bukan Entitas JPA tidak bisakah saya mendeklarasikan @SqlResultSetMapping di POJO saya? Saya tertarik dengan solusi JPA 2.1. Harap sedikit lebih tepat.
Alboz
3
@Alboz @SqlResultSetMappingharus ditempatkan dalam suatu entitas karena dari situlah JPA akan membaca metadata dari. Anda tidak dapat mengharapkan JPA untuk memeriksa POJO Anda. Entitas tempat Anda meletakkan pemetaan tidak relevan, mungkin entitas yang lebih terkait dengan hasil POJO Anda. Sebagai alternatif, pemetaan dapat diekspresikan dalam XML untuk menghindari pemasangan dengan entitas yang sama sekali tidak terkait.
Edwin Dalorzo
1
Apakah mungkin bagi konstruktor untuk menggunakan kelas yang memiliki kelas bersarang?
chrismarx
5
Jika menggunakan JPA 2.1 dengan @SqlResultSetMappingitu mungkin perlu dicatat bahwa Jedikelas akan membutuhkan konstruktor all-arg dan @ColumnResultanotasi mungkin memerlukan typeatribut yang ditambahkan ke konversi yang mungkin tidak implisit (saya perlu menambahkan type = ZonedDateTime.classbeberapa kolom).
Glenn
11

Ya, dengan JPA 2.1 itu mudah. Anda memiliki Anotasi yang sangat berguna. Mereka menyederhanakan hidup Anda.

Pertama, nyatakan kueri asli Anda, lalu pemetaan set hasil Anda (yang menentukan pemetaan data yang dikembalikan oleh database ke POJO Anda). Tulis kelas POJO Anda untuk merujuk (tidak termasuk di sini untuk singkatnya). Last but not least: buat metode dalam DAO (misalnya) untuk memanggil kueri. Ini bekerja untuk saya di aplikasi dropwizard (1.0.0).

Pertama mendeklarasikan kueri asli dalam kelas entitas:

@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

Di bawahnya Anda dapat menambahkan deklarasi pemetaan resultset:

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

Kemudian dalam DAO Anda bisa merujuk ke kueri sebagai

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

Itu dia.

GiulioDS
sumber
Jawaban yang bagus, tapi saya pikir Anda telah melewatkan tanda kurung setelah penjelasan @ColumnResult pertama.
mwatzer
Ada kesalahan dalam kode, tetapi mudah diperbaiki. Sebagai contoh: "resulSetMapping =" harus "resultSetMapping ="
Zbyszek
3
Saya melihat kebenaran pahit lainnya: NamedNativeQuery & SqlResultSetMapping harus dalam kelas @Entity
Arnab Dutta
10

Jika Anda menggunakan Spring-jpa, ini adalah suplemen untuk jawaban dan pertanyaan ini. Perbaiki ini jika ada kekurangan. Saya terutama menggunakan tiga metode untuk mencapai "hasil pemetaan Object[]ke pojo" berdasarkan pada kebutuhan praktis yang saya penuhi:

  1. Metode bawaan JPA sudah cukup.
  2. Metode bawaan JPA tidak cukup, tetapi disesuaikan sqldengan itu Entitysudah cukup.
  3. 2 mantan gagal, dan saya harus menggunakan a nativeQuery. Berikut ini contohnya. Pojo berharap:

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

Metode 1 : Ubah pojo menjadi antarmuka:

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

Dan repositori:

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

Metode 2 : Gudang:

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

Catatan: urutan parameter konstruktor POJO harus identik dalam definisi POJO dan sql.

Metode 3 : Gunakan @SqlResultSetMappingdan @NamedNativeQuerydi Entitysebagai contoh dalam jawaban Edwin Dalorzo ini.

Dua metode pertama akan memanggil banyak penangan di tengah, seperti konverter kustom. Misalnya, AntiStealingtentukan a secretKey, sebelum tetap ada, konverter dimasukkan untuk mengenkripsi itu. Ini akan menghasilkan 2 metode pertama mengembalikan kembali dikonversi secretKeyyang bukan yang saya inginkan. Sementara metode 3 akan mengatasi konverter, dan kembali secretKeyakan sama dengan yang disimpan (yang terenkripsi).

Tiina
sumber
Metode 1 sebenarnya tidak memerlukan Spring dan berfungsi dengan Hibernate murni.
Martin Vysny
@ MartinVysny ya, M1 adalah JPQL. setiap proyek yang mengimplementasikan JPQL harus mendukungnya. Dengan cara ini, mungkin M2 juga didukung secara luas?
Tiina
8

Prosedur unwrap dapat dilakukan untuk menetapkan hasil ke non-entitas (yaitu Kacang / POJO). Prosedurnya adalah sebagai berikut.

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

Penggunaan ini untuk implementasi JPA-Hibernate.

zawhtut
sumber
perhatikan bahwa JobDTOharus memiliki konstruktor default. Atau Anda dapat mengimplementasikan transformator Anda sendiri berdasarkan AliasToBeanResultTransformerimplementasi.
Lu55
4

Pertama, nyatakan penjelasan berikut:

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

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

Kemudian beri catatan POJO Anda sebagai berikut:

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

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

Kemudian tulis prosesor anotasi:

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

Gunakan kerangka kerja di atas sebagai berikut:

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

List<ClassX> results = NativeQueryResultsMapper.map(q.getResultList(), ClassX.class);
riship89
sumber
Paket mana yang ada BeanUtils?
Harish
4

Cara termudah adalah dengan menggunakan proyeksi . Itu dapat memetakan hasil kueri langsung ke antarmuka dan lebih mudah diimplementasikan daripada menggunakan SqlResultSetMapping.

Contohnya ditunjukkan di bawah ini:

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

}

Bidang dari antarmuka yang diproyeksikan harus cocok dengan bidang di entitas ini. Kalau tidak, pemetaan lapangan mungkin rusak.

Juga jika Anda menggunakan SELECT table.columnnotasi selalu tentukan alias pencocokan nama dari entitas seperti yang ditunjukkan dalam contoh.

Thanthu
sumber
1
kueri dan proyeksi asli tidak berjalan seiring.
Kevin Rave
1
Saya tidak bisa mendapatkan pemetaan lapangan berfungsi dengan benar - sebagian besar nilai terus kembali sebagai nol
ayang
4

Dalam hibernasi Anda dapat menggunakan kode ini untuk memetakan kueri asli Anda dengan mudah.

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;}
Yaswanth raju Vysyaraju
sumber
2

Menggunakan 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();
}
Rubens
sumber
2

Gaya lama menggunakan 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"));
                }
            }
        }
    });
}
Rubens
sumber
1

Karena orang lain telah menyebutkan semua solusi yang mungkin, saya membagikan solusi pemecahan masalah saya.

Dalam situasi saya dengan Postgres 9.4, saat bekerja dengan 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>>() {});

Saya yakin Anda dapat menemukan yang sama untuk database lain.

Juga hasil pencarian asli FYI, JPA 2.0 sebagai peta

Darshan Patel
sumber
1

Tidak yakin apakah ini cocok di sini, tetapi saya memiliki pertanyaan serupa dan menemukan solusi / contoh sederhana berikut untuk saya:

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

Dalam kasus saya, saya harus menggunakan bagian SQL yang didefinisikan dalam String di tempat lain, jadi saya tidak bisa hanya menggunakan NamedNativeQuery.

Andreas L.
sumber
segera setelah kami mengembalikan entitas. tidak ada yang mewah. Masalahnya adalah ketika Anda mencoba memetakan hasilnya ke POJO yang tidak dikelola.
Olgun Kaya
1

Gaya lama menggunakan 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"));
                }
            }
        }
    });
}
Rubens
sumber
1

Kami telah menyelesaikan masalah menggunakan cara berikut:

   //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();
Akash
sumber
0

Lihat contoh di bawah ini untuk menggunakan POJO sebagai entitas semu untuk mengambil hasil dari kueri asli tanpa menggunakan SqlResultSetMapping yang kompleks. Hanya perlu dua anotasi, @Enitas kosong dan @Id dummy di POJO Anda. @Id dapat digunakan pada bidang apa pun pilihan Anda, bidang @Id dapat memiliki kunci duplikat tetapi bukan nilai null.

Karena @Enity tidak memetakan ke tabel fisik apa pun, jadi POJO ini disebut entitas semu.

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

Anda dapat mengunduh proyek pakar lengkap di sini

Kueri asli didasarkan pada sampel karyawan 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);
    }
}
}
Jonathan L.
sumber
1
Karena Anda list, menurut dugaan, adalah daftar Employee, mengapa untuk setiap loop Anda mengulangi suatu tipe Object? Jika Anda menulis untuk-setiap loop for(Employee emp : list)Anda maka Anda akan menemukan bahwa jawaban Anda salah dan isi daftar Anda bukan karyawan dan bahwa peringatan yang Anda tekan memiliki tujuan untuk mengingatkan Anda tentang kesalahan potensial ini.
Edwin Dalorzo
@SuppressWarnings ("tidak dicentang") digunakan untuk menekan peringatan agar List<Employee> list = (List<Employee>) query.getResultList();Perubahan for (Object emp : list)menjadi for (Employee emp : list)lebih baik, tetapi tidak ada kesalahan jika disimpan Object empkarena daftar adalah turunan dari List<Employee>. Saya mengubah kode dalam proyek git tetapi tidak di sini untuk menjaga komentar Anda tetap relevan dengan posting asli
Jonathan L
masalahnya adalah bahwa permintaan Anda tidak mengembalikan daftar karyawan, tetapi sebuah array objek. Peringatan tertekan Anda menyembunyikan itu. Saat Anda mencoba mengonversi semua itu menjadi karyawan, Anda akan mendapatkan kesalahan, pengecualian pemain.
Edwin Dalorzo
Lihat Query query = em.createNativeQuery("select * ...", Employee.class);dan persistence.xml, kueri asli memang mengembalikan daftar Karyawan. Saya baru saja memeriksa dan menjalankan proyek tanpa masalah. Jika Anda mengatur sampel karyawan mysql db secara lokal, Anda harus dapat menjalankan proyek juga
Jonathan L
Oh, aku mengerti maksudmu sekarang. Tetapi dalam kasus itu jawaban Anda tidak memuaskan pertanyaan, karena ini tentang menggunakan POJO biasa sebagai objek target, dan jawaban Anda menggunakan Employeeyang saya anggap sebagai entitas. Bukan?
Edwin Dalorzo
0

jika Anda menggunakan Spring, Anda bisa menggunakan org.springframework.jdbc.core.RowMapper

Berikut ini sebuah contoh:

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.
} 
Pallab Rath
sumber
0

Menggunakan 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();
}
Rubens
sumber
-1

Cara mudah untuk mengubah kueri SQL ke koleksi kelas POJO,

Query query = getCurrentSession().createSQLQuery(sqlQuery).addEntity(Actors.class);
List<Actors> list = (List<Actors>) query.list();
return list;
Parth Solanki
sumber
-1

Yang Anda butuhkan hanyalah DTO dengan 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];
    }

dan menyebutnya:

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());
Rubens
sumber
Tambahkan kolom baru dan kode akan rusak.
Hidangan
-2

Gunakan DTO Design Pattern. Itu digunakan di EJB 2.0. Entitas dikelola wadah. DTO Design Patterndigunakan untuk mengatasi masalah ini. Tapi, itu mungkin digunakan sekarang, ketika aplikasi dikembangkan Server Sidedan Client Sideterpisah. DTOdigunakan ketika Server sidetidak ingin meneruskan / kembali Entitydengan anotasi ke Client Side.

Contoh 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 <- itu memang perlu

public class EntityBuilder() {
    public static PersonEntity buildPersonEntity(PersonDTO person) {
        return new PersonEntity(person.getId(). person.getAddress());
    }
}
Zaw Than oo
sumber
4
Terima kasih atas jawabannya. Di sini saya tidak perlu pola DTO. Persyaratan saya adalah tidak menyembunyikan detail penjelasan dari klien. Jadi saya tidak perlu membuat satu POJO lagi di aplikasi saya. Persyaratan saya adalah untuk melemparkan set hasil ke qa pojo yang bukan entitas JAVA tetapi kelas POJO sederhana yang memiliki bidang yang sama dengan kolom set hasil.
Gunjan Shah