使用ORM時,常常碰到N+1次查詢的問題。Hibernate采用立即加載(eager load)和延遲加載(lazy load)來解決這一問題,GROM建立在Hibernate的基礎(chǔ)之上,理論上同樣適用。但事實如何?
Grails的官方文檔中提到:默認情況下,GORM 集合使用延遲加載的并且可以通過fetchMode來配置或者是使用mapping來配置 。并給出了一段在domain中配置的樣例代碼。
但從我的使用經(jīng)驗來看,不推薦在domain類中配置延遲加載。原因如下:
1、在domain類中配置延遲加載是全局性的,有可能造成不需要開銷。
2、目前在domain類中配置延遲加載存在Bug,只對one-to-many的關(guān)系才有效。
例如論壇的列表頁面,需要顯示topic的分頁列表,其中每條topic需要顯示作者的名字,topic最后回復(fù)帖子的作者。
按照官方文檔,可以在Topic類中如下配置,查詢結(jié)果為1條topic,理論上結(jié)果應(yīng)該執(zhí)行一條SQL語句:












但從SQL Log來看,Grails還是執(zhí)行了3條SQL:

select ?

select ?

也就是說在domain類中配置延遲加載存在對one-to-one, many-to-one是無效的。
如果這樣配置:












執(zhí)行Topic.get(id)來加載1條Topic,執(zhí)行結(jié)果為1條SQL:

也就是說在domain類中配置延遲加載存在對one-to-many是有效的。以上結(jié)論對使用mapping來配置也是一樣的。
這無疑是一個尷尬的結(jié)論,適用全局立即加載的author和lastUpdateBy不能在domain類中通過配置事先,不適用全局立即加載的posts卻可以。看來GROM對Hibernate的包裝還存在問題。
目前的解決方法是不要在domain類中配置立即加載,而是在取數(shù)據(jù)的方法中按需要配置,例如Topic.list(fetch:[author:'eager', lastUpdateBy:'eager'])
另外,可以參考一下這里關(guān)于立即加載和N+1次查詢性能的爭論。
歡迎訪問我的blog: http://www.eoss.cn/blog/