使用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语句:
但从SQL Log来看,Grails还是执行了3条SQL:
也就是说在domain类中配置延迟加载存在对one-to-one, many-to-one是无效的。
如果这样配置:
执行Topic.get(id)来加载1条Topic,执行结果为1条SQL:
也就是说在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/
Powered by: BlogJava Copyright © shinewang