我正在处理的项目中有一些代码,其中动态查找器在一个代码分支中的行为与在另一个代码分支中的行为不同。
这行代码返回我所有的广告客户(一共有8个),不管我在哪个分支。
Advertiser.findAllByOwner(ownerInstance)
但是当我开始添加条件时,事情就变得奇怪了。在分支A中,以下代码返回我所有的广告客户:
Advertiser.findAllByOwner(ownerInstance, [max: 25])
但是在分支B中,该代码只返回1个广告客户。
应用程序代码的更改似乎不可能影响动态查找器的工作方式。我错了吗?还有什么其他原因可能导致这个不工作吗?
编辑
我被要求发布我的类定义。我将把我认为重要的部分贴出来,而不是全部贴出来:
static mapping = {
owner fetch: 'join'
category fetch: 'join'
subcategory fetch: 'join'
}
static fetchMode = [
grades: 'eager',
advertiserKeywords: 'eager',
advertiserConnections: 'eager'
]
此代码存在于分支B中,但不存在于分支a中。当我将其取出时,事情现在按预期工作。
我决定对这段代码进行更多的挖掘,看看我能观察到什么。当我使用withCriteria
而不是动态查找器时,我发现了一些有趣的事情:
Advertiser.withCriteria{owner{idEq(ownerInstance.id)}}
我发现这返回了数千个副本!所以我尝试使用listDistinct
:
Adviertiser.createCriteria().listDistinct{owner{idEq(ownerInstance.id)}}
现在返回所有8个广告客户,没有重复。但如果我试图限制结果呢?
Advertiser.createCriteria().listDistinct{
owner{idEq(ownerInstance.id)}
maxResults 25
}
现在它返回一个结果,就像我的动态查找器一样。当我把maxResults
调到100K时,现在我得到了所有8个结果。
发生了什么?似乎连接或即时抓取(或两者)生成的sql返回了数千个重复项。默认情况下,Grails动态查找器必须返回不同的结果,所以当我不限制它们时,我没有注意到任何奇怪的东西。但是一旦我设置了限制,因为记录是按ID排序的,所以前25条记录都是重复记录,这意味着只会返回一条不同的记录。
至于连接和急切抓取,我不知道代码试图解决什么问题,所以我不能说它是否是必要的;问题是,为什么在我的类中有这么多重复的代码?
我发现,由于要进行数百个查询,因此添加了(许多层次深度的)即时抓取以加速某些报告的呈现。我们尝试过按需抓取,但其他开发人员很难使用查找器或Grails标准进行更深入的挖掘。
所以上面问题的一般答案是:而不是默认的eager,这可能会在其他地方造成巨大的噩梦,我们需要找到一种方法在单个查询上进行eager抓取,可以在树下进行多个级别
下一个问题是,怎么做?Grails对它的支持不是很好,但只要使用Hibernate的Criteria类就可以实现。以下是要点:
def advertiser = Advertiser.createCriteria()
.add(Restrictions.eq('id', advertiserId))
.createCriteria('advertiserConnections', CriteriaSpecification.INNER_JOIN)
.setFetchMode('serpEntries', FetchMode.JOIN)
.uniqueResult()
现在广告商的advertiserConnections
,将被急切获取,advertiserConnections
' serpEntries
也将被急切获取。你想往下走多远就走多远。然后你可以让你的类在默认情况下是懒惰的——对于hasMany
场景,它们绝对应该是懒惰的。
由于您的查询正在检索重复项,因此有可能这个25条记录的限制返回相同的数据,因此您的distinct将减少到一条记录。
尝试在您的类中定义equals()
和hashCode()
,特别是如果您有一些具有复合主键,或者用作hasMany
。
我也建议你尽量排除这些可能性。逐一删除fetch和eager,看看它们是如何影响结果数据的(没有限制)。