我一直在学习JPA
,我发现我们可以从JPA
2.1 开始使用实体图。
但是我还没有理解实体图的优点。
我知道使用实体图的优点之一是我们可以只指定我们想要在整个实体中获取的数据,但是如果我们想要一个完整的实体,还有其他理由使用实体图吗?
或者我们应该只在我们想要检索部分数据时使用实体图?
如果我们在使用实体图时还有其他目的或优点,我想知道它。
我一直在学习JPA
,我发现我们可以从JPA
2.1 开始使用实体图。
但是我还没有理解实体图的优点。
我知道使用实体图的优点之一是我们可以只指定我们想要在整个实体中获取的数据,但是如果我们想要一个完整的实体,还有其他理由使用实体图吗?
或者我们应该只在我们想要检索部分数据时使用实体图?
如果我们在使用实体图时还有其他目的或优点,我想知道它。
JPAEntity Graph
允许您覆盖默认提取计划。
正如我在本文中所解释的,每个实体都有一个在实体映射期间定义的默认获取计划,并指示 Hibernate 如何获取实体关联。
默认情况下,@ManyToOne
关联@OneToOne
使用 FetchTyp.EAGER 策略,从性能角度来看,这是一个糟糕的选择。因此,出于这个原因,最好将 all@ManyToOne
和@OneToOne
关联设置为使用该FetchType.LAZY
策略,如下例所示:
@Entity(name = "PostComment")
@Table(name = "post_comment")
public class PostComment {
@Id
private Long id;
@ManyToOne(fetch = FetchType.LAZY)
private Post post;
private String review;
//Getters and setters omitted for brevity
}
使用方法获取PostComment
实体时find
:
PostComment comment = entityManager.find(PostComment.class, 1L);
Hibernate 执行以下 SQL 查询:
SELECT pc.id AS id1_1_0_,
pc.post_id AS post_id3_1_0_,
pc.review AS review2_1_0_
FROM post_comment pc
WHERE pc.id = 1
该post
关联作为代理获取,该代理仅具有由上述 SQL 查询加载id
的外键列的集合。post_id
post
访问Proxy的任何非 id 属性时:
LOGGER.info("The comment post title is '{}'", comment.getPost().getTitle());
Post
执行辅助 SQL 查询,按需获取实体:
SELECT p.id AS id1_0_0_,
p.title AS title2_0_0_
FROM post p
WHERE p.id = 1
-- The comment post title is 'High-Performance Java Persistence, part 1'
如果我们想覆盖默认的获取计划并post
在查询执行时急切地获取关联,我们可以使用 JPQL 查询来指示 Hibernate 使用 FETCH JOIN 子句获取惰性关联:
PostComment comment = entityManager.createQuery("""
select pc
from PostComment pc
left join fetch pc.post
where pc.id = :id
""", PostComment.class)
.setParameter("id", 1L)
.getSingleResult();
LOGGER.info("The comment post title is '{}'", comment.getPost().getTitle());
然后,默认的获取计划将被覆盖,post
关联将被急切地获取:
SELECT pc.id AS id1_1_0_,
p.id AS id1_0_1_,
pc.post_id AS post_id3_1_0_,
pc.review AS review2_1_0_,
p.title AS title2_0_1_
FROM post_comment pc
LEFT JOIN post p ON pc.post_id = p.id
WHERE pc.id = 1
也可以使用 JPA 实体图覆盖默认提取计划。例如,我们可以使用以下 JPA@EntityGraph
注释定义特定的获取计划:
@Entity(name = "PostComment")
@Table(name = "post_comment")
@NamedEntityGraph(
name = "PostComment.post",
attributeNodes = @NamedAttributeNode("post")
)
public class PostComment {
//Code omitted for brevity
}
有了PostComment.post
实体图,我们现在可以加载PostComment
实体及其关联post
实体,如下所示:
PostComment comment = entityManager.find(
PostComment.class,
1L,
Collections.singletonMap(
"javax.persistence.loadgraph",
entityManager.getEntityGraph("PostComment.post")
)
);
并且,在执行上述find
方法时,Hibernate 会生成以下 SQL SELECT 查询:
SELECT pc.id AS id1_1_0_,
pc.post_id AS post_id3_1_0_,
pc.review AS review2_1_0_,
p.id AS id1_0_1_,
p.title AS title2_0_1_
FROM post_comment pc
LEFT OUTER JOIN post p ON pc.post_id = p.id
WHERE pc.id = 1
如果您使用的是 Spring,那么您可以使用@EntityGraph
注释在 Repository 方法中引用 JPA 实体图:
@Repository
public interface PostCommentRepository
extends CrudRepository<PostComment, Long> {
@EntityGraph(
value = "PostComment.post",
type = EntityGraphType.LOAD
)
PostComment findById(Long id);
}
如果您不喜欢注解,那么您也可以使用 JPA 的createEntityGraph
方法以编程方式构建 JPA 实体图,EntityManager
如下例所示:
EntityGraph<PostComment> postCommentGraph = entityManager
.createEntityGraph(PostComment.class);
postCommentGraph.addAttributeNodes("post");
PostComment comment = entityManager.find(
PostComment.class,
1L,
Collections.singletonMap(
"javax.persistence.loadgraph",
postCommentGraph
)
);
在 JPA/Hibernate 中,获取具有关联的实体一直是性能问题。
对于代码,您可以查看我的 Github 存储库。