我在我的项目中使用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
})
Run Code Online (Sandbox Code Playgroud)
现在在resultClass中,我们是否需要提供一个实际的JPA实体类?或者我们可以将它转换为包含相同列名的任何JAVA POJO类吗?
Edw*_*rzo 190
我找到了几个解决方案.
使用映射实体(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();
Run Code Online (Sandbox Code Playgroud)
但在这种情况下,Jedi
必须是映射的实体类.
在此处避免未经检查的警告的替代方法是使用命名的本机查询.因此,如果我们在实体中声明本机查询
@NamedNativeQuery(
name="jedisQry",
query = "SELECT name,age FROM jedis_table",
resultClass = Jedi.class)
Run Code Online (Sandbox Code Playgroud)
然后,我们可以简单地做:
TypedQuery<Jedi> query = em.createNamedQuery("jedisQry", Jedi.class);
List<Jedi> items = query.getResultList();
Run Code Online (Sandbox Code Playgroud)
这更安全,但我们仍然限制使用映射实体.
手动映射
我尝试了一些解决方案(在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);
}
}
Run Code Online (Sandbox Code Playgroud)
此方法基本上采用元组数组(由本机查询返回),并通过查找具有相同字段数和相同类型的构造函数将其映射到提供的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);
}
Run Code Online (Sandbox Code Playgroud)
我们可以简单地使用以下技术:
Query query = em.createNativeQuery("SELECT name,age FROM jedis_table");
List<Jedi> jedis = getResultList(query, Jedi.class);
Run Code Online (Sandbox Code Playgroud)
带有@SqlResultSetMapping的JPA 2.1
随着JPA 2.1的到来,我们可以使用@SqlResultSetMapping注释来解决问题.
我们需要在实体的某处声明一个结果集映射:
@SqlResultSetMapping(name="JediResult", classes = {
@ConstructorResult(targetClass = Jedi.class,
columns = {@ColumnResult(name="name"), @ColumnResult(name="age")})
})
Run Code Online (Sandbox Code Playgroud)
然后我们简单地做:
Query query = em.createNativeQuery("SELECT name,age FROM jedis_table", "JediResult");
@SuppressWarnings("unchecked")
List<Jedi> samples = query.getResultList();
Run Code Online (Sandbox Code Playgroud)
当然,在这种情况下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>
Run Code Online (Sandbox Code Playgroud)
这些都是我所知道的解决方案.如果我们可以使用JPA 2.1,最后两个是理想的方式.
Den*_*kiy 92
JPA提供了一个 SqlResultSetMapping
允许您将本机查询的任何返回映射到实体的内容或自定义类.
EDIT JPA 1.0不允许映射到非实体类.仅在JPA 2.1中添加了一个ConstructorResult来将返回值映射到java类.
另外,对于OP的计数问题,应该足以用单个定义结果集映射 ColumnResult
小智 11
是的,使用JPA 2.1很容易.你有非常有用的注释.它们简化了你的生活.
首先声明您的本机查询,然后是结果集映射(定义数据库返回到POJO的数据的映射).写下你的POJO课程(为简洁起见,不包括在内).最后但并非最不重要的:在DAO中创建一个方法(例如)来调用查询.这对我来说是一个dropwizard(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
Run Code Online (Sandbox Code Playgroud)
在下面,您可以添加结果集映射声明:
@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)
}
)
}
)
Run Code Online (Sandbox Code Playgroud)
稍后在DAO中,您可以将查询称为
public List<domain.io.MyMapping> findAll() {
return (namedQuery("domain.io.MyClass.myQuery").list());
}
Run Code Online (Sandbox Code Playgroud)
而已.
如果你使用Spring-jpa
,这是答案和这个问题的补充.如果有任何缺陷,请更正此问题.Object[]
基于我遇到的实际需要,我主要使用三种方法来实现"将结果映射到pojo":
sql
用它定制Entity
就足够了.前者2失败了,我必须使用一个nativeQuery
.以下是示例.pojo期望:
public class Antistealingdto {
private String secretKey;
private Integer successRate;
// GETTERs AND SETTERs
public Antistealingdto(String secretKey, Integer successRate) {
this.secretKey = secretKey;
this.successRate = successRate;
}
}
Run Code Online (Sandbox Code Playgroud)方法1:将pojo更改为接口:
public interface Antistealingdto {
String getSecretKey();
Integer getSuccessRate();
}
Run Code Online (Sandbox Code Playgroud)
和存储库:
interface AntiStealingRepository extends CrudRepository<Antistealing, Long> {
Antistealingdto findById(Long id);
}
Run Code Online (Sandbox Code Playgroud)
方法2:存储库:
@Query("select new AntistealingDTO(secretKey, successRate) from Antistealing where ....")
Antistealing whatevernamehere(conditions);
Run Code Online (Sandbox Code Playgroud)
注意:POJO构造函数的参数序列在POJO定义和sql中必须相同.
方法3:使用@SqlResultSetMapping
并@NamedNativeQuery
在Entity
在埃德温Dalorzo的答案的例子.
前两种方法会调用许多中间处理程序,如自定义转换器.例如,在持久化之前AntiStealing
定义a secretKey
,插入转换器以对其进行加密.这将导致前两个方法返回转换后退secretKey
,这不是我想要的.虽然方法3将克服转换器,并且返回secretKey
将与存储的相同(加密的).
最简单的方法是使用 so投影。可以将查询结果直接映射到接口上,比使用SqlResultSetMapping更容易实现。
一个例子如下所示:
@Repository
public interface PeopleRepository extends JpaRepository<People, Long> {
@Query(value = "SELECT p.name AS name, COUNT(dp.people_id) AS count " +
"FROM people p INNER JOIN dream_people dp " +
"ON p.id = dp.people_id " +
"WHERE p.user_id = :userId " +
"GROUP BY dp.people_id " +
"ORDER BY p.name", nativeQuery = true)
List<PeopleDTO> findByPeopleAndCountByUserId(@Param("userId") Long userId);
@Query(value = "SELECT p.name AS name, COUNT(dp.people_id) AS count " +
"FROM people p INNER JOIN dream_people dp " +
"ON p.id = dp.people_id " +
"WHERE p.user_id = :userId " +
"GROUP BY dp.people_id " +
"ORDER BY p.name", nativeQuery = true)
Page<PeopleDTO> findByPeopleAndCountByUserId(@Param("userId") Long userId, Pageable pageable);
}
// Interface to which result is projected
public interface PeopleDTO {
String getName();
Long getCount();
}
Run Code Online (Sandbox Code Playgroud)
来自投影接口的字段必须与该实体中的字段匹配。否则字段映射可能会中断。
此外,如果您使用SELECT table.column
表示法,请始终定义与实体名称匹配的别名,如示例所示。
可以执行解包过程以将结果分配给非实体(即Beans / POJO)。步骤如下。
List<JobDTO> dtoList = entityManager.createNativeQuery(sql)
.setParameter("userId", userId)
.unwrap(org.hibernate.Query.class).setResultTransformer(Transformers.aliasToBean(JobDTO.class)).list();
Run Code Online (Sandbox Code Playgroud)
该用法用于JPA-Hibernate实现。
小智 5
在休眠中,您可以使用此代码轻松映射您的本机查询。
private List < Map < String, Object >> getNativeQueryResultInMap() {
String mapQueryStr = "SELECT * FROM AB_SERVICE three ";
Query query = em.createNativeQuery(mapQueryStr);
NativeQueryImpl nativeQuery = (NativeQueryImpl) query;
nativeQuery.setResultTransformer(AliasToEntityMapResultTransformer.INSTANCE);
List < Map < String, Object >> result = query.getResultList();
for (Map map: result) {
System.out.println("after request ::: " + map);
}
return result;}
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
319195 次 |
最近记录: |