我在我的项目中使用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类?


当前回答

如果你使用Spring,你可以使用org.springframework.jdbc.core.RowMapper

这里有一个例子:

public List query(String objectType, String namedQuery)
{
  String rowMapper = objectType + "RowMapper";
  // then by reflection you can instantiate and use. The RowMapper classes need to follow the naming specific convention to follow such implementation.
} 

其他回答

如果您使用Spring-jpa,这是对答案和这个问题的补充。如有任何瑕疵,请改正。根据我的实际需求,我主要使用了三种方法来实现“map result Object[] to a pojo”:

JPA内置方法就足够了。 JPA内置的方法是不够的,但一个自定义的sql与它的实体是足够的。 前2失败,我必须使用一个nativeQuery。 下面是一些例子。 pojo期待着: 公共类 private String; private Integer successRate; // getter和setter public Antistealingdto(String secretKey, Integer successRate) { 这一点。secretKey = secretKey; 这一点。successRate =成功率; } }

方法1:将pojo更改为接口:

public interface Antistealingdto {
    String getSecretKey();
    Integer getSuccessRate();
}

和存储库:

interface AntiStealingRepository extends CrudRepository<Antistealing, Long> {
    Antistealingdto findById(Long id);
}

方法2: 存储库:

@Query("select new AntistealingDTO(secretKey, successRate) from Antistealing where ....")
Antistealing whatevernamehere(conditions);

注意:POJO构造函数的参数顺序在POJO定义和sql中必须相同。

方法3: 在Edwin Dalorzo的回答中,以实体中的@SqlResultSetMapping和@NamedNativeQuery为例。

前两个方法将调用许多中间处理程序,如自定义转换器。例如,AntiStealing定义了一个secretKey,在它被持久化之前,会插入一个转换器来加密它。这将导致前2个方法返回一个转换后的返回secretKey,这不是我想要的。而方法3将克服转换器,返回的secretKey将与存储时相同(加密的)。

可以执行展开过程,将结果分配给非实体(即Beans/POJO)。操作步骤如下。

List<JobDTO> dtoList = entityManager.createNativeQuery(sql)
        .setParameter("userId", userId)
        .unwrap(org.hibernate.Query.class).setResultTransformer(Transformers.aliasToBean(JobDTO.class)).list();

该用法用于JPA-Hibernate实现。

我已经找到了一些解决方案。

使用映射实体(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();

但在这种情况下,绝地,必须是一个映射的实体职业。

避免此处未检查警告的另一种方法是使用命名本机查询。如果我们在实体中声明本机查询

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

JPA 2.1与@SqlResultSetMapping

随着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();

当然,在这种情况下,绝地不需要是一个映射实体。它可以是一个普通的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,最后两个是理想的方法。

我们通过以下方式解决了这个问题:

   //Add actual table name here in Query
    final String sqlQuery = "Select a.* from ACTORS a"
    // add your entity manager here 
    Query query = entityManager.createNativeQuery(sqlQuery,Actors.class);
    //List contains the mapped entity data.
    List<Actors> list = (List<Actors>) query.getResultList();

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

EDIT JPA 1.0不允许映射到非实体类。只有在JPA 2.1中,才添加了一个ConstructorResult来映射java类的返回值。

同样,对于OP获取计数的问题,定义一个带有单个ColumnResult的结果集映射就足够了