我找到了几个解决方案。
使用映射实体 (JPA 2.0)
使用 JPA 2.0 无法将本机查询映射到 POJO,只能使用实体来完成。
例如:
Query query = em.createNativeQuery("SELECT name,age FROM jedi_table", Jedi.class);
@SuppressWarnings("unchecked")
List<Jedi> items = (List<Jedi>) query.getResultList();
但在这种情况下,Jedi
, 必须是映射的实体类。
在这里避免未经检查的警告的另一种方法是使用命名的本机查询。因此,如果我们在实体中声明本机查询
@NamedNativeQuery(
name="jedisQry",
query = "SELECT name,age FROM jedis_table",
resultClass = Jedi.class)
然后,我们可以简单地做:
TypedQuery<Jedi> query = em.createNamedQuery("jedisQry", Jedi.class);
List<Jedi> items = query.getResultList();
这更安全,但我们仍然被限制使用映射实体。
手动映射
我尝试了一下(在 JPA 2.1 到来之前)的一个解决方案是使用一点反射对 POJO 构造函数进行映射。
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);
}
}
此方法基本上采用元组数组(由本机查询返回)并通过查找具有相同数量的字段和相同类型的构造函数将其映射到提供的 POJO 类。
然后我们可以使用方便的方法,例如:
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);
}
我们可以简单地使用这种技术,如下所示:
Query query = em.createNativeQuery("SELECT name,age FROM jedis_table");
List<Jedi> jedis = getResultList(query, Jedi.class);
带有@SqlResultSetMapping 的 JPA 2.1
随着 JPA 2.1 的到来,我们可以使用 @SqlResultSetMapping 注解来解决这个问题。
我们需要在实体的某处声明一个结果集映射:
@SqlResultSetMapping(name="JediResult", classes = {
@ConstructorResult(targetClass = Jedi.class,
columns = {@ColumnResult(name="name"), @ColumnResult(name="age")})
})
然后我们简单地做:
Query query = em.createNativeQuery("SELECT name,age FROM jedis_table", "JediResult");
@SuppressWarnings("unchecked")
List<Jedi> samples = query.getResultList();
当然,在这种情况下Jedi
不必是映射实体。它可以是一个普通的 POJO。
使用 XML 映射
我是那些发现@SqlResultSetMapping
在我的实体中添加所有这些非常具有侵入性的人之一,我特别不喜欢实体中命名查询的定义,所以或者我在META-INF/orm.xml
文件中执行所有这些操作:
<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>
这些就是我所知道的所有解决方案。如果我们可以使用 JPA 2.1,最后两个是理想的方式。