JPA 实体图的目标是什么?

Nag*_*aga 5 java entity hibernate jpa entitygraph

我一直在学习JPA并且发现我们可以从JPA2.1 开始使用实体图。

但我还没有理解实体图的优点。

我知道使用实体图的优点之一是我们只能指定我们想要在整个实体中获取的数据,但是如果我们想要整个实体,还有其他理由使用实体图吗?

或者我们应该只在我们想要检索部分数据时才使用实体图?

如果我们在使用实体图时还有其他目的或优点,我想知道。

Vla*_*cea 8

JPAEntity Graph允许您覆盖默认的获取计划。

\n

默认获取计划

\n

正如我在本文中所解释的,每个实体都有一个默认的获取计划,该计划在实体映射期间定义\xe2\x80\x99s 并指示 Hibernate 如何获取实体关联。

\n

默认情况下,@ManyToOne关联@OneToOne使用 FetchTyp.EAGER 策略,从性能角度来看,这是一个糟糕的选择。因此,出于这个原因,设置 all@ManyToOne@OneToOne关联来使用该FetchType.LAZY策略是一个很好的做法,如下例所示:

\n
@Entity(name = "PostComment")\n@Table(name = "post_comment")\npublic class PostComment {\n\n    @Id\n    private Long id;\n\n    @ManyToOne(fetch = FetchType.LAZY)\n    private Post post;\n\n    private String review;\n    \n    //Getters and setters omitted for brevity\n}\n
Run Code Online (Sandbox Code Playgroud)\n

PostComment使用该方法获取实体时find

\n
PostComment comment = entityManager.find(PostComment.class, 1L);\n
Run Code Online (Sandbox Code Playgroud)\n

Hibernate 执行以下 SQL 查询:

\n
SELECT pc.id AS id1_1_0_,\n       pc.post_id AS post_id3_1_0_,\n       pc.review AS review2_1_0_\nFROM post_comment pc\nWHERE pc.id = 1\n
Run Code Online (Sandbox Code Playgroud)\n

post关联作为代理获取,该代理仅具有由上述 SQL 查询加载的外键列id设置。post_id

\n

当访问 Proxy 的任何非 id 属性时post

\n
LOGGER.info("The comment post title is \'{}\'", comment.getPost().getTitle());\n
Run Code Online (Sandbox Code Playgroud)\n

执行辅助 SQL 查询以Post按需获取实体:

\n
SELECT p.id AS id1_0_0_,\n       p.title AS title2_0_0_\nFROM post p\nWHERE p.id = 1\n\n-- The comment post title is \'High-Performance Java Persistence, part 1\'\n
Run Code Online (Sandbox Code Playgroud)\n

覆盖默认的获取计划

\n

如果我们想要覆盖默认的获取计划并post在查询执行时急切地获取关联,我们可以使用 JPQL 查询来指示 Hibernate 使用 FETCH JOIN 子句获取惰性关联:

\n
PostComment comment = entityManager.createQuery("""\n    select pc\n    from PostComment pc\n    left join fetch pc.post\n    where pc.id = :id\n    """, PostComment.class)\n.setParameter("id", 1L)\n.getSingleResult();\n\nLOGGER.info("The comment post title is \'{}\'", comment.getPost().getTitle());\n
Run Code Online (Sandbox Code Playgroud)\n

然后,默认的获取计划将被覆盖,并且post关联将被急切地获取:

\n
SELECT pc.id AS id1_1_0_,\n       p.id AS id1_0_1_,\n       pc.post_id AS post_id3_1_0_,\n       pc.review AS review2_1_0_,\n       p.title AS title2_0_1_\nFROM post_comment pc\nLEFT JOIN post p ON pc.post_id = p.id\nWHERE pc.id = 1\n
Run Code Online (Sandbox Code Playgroud)\n

声明式 JPA 实体图

\n

还可以使用 JPA 实体图覆盖默认的获取计划。例如,我们可以使用以下 JPA@EntityGraph注释定义特定的获取计划:

\n
@Entity(name = "PostComment")\n@Table(name = "post_comment")\n@NamedEntityGraph(\n    name = "PostComment.post",\n    attributeNodes = @NamedAttributeNode("post")\n)\npublic class PostComment {\n    //Code omitted for brevity\n}\n
Run Code Online (Sandbox Code Playgroud)\n

实体图就位后PostComment.post,我们现在可以加载PostComment实体及其关联post实体,如下所示:

\n
PostComment comment = entityManager.find(\n    PostComment.class, \n    1L,\n    Collections.singletonMap(\n        "javax.persistence.loadgraph",\n        entityManager.getEntityGraph("PostComment.post")\n    )\n);\n
Run Code Online (Sandbox Code Playgroud)\n

并且,当执行上述find方法时,Hibernate 会生成以下 SQL SELECT 查询:

\n
SELECT pc.id AS id1_1_0_,\n       pc.post_id AS post_id3_1_0_,\n       pc.review AS review2_1_0_,\n       p.id AS id1_0_1_,\n       p.title AS title2_0_1_\nFROM post_comment pc\nLEFT OUTER JOIN post p ON pc.post_id = p.id\nWHERE pc.id = 1\n
Run Code Online (Sandbox Code Playgroud)\n

如果您使用 Spring,那么您可以使用注释在存储库方法中引用 JPA 实体图@EntityGraph

\n
@Repository\npublic interface PostCommentRepository \n        extends CrudRepository<PostComment, Long> {\n\n    @EntityGraph(\n        value = "PostComment.post", \n        type = EntityGraphType.LOAD\n    )\n    PostComment findById(Long id);\n}\n
Run Code Online (Sandbox Code Playgroud)\n

编程式 JPA 实体图

\n

如果您不喜欢注释,那么您还可以使用createEntityGraphJPA 的方法以编程方式构建 JPA 实体图EntityManager,如以下示例所示:

\n
EntityGraph<PostComment> postCommentGraph = entityManager\n    .createEntityGraph(PostComment.class);\n    \npostCommentGraph.addAttributeNodes("post");\n\nPostComment comment = entityManager.find(\n    PostComment.class, \n    1L,\n    Collections.singletonMap(\n        "javax.persistence.loadgraph",\n        postCommentGraph\n    )\n);\n
Run Code Online (Sandbox Code Playgroud)\n


Van*_*ria 3

在 JPA/Hibernate 中,获取具有关联的实体始终是性能问题。

  • 一次又一次地延迟加载事务中的关联会导致 n+1 选择问题,为了避免此类问题,使用了 JPQL join fetch 和 Criteria api join。但是用这两者获取数据也会导致交叉连接问题,这意味着所有表记录的交叉连接都会由hibernate返回给应用程序。
  • 此外,更改实体级别注释中定义的获取变量对于用例来说也不是一个好的选择。
  • 因此为了解决上述两个问题,引入了实体图。实体图中定义的所有节点始终会立即获取,无论它们在实体级别的定义如何。这些图表作为查询的提示传递。
  • 通过传递图形作为提示,交叉连接问题得到解决,并且注释级别指定的关联的获取行为也可以更改。

对于代码,您可以查看我的 Github 存储库