Hibernate的抓取策略
Hibernate的抓取策略
猪刚烈 发表于3年前
Hibernate的抓取策略
  • 发表于 3年前
  • 阅读 8
  • 收藏 0
  • 点赞 0
  • 评论 0

腾讯云 技术升级10大核心产品年终让利>>>   

1.制定合理的抓取策略对系统性能的提升有很大的作用。

Hibernate推荐的原则是:通常情况下,我们并不使用映射文档进行抓取策略的定制。更多的是,保持其默认值,然后在特
定的事务中, 使用 HQL 的左连接抓取(left join fetch) 对其进行重载。

 

hibernate推荐的做法也是最佳实践:把所有对象关联的抓取都设为lazy!然后在特定事务中重载!

 这种考虑是基于:对象图是错综复杂的,有时候哪怕我们只是eager load了一个对象也可能会导致很多对象被load出来!

在Hibernate中,所有对象关联都是lazy的,但是JPA有所不同,one-to-one和many-to-one对象的默认抓取策略是eager!因此在使用JPA @annotation时务必要把这些默认是eager的映射都改成lazy!

以下摘自JPwH一书571页:

in Hibernate:
the associated entity instance must be fetched eagerly, not lazily. We already mentioned that Java Persistence has a different default fetch plan than Hibernate. Although all associations in Hibernate are completely lazy, all @ManyToOne
and@OneToOne associations default to FetchType.EAGER! This default was standardized to allow Java Persistence provider implementations without lazy loading (in practice, such a persistence provider wouldn’t be very useful). We recommend that you default to the Hibernate lazy loading fetch plan by setting FetchType. LAZY in your to-one association mappings and only override it when necessary!

关于N+1次查询与使用抓取策略进行调优

首先明确一点:

lazy的集合是在第一次访问时通过一个select一次性加载出来的(不是在迭代时迭代一条select一条)。这就是N+1中的1。

在迭代过程中,如果要访问迭代元素依赖的其他对象或集合时,若它们是lazyload的,那么hibernate会生成一个select从数据库中加载出这个对象。N次循环就会生成n个select,这就是N+1中的N.

如上面所说,在映射时,我们应该遵从hibernate的默认抓取设置,即所有关联对象都是lazyload的。然后,我们应该在具体的事务中定制抓取策略,使得每次load的数据刚刚好,不多也不少,使得性能最优化。以下是Forum和Thread的一个典型例子:

1.未制定抓取计划。所有抓取按映射文档中的定义执行。(文档中定义的全部是lazy)

根据映射文档定义的抓取策略(所有关联对象,不管是单端还是集合都映射为fetch=lazy)生成的SQL.从SQL中可以看出:一开始只加载了Forum的基本信息,无任何关联对象。然后访问到Forum的Thread集合时,生成一条select,加载出全部的集合元素。然后再迭代集合的过程中生成一条一条的select查出每一个thread的subject post和author!
这也就是典型的N+1次查询问题。
Hibernate:
    /* Get Forum_1 */
 select
        this_.id as id2_0_,
        this_.creationTime as creation2_2_0_,
        this_.description as descript3_2_0_,
        this_.groupId as groupId2_0_,
        this_.modifiedTime as modified4_2_0_,
        this_.name as name2_0_
    from
        Forum this_
    where
        this_.id=?
Hibernate:
    /*  Get all threads of Forum_1.(Only thread's basic fields)*/
 select
        threads0_.forumId as forumId1_,
        threads0_.id as id1_,
        threads0_.id as id5_0_,
        threads0_.creationTime as creation2_5_0_,
        threads0_.forumId as forumId5_0_,
        threads0_.modifiedTime as modified3_5_0_,
        threads0_1_.subjectId as subjectId6_0_
    from
        Thread threads0_
    left outer join
        Thread_Subject threads0_1_
            on threads0_.id=threads0_1_.threadId
    where
        threads0_.forumId=?
/* Below is N+1 select! */
Hibernate:
    /* Select Thread_1's subject post */
 select
        post0_.id as id4_0_,
        post0_.authorId as authorId4_0_,
        post0_.creationTime as creation2_4_0_,
        post0_.isSubject as isSubject4_0_,
        post0_.messageBody as messageB4_4_0_,
        post0_.modifiedTime as modified5_4_0_,
        post0_.quotedPostId as quotedPo7_4_0_,
        post0_.threadId as threadId4_0_,
        post0_.title as title4_0_
    from
        Post post0_
    where
        post0_.id=?
