Java 如何使用 SqlResultSetMapping 将 JPA NativeQuery 的结果集映射到 POJO
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/25188939/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to map the result set of a JPA NativeQuery to a POJO using SqlResultSetMapping
提问by mhlandry
I am attempting to map the results of a Native query to a POJO using @SqlResultSetMapping with @ConstructorResult. Here is my code:
我正在尝试使用 @SqlResultSetMapping 和 @ConstructorResult 将本机查询的结果映射到 POJO。这是我的代码:
@SqlResultSetMapping(name="foo",
classes = {
@ConstructorResult(
targetClass = Bar.class,
columns = {
@ColumnResult(name = "barId", type = Long.class),
@ColumnResult(name = "barName", type = String.class),
@ColumnResult(name = "barTotal", type = Long.class)
})
})
public class Bar {
private Long barId;
private String barName;
private Long barTotal;
...
And then in my DAO:
然后在我的 DAO 中:
Query query = em.createNativeQueryBar(QUERY, "foo");
... set some parameters ...
List<Bar> list = (List<Bar>) query.getResultList();
I have read that this functionality is only supported in JPA 2.1, but that is what I am using. Here's my dependency:
我已经读到此功能仅在 JPA 2.1 中受支持,但这就是我正在使用的。这是我的依赖:
<dependency>
<groupId>org.hibernate.javax.persistence</groupId>
<artifactId>hibernate-jpa-2.1-api</artifactId>
<version>1.0.0.Final</version>
</dependency>
I found a couple of resources, including this one: @ConstructorResult mapping in jpa 2.1. But I am still not having any luck.
我找到了一些资源,包括这个:@ConstructorResult mapping in jpa 2.1。但我仍然没有任何运气。
What am I missing? Why can't the SqlResultSetMapping be found?
我错过了什么?为什么找不到SqlResultSetMapping?
javax.persistence.PersistenceException: org.hibernate.MappingException: Unknown SqlResultSetMapping [foo]
采纳答案by zbig
@SqlResultSetMapping
annotation should not be put on a POJO. Put it at (any) @Entity
class. "Unknown SqlResultSetMapping [foo]" tells you, that JPA provider doesn't see any mapping under name 'foo'. Please see another answer of mine for the correct example
@SqlResultSetMapping
注释不应放在 POJO 上。把它放在(任何)@Entity
班级。“Unknown SqlResultSetMapping [foo]”告诉您,JPA 提供程序在名称“foo”下看不到任何映射。请参阅我的另一个答案以获取正确示例
回答by Emerson Moretto
I able to do it this way:
我可以这样做:
Session session = em().unwrap(Session.class);
SQLQuery q = session.createSQLQuery("YOUR SQL HERE");
q.setResultTransformer( Transformers.aliasToBean( MyNotMappedPojoClassHere.class) );
List<MyNotMappedPojoClassHere> postList = q.list();
回答by kinjelom
Short working example:
简短的工作示例:
DTO POJO class
@lombok.Getter @lombok.AllArgsConstructor public class StatementDto { private String authorName; private Date createTime; }
Repository bean:
@Repository public class StatementNativeRepository { @PersistenceContext private EntityManager em; static final String STATEMENT_SQLMAP = "Statement-SQL-Mapping"; public List<StatementDto> findPipelinedStatements() { Query query = em.createNativeQuery( "select author_name, create_time from TABLE(SomePipelinedFun('xxx'))", STATEMENT_SQLMAP); return query.getResultList(); } @SqlResultSetMapping(name= STATEMENT_SQLMAP, classes = { @ConstructorResult(targetClass = StatementDto.class, columns = { @ColumnResult(name="author_name",type = String.class), @ColumnResult(name="create_time",type = Date.class) } ) }) @Entity class SQLMappingCfgEntity{@Id int id;} // <- workaround }
DTO POJO 类
@lombok.Getter @lombok.AllArgsConstructor public class StatementDto { private String authorName; private Date createTime; }
存储库bean:
@Repository public class StatementNativeRepository { @PersistenceContext private EntityManager em; static final String STATEMENT_SQLMAP = "Statement-SQL-Mapping"; public List<StatementDto> findPipelinedStatements() { Query query = em.createNativeQuery( "select author_name, create_time from TABLE(SomePipelinedFun('xxx'))", STATEMENT_SQLMAP); return query.getResultList(); } @SqlResultSetMapping(name= STATEMENT_SQLMAP, classes = { @ConstructorResult(targetClass = StatementDto.class, columns = { @ColumnResult(name="author_name",type = String.class), @ColumnResult(name="create_time",type = Date.class) } ) }) @Entity class SQLMappingCfgEntity{@Id int id;} // <- workaround }
回答by benito
@Entity
@SqlResultSetMapping(name="ConnexionQueryBean",
entities={
@EntityResult(entityClass=com.collecteJ.business.bean.ConnexionQueryBean.class, fields={
@FieldResult(name="utilisateurId", column="UTILISATEUR_ID"),
@FieldResult(name="nom", column="NOM"),
@FieldResult(name="prenom", column="PRENOM"),
@FieldResult(name="nomConnexion", column="NOM_CONNEXION"),
@FieldResult(name="codeAgence", column="CODE_AGENCE"),
@FieldResult(name="codeBanque", column="CODE_BANQUE"),
@FieldResult(name="codeDevise", column="CODE_DEVISE"),
@FieldResult(name="codeCollecteur", column="CODE_COLLECTEUR")})
})
public class ConnexionQueryBean implements Serializable {
@Id
private long utilisateurId;
private String codeCollecteur;
private String nom;
private String prenom;
private String nomConnexion;
private String codeAgence;
private String codeBanque;
private String codeDevise;
public ConnexionQueryBean() {
}
public long getUtilisateurId() {
return utilisateurId;
}
public void setUtilisateurId(long utilisateurId) {
this.utilisateurId = utilisateurId;
}
public String getCodeCollecteur() {
return codeCollecteur;
}
public void setCodeCollecteur(String codeCollecteur) {
this.codeCollecteur = codeCollecteur;
}
public String getNom() {
return nom;
}
public void setNom(String nom) {
this.nom = nom;
}
public String getPrenom() {
return prenom;
}
public void setPrenom(String prenom) {
this.prenom = prenom;
}
public String getNomConnexion() {
return nomConnexion;
}
public void setNomConnexion(String nomConnexion) {
this.nomConnexion = nomConnexion;
}
public String getCodeAgence() {
return codeAgence;
}
public void setCodeAgence(String codeAgence) {
this.codeAgence = codeAgence;
}
public String getCodeBanque() {
return codeBanque;
}
public void setCodeBanque(String codeBanque) {
this.codeBanque = codeBanque;
}
public String getCodeDevise() {
return codeDevise;
}
public void setCodeDevise(String codeDevise) {
this.codeDevise = codeDevise;
}
@Override
public String toString() {
return "ConnexionQueryBean{" + "utilisateurId=" + utilisateurId + ", codeCollecteur=" + codeCollecteur + ", nom=" + nom + ", prenom=" + prenom + ", nomConnexion=" + nomConnexion + ", codeAgence=" + codeAgence + ", codeBanque=" + codeBanque + ", codeDevise=" + codeDevise + '}';
}
This is not really an entity as it does not match any database table. But the @Entity
and the @Id
annotations are compulsories for JPA to understand the mapping. If you don't really want to have @Entity / @Id
in that class, you can remove the @SqlResultSetMapping
annotation and put it in any other entity as far as JPA can scan it.
这不是真正的实体,因为它不匹配任何数据库表。但是@Entity
和@Id
注释是 JPA 理解映射所必需的。如果你真的不想@Entity / @Id
在那个类中,你可以删除 @SqlResultSetMapping
注释并将它放在任何其他实体中,只要 JPA 可以扫描它。
You should also make sure that your @ComponentScan
content the corresponding package, if you are using a java based spring configuration, you should explicitly declare your entity in the persistence.xml/orm.xml
under the META-INF
directory.
你还应该确保你的@ComponentScan
内容对应的包,如果你使用的是基于java的spring配置,你应该persistence.xml/orm.xml
在META-INF
目录下显式声明你的实体。
This is the call
这是电话
String connexionQuery = "SELECT u.UTILISATEUR_ID, u.NOM, u.PRENOM, u.NOM_CONNEXION, a.CODE_AGENCE, a.CODE_BANQUE, a.CODE_DEVISE, c.CODE_COLLECTEUR FROM UTILISATEUR u, AGENCE a, COLLECTEUR c "
+ " WHERE (a.CODE_AGENCE = c.CODE_AGENCE AND u.UTILISATEUR_ID = c.UTILISATEUR_ID AND u.NOM_CONNEXION = '"+nomConnextion+"')";
ConnexionQueryBean ConnexionResults = (ConnexionQueryBean) defaultService.getEntityManager().createNativeQuery(connexionQuery,"ConnexionQueryBean").getSingleResult();
System.out.println(ConnexionResults.toString());
I'm using Spring, JPA 2.1, Hibernate 5 and Oracle, i think this might not be possible with JPA lower version, find more http://www.thoughts-on-java.org/result-set-mapping-complex-mappings/
我正在使用 Spring、JPA 2.1、Hibernate 5 和 Oracle,我认为 JPA 较低版本可能无法实现,找到更多http://www.thoughts-on-java.org/result-set-mapping-complex-映射/
回答by Simon Martinelli
QLRM could be a alternative: http://simasch.github.io/qlrm/
QLRM 可能是另一种选择:http://simasch.github.io/qlrm/
It is not related to a specific JPA implementation and also works with JDBC.
它与特定的 JPA 实现无关,也适用于 JDBC。
回答by Artanis Zeratul
I have a slightly varied answer which is just derived from wildloop's answer.
Here is my answer:
我有一个稍微不同的答案,它只是从 Wildloop 的答案中得出的。
这是我的回答:
Constants class: Constants.java
常量类:Constants.java
public class Constants {
public final String TESTQUERYRESULT_MAPPING_NAME = "TestQueryResultMapping";
}
Result Mapping Class: TestQueryResult.java
结果映射类:TestQueryResult.java
import lombok.Getter;
import lombok.Setter;
import javax.persistence.Entity;
import javax.persistence.EntityResult;
import javax.persistence.FieldResult;
import javax.persistence.Id;
import javax.persistence.SqlResultSetMapping;
@Getter
@Setter
@SqlResultSetMapping(
//name = "TestQueryResultMapping"
name = Constants.TESTQUERYRESULT_MAPPING_NAME
,entities = @EntityResult(
entityClass = TestQueryResult.class
,fields = {
@FieldResult(name = "rowId", column = "row_id")
,@FieldResult(name = "rowName", column = "row_name")
,@FieldResult(name = "value", column = "row_value")
}
)
)
@Entity
public class TestQueryResult {
@Id
private Integer rowId;
private String rowName;
private String value;
}
Then... somewhere in my Repository Implementation code:
然后......在我的存储库实现代码中的某处:
public class TestQueryRepository {
//... some code here to get the entity manager here
public TestQueryResult getTopMost(Integer rowName) {
//... some code here
String queryString = "... some query string here" + rowName;
TestQueryResult testQueryResult = null;
//this.entityManager.createNativeQuery(queryString ,"TestQueryResultMapping").getResultList();
List<TestQueryResult> results = this.entityManager.createNativeQuery(queryString ,Constants.TESTQUERYRESULT_MAPPING_NAME).getResultList();
if (results != null && !results.isEmpty()) {
testQueryResult = results.get(0);
}
return testQueryResult;
}
}
... then violah! I got some results :D!
……那么薇奥拉!我得到了一些结果:D!
Cheers,
Artanis Zeratul
干杯,
阿塔尼斯·泽拉图
回答by Vlad Palnik
The problem with adding @Entity
to your DTO POJO is that it will create a table in your db that you don't need. Having to add @Id
and a transient keyword to necessary fields is also a hassle. A simple solution is to move your @SqlResultSetMapping
to an abstract class.
添加@Entity
到您的 DTO POJO的问题在于它会在您的数据库中创建一个您不需要的表。必须在@Id
必要的字段中添加一个临时关键字也很麻烦。一个简单的解决方案是将您移动@SqlResultSetMapping
到抽象类。
@MappedSuperclass
@SqlResultSetMapping(name="foo",
classes = {
@ConstructorResult(
targetClass = Bar.class,
columns = {
@ColumnResult(name = "barId", type = Long.class),
@ColumnResult(name = "barName", type = String.class),
@ColumnResult(name = "barTotal", type = Long.class)
})
})
public abstract class sqlMappingCode {}
Don't forget to add @MappedSuperclass
. This will ensure Hibernate auto-wires your mapping.
不要忘记添加@MappedSuperclass
. 这将确保 Hibernate 自动连接您的映射。
回答by dinesh salve
This solution is independent of JPA implementation. Once you collect result of native query as
此解决方案独立于 JPA 实现。一旦您将本机查询的结果收集为
List<Object[]> = em.createNativeQueryBar(QUERY, "foo").getResultList();
and if you need to map each List element to a Person e.g.
如果您需要将每个 List 元素映射到一个 Person 例如
Class Person { String name; Int age; }
where List element[0] is name and element [ 1] is age.
其中列表元素[0] 是姓名,元素[1] 是年龄。
You can convert Object[] to Person using ObjectMapper. You need to add @JsonFormat annotation on the class.
您可以使用ObjectMapper将 Object[] 转换为 Person 。您需要在类上添加 @JsonFormat 注释。
@JsonFormat(shape = JsonFormat.Shape.ARRAY)
Class Person { String name; Int age; }
and convert Object[] to Person as
并将 Object[] 转换为 Person 作为
ObjectMapper mapper = new ObjectMapper();
JSONArray jsonArray = new JSONArray(Arrays.asList(attributes)).toString();
Person person = mapper.readValue(jsonArray, Person.class);
回答by Vlad Mihalcea
This is a very common question, so this answer is based on this articleI wrote on my blog.
这是一个很常见的问题,所以这个答案是基于我在博客上写的这篇文章。
Domain Model
领域模型
Let's consider we have the following post
and post_comment
tables in our database:
让我们考虑一下我们的数据库中有以下内容post
和post_comment
表格:
JPA SqlResultSetMapping
日本特许经营协会 SqlResultSetMapping
The SqlResultSetMapping
JPA annotation looks as follows:
在SqlResultSetMapping
JPA注解如下所示:
@Repeatable(SqlResultSetMappings.class)
@Target({TYPE})
@Retention(RUNTIME)
public @interface SqlResultSetMapping {
String name();
EntityResult[] entities() default {};
ConstructorResult[] classes() default {};
ColumnResult[] columns() default {};
}
The SqlResultSetMapping
annotation is repeatable and is applied at the entity class level. Apart from taking a unique name, which is used by Hibernate to register the mapping, there are three mapping options:
该SqlResultSetMapping
注释是可重复的,并在实体类一级应用。除了采用 Hibernate 用来注册映射的唯一名称之外,还有三种映射选项:
EntityResult
ConstructorResult
ColumnResult
EntityResult
ConstructorResult
ColumnResult
Next, we are going to see how al these three mapping options work, as well as the use cases where you will need to use them.
接下来,我们将了解这三个映射选项的工作原理,以及您需要使用它们的用例。
JPA SqlResultSetMapping - EntityResult
JPA SqlResultSetMapping - EntityResult
The EntityResult
option allows you to map the JDBC ResultSet
columns to one or more JPA entities.
该EntityResult
选项允许您将 JDBCResultSet
列映射到一个或多个 JPA 实体。
Let's assume we want to fetch the first 5 Post
entities along with all their associated PostComment
entities that match a given title
pattern.
假设我们要获取前 5 个Post
实体以及它们与PostComment
给定title
模式匹配的所有关联实体。
As I explained in this article, we can use the DENSE_RANK
SQL Window Functionto know how to filter the post
and post_comment
joined records, as illustrated by the following SQL query:
正如我在本文中解释的,我们可以使用DENSE_RANK
SQL 窗口函数来了解如何过滤post
和post_comment
连接的记录,如下面的 SQL 查询所示:
SELECT *
FROM (
SELECT
*,
DENSE_RANK() OVER (
ORDER BY
"p.created_on",
"p.id"
) rank
FROM (
SELECT
p.id AS "p.id", p.created_on AS "p.created_on",
p.title AS "p.title", pc.post_id AS "pc.post_id",
pc.id as "pc.id", pc.created_on AS "pc.created_on",
pc.review AS "pc.review"
FROM post p
LEFT JOIN post_comment pc ON p.id = pc.post_id
WHERE p.title LIKE :titlePattern
ORDER BY p.created_on
) p_pc
) p_pc_r
WHERE p_pc_r.rank <= :rank
However, we don't want to return a list of scalar column values. We want to return JPA entities from this query, so we need to configure the entities
attribute of the @SqlResultSetMapping
annotation, like this:
但是,我们不想返回标量列值的列表。我们想从这个查询中返回 JPA 实体,所以我们需要配置注解的entities
属性@SqlResultSetMapping
,像这样:
@NamedNativeQuery(
name = "PostWithCommentByRank",
query = """
SELECT *
FROM (
SELECT
*,
DENSE_RANK() OVER (
ORDER BY
"p.created_on",
"p.id"
) rank
FROM (
SELECT
p.id AS "p.id", p.created_on AS "p.created_on",
p.title AS "p.title", pc.post_id AS "pc.post_id",
pc.id as "pc.id", pc.created_on AS "pc.created_on",
pc.review AS "pc.review"
FROM post p
LEFT JOIN post_comment pc ON p.id = pc.post_id
WHERE p.title LIKE :titlePattern
ORDER BY p.created_on
) p_pc
) p_pc_r
WHERE p_pc_r.rank <= :rank
""",
resultSetMapping = "PostWithCommentByRankMapping"
)
@SqlResultSetMapping(
name = "PostWithCommentByRankMapping",
entities = {
@EntityResult(
entityClass = Post.class,
fields = {
@FieldResult(name = "id", column = "p.id"),
@FieldResult(name = "createdOn", column = "p.created_on"),
@FieldResult(name = "title", column = "p.title"),
}
),
@EntityResult(
entityClass = PostComment.class,
fields = {
@FieldResult(name = "id", column = "pc.id"),
@FieldResult(name = "createdOn", column = "pc.created_on"),
@FieldResult(name = "review", column = "pc.review"),
@FieldResult(name = "post", column = "pc.post_id"),
}
)
}
)
With the SqlResultSetMapping
in place, we can fetch the Post
and PostComment
entities like this:
有了SqlResultSetMapping
就位,我们可以像这样获取Post
和PostComment
实体:
List<Object[]> postAndCommentList = entityManager
.createNamedQuery("PostWithCommentByRank")
.setParameter("titlePattern", "High-Performance Java Persistence %")
.setParameter("rank", POST_RESULT_COUNT)
.getResultList();
And, we can validate that the entities are properly fetched:
并且,我们可以验证实体是否被正确获取:
assertEquals(
POST_RESULT_COUNT * COMMENT_COUNT,
postAndCommentList.size()
);
for (int i = 0; i < COMMENT_COUNT; i++) {
Post post = (Post) postAndCommentList.get(i)[0];
PostComment comment = (PostComment) postAndCommentList.get(i)[1];
assertTrue(entityManager.contains(post));
assertTrue(entityManager.contains(comment));
assertEquals(
"High-Performance Java Persistence - Chapter 1",
post.getTitle()
);
assertEquals(
String.format(
"Comment nr. %d - A must read!",
i + 1
),
comment.getReview()
);
}
The
@EntityResult
is also useful when fetching JPA entities via SQL stored procedures. Check out this articlefor more details.
这
@EntityResult
在通过 SQL 存储过程获取 JPA 实体时也很有用。查看这篇文章了解更多详情。
JPA SqlResultSetMapping - ConstructorResult
JPA SqlResultSetMapping - ConstructorResult
Let's assume we want to execute an aggregation query that counts the number of post_coment
records for each post
and returns the post
title
for reporting purposes. We can use the following SQL query to achieve this goal:
假设我们要执行一个聚合查询,该查询计算post_coment
每个记录的数量post
并返回post
title
用于报告目的。我们可以使用以下 SQL 查询来实现这一目标:
SELECT
p.id AS "p.id",
p.title AS "p.title",
COUNT(pc.*) AS "comment_count"
FROM post_comment pc
LEFT JOIN post p ON p.id = pc.post_id
GROUP BY p.id, p.title
ORDER BY p.id
We also want to encapsulate the post title and the comment count in the following DTO:
我们还想将帖子标题和评论数封装在以下 DTO 中:
public class PostTitleWithCommentCount {
private final String postTitle;
private final int commentCount;
public PostTitleWithCommentCount(
String postTitle,
int commentCount) {
this.postTitle = postTitle;
this.commentCount = commentCount;
}
public String getPostTitle() {
return postTitle;
}
public int getCommentCount() {
return commentCount;
}
}
To map the result set of the above SQL query to the PostTitleWithCommentCount
DTO, we can use the classes
attribute of the @SqlResultSetMapping
annotation, like this:
要将上述 SQL 查询的结果集映射到PostTitleWithCommentCount
DTO,我们可以使用注解的classes
属性@SqlResultSetMapping
,如下所示:
@NamedNativeQuery(
name = "PostTitleWithCommentCount",
query = """
SELECT
p.id AS "p.id",
p.title AS "p.title",
COUNT(pc.*) AS "comment_count"
FROM post_comment pc
LEFT JOIN post p ON p.id = pc.post_id
GROUP BY p.id, p.title
ORDER BY p.id
""",
resultSetMapping = "PostTitleWithCommentCountMapping"
)
@SqlResultSetMapping(
name = "PostTitleWithCommentCountMapping",
classes = {
@ConstructorResult(
columns = {
@ColumnResult(name = "p.title"),
@ColumnResult(name = "comment_count", type = int.class)
},
targetClass = PostTitleWithCommentCount.class
)
}
)
The ConstructorResult
annotation allows us to instruct Hibernate what DTO class to use as well as which constructor to be called when instantiating the DTO objects.
该ConstructorResult
注解允许我们指示休眠什么DTO类使用以及它的构造函数实例化对象DTO时调用。
Note that we used the type
attribute of the @ColumnResult
annotation to specify that the comment_count
should be cast to a Java int
. This is needed since some JDBC drivers use either Long
or BigInteger
for the SQL aggregation function results.
请注意,我们使用注释的type
属性@ColumnResult
来指定comment_count
应该被转换为 Java int
。这是必需的,因为某些 JDBC 驱动程序使用Long
或BigInteger
用于 SQL 聚合函数结果。
This is how you can call the PostTitleWithCommentCount
named native query using JPA:
这是PostTitleWithCommentCount
使用 JPA调用命名本机查询的方法:
List<PostTitleWithCommentCount> postTitleAndCommentCountList = entityManager
.createNamedQuery("PostTitleWithCommentCount")
.setMaxResults(POST_RESULT_COUNT)
.getResultList();
And, we can see that the returned PostTitleWithCommentCount
DTOs have been fetched properly:
而且,我们可以看到返回的PostTitleWithCommentCount
DTO 已被正确获取:
assertEquals(POST_RESULT_COUNT, postTitleAndCommentCountList.size());
assertEquals(POST_RESULT_COUNT, postTitleAndCommentCountList.size());
for (int i = 0; i < POST_RESULT_COUNT; i++) {
PostTitleWithCommentCount postTitleWithCommentCount =
postTitleAndCommentCountList.get(i);
assertEquals(
String.format(
"High-Performance Java Persistence - Chapter %d",
i + 1
),
postTitleWithCommentCount.getPostTitle()
);
assertEquals(COMMENT_COUNT, postTitleWithCommentCount.getCommentCount());
}
For more details about the best way to fetch DTO projections with JPA and Hibernate, check out this article.
有关使用 JPA 和 Hibernate 获取 DTO 投影的最佳方法的更多详细信息,请查看这篇文章。
JPA SqlResultSetMapping - ColumnResult
JPA SqlResultSetMapping - ColumnResult
The previous example showed how we could map the SQL aggregation result set to a DTO. But, what if we want to return the JPA entity for which we are counting the comments?
前面的示例展示了我们如何将 SQL 聚合结果集映射到 DTO。但是,如果我们想返回正在计算评论的 JPA 实体呢?
To achieve this goal we can use the entities
attribute to define the Post
entity we are fetching, and the classes
attribute of the @SqlResultSetMapping
annotation to map the scalar value, which in our case is the number of associated post_comment
records:
为了实现这个目标,我们可以使用entities
属性来定义Post
我们正在获取的实体,并classes
使用@SqlResultSetMapping
注释的属性来映射标量值,在我们的例子中是关联post_comment
记录的数量:
@NamedNativeQuery(
name = "PostWithCommentCount",
query = """
SELECT
p.id AS "p.id",
p.title AS "p.title",
p.created_on AS "p.created_on",
COUNT(pc.*) AS "comment_count"
FROM post_comment pc
LEFT JOIN post p ON p.id = pc.post_id
GROUP BY p.id, p.title
ORDER BY p.id
""",
resultSetMapping = "PostWithCommentCountMapping"
)
@SqlResultSetMapping(
name = "PostWithCommentCountMapping",
entities = @EntityResult(
entityClass = Post.class,
fields = {
@FieldResult(name = "id", column = "p.id"),
@FieldResult(name = "createdOn", column = "p.created_on"),
@FieldResult(name = "title", column = "p.title"),
}
),
columns = @ColumnResult(
name = "comment_count",
type = int.class
)
)
When executing the PostWithCommentCount
named native query:
执行PostWithCommentCount
命名本机查询时:
List<Object[]> postWithCommentCountList = entityManager
.createNamedQuery("PostWithCommentCount")
.setMaxResults(POST_RESULT_COUNT)
.getResultList();
we will get both the Post
entity and the commentCount
scalar column value:
我们将同时获得Post
实体和commentCount
标量列值:
assertEquals(POST_RESULT_COUNT, postWithCommentCountList.size());
for (int i = 0; i < POST_RESULT_COUNT; i++) {
Post post = (Post) postWithCommentCountList.get(i)[0];
int commentCount = (int) postWithCommentCountList.get(i)[1];
assertTrue(entityManager.contains(post));
assertEquals(i + 1, post.getId().intValue());
assertEquals(
String.format(
"High-Performance Java Persistence - Chapter %d",
i + 1
),
post.getTitle()
);
assertEquals(COMMENT_COUNT, commentCount);
}