为了账号安全,请及时绑定邮箱和手机立即绑定

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

/ 猿问

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

海绵宝宝撒 2019-07-22 15:48:30

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

我在我的项目中使用JPA。

我来到了一个查询,在这个查询中,我需要对五个表进行联接操作。因此,我创建了一个本地查询,它返回五个字段。

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

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

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


查看完整描述

3 回答

?
明月笑刀无情

JPA提供了一个SqlResultSetMapping,这使您可以将来自本机查询的任何返回映射到实体中。

JPA1.0不允许映射到非实体类。仅在JPA 2.1 a中建筑成果已添加以映射java类的返回值。

另外,对于OP获取计数的问题,应该可以用一个单独的结果集映射来定义结果集映射。ColumnResult


查看完整回答
反对 回复 2019-07-22
?
一只甜甜圈

我找到了几个解决办法。

使用映射实体(JPA2.0)

使用JPA2.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();

这是比较安全的,但我们仍然被限制使用映射的实体。

人工映射

我试验过的一个解决方案(在JPA2.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);

带有@SqlResultSetmap的JPA2.1

随着JPA2.1的到来,我们可以使用@SqlResultSetmap注释来解决这个问题。

我们需要声明实体中某个地方的结果集映射:

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

这些都是我知道的解决办法。如果我们可以使用JPA2.1,最后两种方法是理想的。


查看完整回答
反对 回复 2019-07-22
?
眼眸繁星

是的,使用JPA2.1很容易。您有非常有用的注释。它们简化了你的生活。

首先声明原生查询,然后声明结果集映射(它定义了数据库返回到POJO的数据的映射)。编写要引用的POJO类(此处不包括)。最后但并非最不重要的一点是:在DAO中创建一个方法来调用查询。这在一个下拉向导(1.0.0)应用程序中适用于我。

首先在实体类中声明本机查询:

@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

在下面可以添加结果集映射声明:

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

在DAO的后面,您可以将查询引用为

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

就这样。


查看完整回答
反对 回复 2019-07-22

添加回答

回复

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信