JPA:如何将本机查询结果集转换为 POJO 类集合

新手上路,请多包涵

我在我的项目中使用 JPA。

我来到一个查询,我需要在五个表上进行连接操作。所以我创建了一个返回五个字段的本机查询。

现在我想将结果对象转换为包含相同五个字符串的 java POJO 类。

JPA 中有什么方法可以直接将该结果转换为 POJO 对象列表?

我来到以下解决方案..

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

现在在 resultClass 中,我们是否需要提供一个实际的 JPA 实体类?或者我们可以将它转换为任何包含相同列名的 JAVA POJO 类吗?

原文由 Gunjan Shah 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 792
2 个回答

JPA 提供了一个 SqlResultSetMapping 允许您将本机查询的任何返回映射到实体或自定义类.

编辑 JPA 1.0 不允许映射到非实体类。只有在 JPA 2.1 中,才添加了 ConstructorResult 以将返回值映射到 java 类。

此外,对于 OP 的计数问题,使用单个 ColumnResult 定义结果集映射就足够了

原文由 Denis Tulskiy 发布,翻译遵循 CC BY-SA 3.0 许可协议

我已经找到了几个解决方案。

使用映射实体 (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,最后两个是理想的方式。

原文由 Edwin Dalorzo 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题