shinewang

  BlogJava :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理 ::
  53 随笔 :: 0 文章 :: 200 评论 :: 0 Trackbacks

使用ORM时,常常碰到N+1次查询的问题。Hibernate采用立即加载(eager load)和延迟加载(lazy load)来解决这一问题,GROM建立在Hibernate的基础之上,理论上同样适用。但事实如何?

Grails的官方文档中提到:默认情况下,GORM 集合使用延迟加载的并且可以通过fetchMode来配置或者是使用mapping来配置 。并给出了一段在domain中配置的样例代码。

但从我的使用经验来看,不推荐在domain类中配置延迟加载。原因如下:
1、在domain类中配置延迟加载是全局性的,有可能造成不需要开销。
2、目前在domain类中配置延迟加载存在Bug,只对one-to-many的关系才有效。

例如论坛的列表页面,需要显示topic的分页列表,其中每条topic需要显示作者的名字,topic最后回复帖子的作者。

按照官方文档,可以在Topic类中如下配置,查询结果为1条topic,理论上结果应该执行一条SQL语句:

class  Topic  {
    String title
    String body
    Date createTime 
=   new  Date()
    User author
    Integer viewNum 
=   0
    Integer postNum 
=   0
    Date lastUpdateTime
    User lastUpdateBy
    
static  hasMany  =  [ posts : Post ]
    
static  fetchMode  =  [author: ' eager ' , lastUpdateBy: ' eager ' ]
}

但从SQL Log来看,Grails还是执行了3条SQL:

select    from  topic  where  id = ?
select    from   user   where  id = ?
select    from   user   where  id = ?

也就是说在domain类中配置延迟加载存在对one-to-one, many-to-one是无效的。

如果这样配置:

class  Topic  {
    String title
    String body
    Date createTime 
=   new  Date()
    User author
    Integer viewNum 
=   0
    Integer postNum 
=   0
    Date lastUpdateTime
    User lastUpdateBy
    
static  hasMany  =  [ posts : Post ]
    
static  fetchMode  =  [posts: ' eager ' ]
}

执行Topic.get(id)来加载1条Topic,执行结果为1条SQL:

select    from  topic  left   outer   join  post  on  topic.id = posttopic_id  where  topic.id = ?

也就是说在domain类中配置延迟加载存在对one-to-many是有效的。以上结论对使用mapping来配置也是一样的。

这无疑是一个尴尬的结论,适用全局立即加载的author和lastUpdateBy不能在domain类中通过配置事先,不适用全局立即加载的posts却可以。看来GROM对Hibernate的包装还存在问题。

目前的解决方法是不要在domain类中配置立即加载,而是在取数据的方法中按需要配置,例如Topic.list(fetch:[author:'eager', lastUpdateBy:'eager'])

另外,可以参考一下这里关于立即加载和N+1次查询性能的争论。


欢迎访问我的blog: http://www.eoss.cn/blog/

posted on 2008-11-26 16:06 shinewang 阅读(2007) 评论(0)  编辑  收藏 所属分类: Groovy & Grails

只有注册用户登录后才能发表评论。


网站导航: