mhl*_*dry 34 java hibernate jpa pojo
我试图使用带@ConstructorResult的@SqlResultSetMapping将Native查询的结果映射到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;
...
Run Code Online (Sandbox Code Playgroud)
然后在我的DAO中:
Query query = em.createNativeQueryBar(QUERY, "foo");
... set some parameters ...
List<Bar> list = (List<Bar>) query.getResultList();
Run Code Online (Sandbox Code Playgroud)
我已经读过这个功能仅在JPA 2.1中受支持,但这正是我正在使用的.这是我的依赖:
<dependency>
<groupId>org.hibernate.javax.persistence</groupId>
<artifactId>hibernate-jpa-2.1-api</artifactId>
<version>1.0.0.Final</version>
</dependency>
Run Code Online (Sandbox Code Playgroud)
我发现了一些资源,包括这一个:jpa 2.1中的@ConstructorResult映射.但我仍然没有运气.
我错过了什么?为什么不能找到SqlResultSetMapping?
javax.persistence.PersistenceException: org.hibernate.MappingException: Unknown SqlResultSetMapping [foo]
Run Code Online (Sandbox Code Playgroud)
zbi*_*big 32
@SqlResultSetMapping注释不应该放在POJO上.把它放在(任何)@Entity课堂上."Unknown SqlResultSetMapping [foo]"告诉您,JPA提供程序在名称'foo'下没有看到任何映射.请参阅我的另一个答案,以获得正确的示例
wil*_*oop 11
简短的工作示例:
DTO POJO课程
@lombok.Getter
@lombok.AllArgsConstructor
public class StatementDto {
private String authorName;
private Date createTime;
}
Run Code Online (Sandbox Code Playgroud)存储库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
}
Run Code Online (Sandbox Code Playgroud)Vla*_*cea 10
让我们考虑一下我们的数据库中有以下内容post和表格:post_comment
SqlResultSetMappingJPA注释SqlResultSetMapping如下所示:
@Repeatable(SqlResultSetMappings.class)
@Target({TYPE})
@Retention(RUNTIME)
public @interface SqlResultSetMapping {
String name();
EntityResult[] entities() default {};
ConstructorResult[] classes() default {};
ColumnResult[] columns() default {};
}
Run Code Online (Sandbox Code Playgroud)
该SqlResultSetMapping 注释是可重复的,并且应用于实体类级别。除了使用 Hibernate 用来注册映射的唯一名称之外,还有三种映射选项:
EntityResultConstructorResultColumnResult接下来,我们将了解这三个映射选项如何工作,以及需要使用它们的用例。
该EntityResult选项允许您将 JDBCResultSet列映射到一个或多个 JPA 实体。
假设我们想要获取前 5 个Post实体以及与PostComment给定模式匹配的所有关联实体title。
正如我在本文中所解释的,我们可以使用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
Run Code Online (Sandbox Code Playgroud)
但是,我们不想返回标量列值的列表。我们希望从此查询返回 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"),
}
)
}
)
Run Code Online (Sandbox Code Playgroud)
就位后,我们可以像这样SqlResultSetMapping获取Post和实体:PostComment
List<Object[]> postAndCommentList = entityManager
.createNamedQuery("PostWithCommentByRank")
.setParameter("titlePattern", "High-Performance Java Persistence %")
.setParameter("rank", POST_RESULT_COUNT)
.getResultList();
Run Code Online (Sandbox Code Playgroud)
并且,我们可以验证实体是否已正确获取:
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()
);
}
Run Code Online (Sandbox Code Playgroud)
@EntityResult当通过 SQL 存储过程获取 JPA 实体时,这也很有用。查看这篇文章了解更多详细信息。
假设我们要执行一个聚合查询,计算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
Run Code Online (Sandbox Code Playgroud)
我们还希望将帖子标题和评论计数封装在以下 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;
}
}
Run Code Online (Sandbox Code Playgroud)
为了将上述SQL查询的结果集映射到DTO PostTitleWithCommentCount,我们可以使用注释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
)
}
)
Run Code Online (Sandbox Code Playgroud)
该ConstructorResult注释允许我们指示 Hibernate 使用什么 DTO 类以及在实例化 DTO 对象时调用哪个构造函数。
请注意,我们使用注释type的属性@ColumnResult来指定comment_count应该转换为 Java int。这是必需的,因为某些 JDBC 驱动程序使用Long或BigInteger作为 SQL 聚合函数结果。
这是PostTitleWithCommentCount使用 JPA 调用命名本机查询的方法:
List<PostTitleWithCommentCount> postTitleAndCommentCountList = entityManager
.createNamedQuery("PostTitleWithCommentCount")
.setMaxResults(POST_RESULT_COUNT)
.getResultList();
Run Code Online (Sandbox Code Playgroud)
并且,我们可以看到返回的PostTitleWithCommentCountDTO 已被正确获取:
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());
}
Run Code Online (Sandbox Code Playgroud)
有关使用 JPA 和 Hibernate 获取 DTO 投影的最佳方法的更多详细信息,请查看这篇文章。
前面的示例展示了如何将 SQL 聚合结果集映射到 DTO。但是,如果我们想要返回正在计算评论的 JPA 实体,该怎么办?
为了实现这个目标,我们可以使用属性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
)
)
Run Code Online (Sandbox Code Playgroud)
执行PostWithCommentCount指定的本机查询时:
List<Object[]> postWithCommentCountList = entityManager
.createNamedQuery("PostWithCommentCount")
.setMaxResults(POST_RESULT_COUNT)
.getResultList();
Run Code Online (Sandbox Code Playgroud)
我们将获得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);
}
Run Code Online (Sandbox Code Playgroud)
小智 6
我能够这样做:
Session session = em().unwrap(Session.class);
SQLQuery q = session.createSQLQuery("YOUR SQL HERE");
q.setResultTransformer( Transformers.aliasToBean( MyNotMappedPojoClassHere.class) );
List<MyNotMappedPojoClassHere> postList = q.list();
Run Code Online (Sandbox Code Playgroud)
添加@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 {}
Run Code Online (Sandbox Code Playgroud)
不要忘记添加@MappedSuperclass. 这将确保 Hibernate 自动连接您的映射。
更新:此功能在 Hibernate 5.4.30.Final 中被破坏,Jira 票创建了HHH-14572
| 归档时间: |
|
| 查看次数: |
62237 次 |
| 最近记录: |