Bunun için birkaç çözüm buldum.
Eşlenen Varlıkları Kullanma (JPA 2.0)
JPA 2.0 kullanarak, yerel bir sorguyu POJO ile eşlemek mümkün değildir, yalnızca bir varlık ile yapılabilir.
Örneğin:
Query query = em.createNativeQuery("SELECT name,age FROM jedi_table", Jedi.class);
@SuppressWarnings("unchecked")
List<Jedi> items = (List<Jedi>) query.getResultList();
Ancak bu durumda, Jedi
eşlenmiş bir varlık sınıfı olmalıdır.
Burada işaretlenmeyen uyarıyı önlemenin bir alternatifi, adlandırılmış bir yerel sorgu kullanmak olabilir. Dolayısıyla, bir varlıktaki yerel sorguyu bildirirsek
@NamedNativeQuery(
name="jedisQry",
query = "SELECT name,age FROM jedis_table",
resultClass = Jedi.class)
Ardından, şunları yapabiliriz:
TypedQuery<Jedi> query = em.createNamedQuery("jedisQry", Jedi.class);
List<Jedi> items = query.getResultList();
Bu daha güvenlidir, ancak yine de eşlenmiş bir varlığı kullanmakla sınırlıdır.
Manuel Haritalama
Biraz denediğim bir çözüm (JPA 2.1 gelmeden önce) biraz yansıma kullanarak bir POJO yapıcısına eşleme yapıyordu.
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);
}
}
Bu yöntem temel olarak bir tuple dizisi alır (yerel sorgular tarafından döndürüldüğü gibi) ve aynı sayıda alana ve aynı tipte bir yapıcı arayarak bunu sağlanan bir POJO sınıfıyla eşleştirir.
O zaman aşağıdaki gibi uygun yöntemleri kullanabiliriz:
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);
}
Ve bu tekniği aşağıdaki gibi kullanabiliriz:
Query query = em.createNativeQuery("SELECT name,age FROM jedis_table");
List<Jedi> jedis = getResultList(query, Jedi.class);
@SqlResultSetMapping ile JPA 2.1
JPA 2.1'in gelişiyle, sorunu çözmek için @SqlResultSetMapping ek açıklamasını kullanabiliriz.
Bir varlığın herhangi bir yerinde bir sonuç kümesi eşlemesi bildirmemiz gerekir:
@SqlResultSetMapping(name="JediResult", classes = {
@ConstructorResult(targetClass = Jedi.class,
columns = {@ColumnResult(name="name"), @ColumnResult(name="age")})
})
Ve sonra basitçe şunu yaparız:
Query query = em.createNativeQuery("SELECT name,age FROM jedis_table", "JediResult");
@SuppressWarnings("unchecked")
List<Jedi> samples = query.getResultList();
Tabii ki, bu durumda Jedi
eşlenmiş bir varlık olması gerekmez. Düzenli bir POJO olabilir.
XML Eşlemeyi Kullanma
Varlıklarımdaki tüm bu @SqlResultSetMapping
oldukça invaziv eklemeyi bulanlardan biriyim ve özellikle varlıklar içindeki adlandırılmış sorguların tanımını beğenmedim, bu yüzden alternatif olarak tüm bunları META-INF/orm.xml
dosyada yapıyorum :
<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>
Ve bunların hepsi bildiğim çözümler. Son iki, JPA 2.1'i kullanabilmemiz için ideal yoldur.