Hibernate:
     /* Select Thread_1's subject post's author. */
 select
        user0_.id as id0_0_,
        user0_.accountNonExpired as accountN2_0_0_,
        user0_.accountNonLocked as accountN3_0_0_,
        user0_.credentialsNonExpired as credenti4_0_0_,
        user0_.email as email0_0_,
        user0_.enabled as enabled0_0_,
        user0_.password as password0_0_,
        user0_.username as username0_0_,
        user0_.version as version0_0_
    from
        User user0_
    where
        user0_.id=?
Hibernate:
     /* Select Thread_2's subject post */
  select
        post0_.id as id4_0_,
        post0_.authorId as authorId4_0_,
        post0_.creationTime as creation2_4_0_,
        post0_.isSubject as isSubject4_0_,
        post0_.messageBody as messageB4_4_0_,
        post0_.modifiedTime as modified5_4_0_,
        post0_.quotedPostId as quotedPo7_4_0_,
        post0_.threadId as threadId4_0_,
        post0_.title as title4_0_
    from
        Post post0_
    where
        post0_.id=?
Hibernate:
   /* Select Thread_2's subject post's author. */
 select
        user0_.id as id0_0_,
        user0_.accountNonExpired as accountN2_0_0_,
        user0_.accountNonLocked as accountN3_0_0_,
        user0_.credentialsNonExpired as credenti4_0_0_,
        user0_.email as email0_0_,
        user0_.enabled as enabled0_0_,
        user0_.password as password0_0_,
        user0_.username as username0_0_,
        user0_.version as version0_0_
    from
        User user0_
    where
        user0_.id=?

2.下面是通过join fetch重新制定了抓取计划后生成的sql。
Hibernate:
    /* Fetch all threads with subject post and author by left out join.*/
 select
        this_.id as id2_3_,
        this_.creationTime as creation2_2_3_,
        this_.description as descript3_2_3_,
        this_.groupId as groupId2_3_,
        this_.modifiedTime as modified4_2_3_,
        this_.name as name2_3_,
        threads2_.forumId as forumId5_,
        threads2_.id as id5_,
        threads2_.id as id5_0_,
        threads2_.creationTime as creation2_5_0_,
        threads2_.forumId as forumId5_0_,
        threads2_.modifiedTime as modified3_5_0_,
        threads2_1_.subjectId as subjectId6_0_,
        post3_.id as id4_1_,
        post3_.authorId as authorId4_1_,
        post3_.creationTime as creation2_4_1_,
        post3_.isSubject as isSubject4_1_,
        post3_.messageBody as messageB4_4_1_,
        post3_.modifiedTime as modified5_4_1_,
        post3_.quotedPostId as quotedPo7_4_1_,
        post3_.threadId as threadId4_1_,
        post3_.title as title4_1_,
        user4_.id as id0_2_,
        user4_.accountNonExpired as accountN2_0_2_,
        user4_.accountNonLocked as accountN3_0_2_,
        user4_.credentialsNonExpired as credenti4_0_2_,
        user4_.email as email0_2_,
        user4_.enabled as enabled0_2_,
        user4_.password as password0_2_,
        user4_.username as username0_2_,
        user4_.version as version0_2_
    from
        Forum this_
    left outer join
        Thread threads2_
            on this_.id=threads2_.forumId
    left outer join
        Thread_Subject threads2_1_
            on threads2_.id=threads2_1_.threadId
    left outer join
        Post post3_
            on threads2_1_.subjectId=post3_.id
    left outer join
        User user4_
            on post3_.authorId=user4_.id
    where
        this_.id=?

调优后的只生成一条sql,性能调优效果明显!

成lazy 

 

 

1

1

1

1

1

 

 

 

 

共有 人打赏支持
粉丝 22
博文 708
码字总数 110
作品 1
×
猪刚烈
如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
* 金额(元)
¥1 ¥5 ¥10 ¥20 其他金额
打赏人
留言
* 支付类型
微信扫码支付
打赏金额:
已支付成功
打赏金额: