为什么我们需要使用实体图?

时间:2015-07-29 14:33:17

标签: java jpa entity entitygraph

我一直在关注[^"]*,我发现自JPA 2.1以来我们可以使用实体图。

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

我知道使用实体图的一个优点是我们只能指定我们想要在整个实体中获得的数据,但是如果我们想要整个实体,是否还有其他理由使用实体图? 或者我们应该只在我们想要检索部分数据时使用实体图?

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

2 个答案:

答案 0 :(得分:2)

  1. 在Jpa中,休眠一直使用关联来获取实体 性能问题。
    • 一次又一次地在事务中缓慢加载与的关联会导致n + 1个选择问题并避免此类问题JPQL 使用join fetch和Cr iteria api join。但是用 这两个也导致交叉连接问题,意味着所有交叉连接 表记录由休眠返回到合用。
    • 对于基于用例而言,在实体级别上更改注释中定义的提取变量也不是一个好选择。
    • 因此,已经提出了解决上述两个问题的实体图。实体图中定义的所有节点总是渴望的 不论它们在实体级别上的定义如何都可以获取。这些 图形作为提示传递给查询。
    • 通过传递图作为提示,可以解决交叉联接问题以及注释级别指定的关联获取行为 也可以更改。

对于代码,您可以检查我的github存储库:

https://github.com/vaneetkataria/Jpa-Hibernate/blob/master/jdbcToJpaMigration/src/test/java/com/katariasoft/technologies/jpaHibernate/entity/fetch/entitygraph/dynamic/MultiInstructorsDynamicEntityGrpahTests.java

答案 1 :(得分:1)

JPA Entity Graph 允许您覆盖默认获取计划。

默认获取计划

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

默认情况下,@ManyToOne@OneToOne 关联使用 FetchTyp.EAGER 策略,从性能角度来看,这是一个糟糕的选择。因此,出于这个原因,最好将所有 @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 关联被提取为 Proxy,其中只有 id 由上述 SQL 查询加载的 post_id 外键列设置。

访问 post 代理的任何非 id 属性时:

LOGGER.info("The comment post title is '{}'", comment.getPost().getTitle());

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

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 实体图来覆盖默认获取计划。例如,我们可以使用以下 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 实体图

如果您不喜欢注解,那么您也可以使用 JPA createEntityGraphEntityManager 方法以编程方式构建 JPA 实体图,如下例所示:

EntityGraph<PostComment> postCommentGraph = entityManager
    .createEntityGraph(PostComment.class);
    
postCommentGraph.addAttributeNodes("post");

PostComment comment = entityManager.find(
    PostComment.class, 
    1L,
    Collections.singletonMap(
        "javax.persistence.loadgraph",
        postCommentGraph
    )
);