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 Jedi
tidak perlu menjadi entitas yang dipetakan. Ini bisa menjadi POJO biasa.
Menggunakan Pemetaan XML
Saya adalah salah satu dari mereka yang menemukan menambahkan semua ini @SqlResultSetMapping
cukup invasif di entitas saya, dan saya sangat tidak suka definisi nama query dalam entitas, jadi saya melakukan semua ini dalam META-INF/orm.xml
file:
<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.