如何在Grails中使用createCriteria执行深度查询及关联预加载
嘿,这个场景我太熟悉了!在Grails里用withCriteria实现深度预加载查询,尤其是处理你这种A→B→C的多层关联领域类,其实有挺直观的做法,还能避免讨厌的N+1查询问题。下面给你两种实用的实现方式:
在Grails中用withCriteria实现深度预加载查询
方法一:用fetchMode指定关联加载策略
fetchMode是Grails Criteria API里用来控制关联加载的核心方法,用FetchMode.JOIN可以让Grails通过SQL JOIN一次性把所有关联数据拉取回来,完美适配你的多层关联需求:
import org.hibernate.FetchMode List<A> aList = A.withCriteria { // 预加载A关联的B实例,避免懒加载触发额外查询 fetchMode 'b', FetchMode.JOIN // 给B起别名,方便后续操作它的关联C createAlias 'b', 'bAlias' // 继续预加载B关联的C实例 fetchMode 'bAlias.c', FetchMode.JOIN // 这里可以添加任意深度的查询条件,比如按C的name筛选 eq 'bAlias.c.name', 'targetName' }
简单解释下:
fetchMode 'b', FetchMode.JOIN:告诉Grails查询A的时候,直接把关联的B数据通过JOIN查出来,而不是等你访问a.b时才去数据库拉取。createAlias 'b', 'bAlias':给B创建别名是为了能在Criteria里引用它的子关联C,不然直接写b.c会有语法问题。fetchMode 'bAlias.c', FetchMode.JOIN:把B关联的C也一次性加载完成,这样整个A→B→C的链条就都在内存里了。
方法二:Grails 3+专属的简洁fetch写法
如果你用的是Grails 3及以上版本,官方提供了更简洁的fetch方法,不需要手动创建别名,直接通过关联路径就能指定预加载:
List<A> aList = A.withCriteria { // 链式指定预加载路径 fetch 'b', 'join' fetch 'b.c', 'join' // 同样可以添加深度查询条件,比如筛选C的attr1 eq 'b.c.attr1', 'desiredAttrValue' }
这种写法更直观,代码量也更少,推荐在新版本里使用。
验证预加载是否生效
你可以通过打印或者调试来确认关联对象是否真的被预加载了:
aList.each { a -> println "A ID: ${a.id}, B ID: ${a.b.id}, C Name: ${a.b.c.name}" // 如果预加载成功,这里只会执行一次SQL查询,不会每次访问b或c都触发新查询 }
如果看到控制台只有一条SQL语句,就说明预加载成功啦!
额外注意点
- 如果你的关联是一对多(比如A有多个B,或者B有多个C),用
FetchMode.JOIN可能会返回重复的A实例,这时候只要在Criteria里加个distinct(true)就能去重:List<A> aList = A.withCriteria { distinct true fetch 'b', 'join' fetch 'b.c', 'join' } - 预加载虽然方便,但也不要滥用——如果只需要A的部分数据,懒加载反而更高效。只有当你确定要用到完整的关联链条时,再用预加载。
内容的提问来源于stack exchange,提问作者Pila
相关产品推荐
相关产品推荐

