您的位置:首页 > 其它

Hibernate的抓取策略

2010-05-05 18:07 225 查看
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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: