从 Grails 中的域对象的子属性列表创建对子 属性 的 属性 进行操作的条件限制

Creating a criteria restriction that operates on a property of a subproperty, from a list of sub-properties on a domain object in Grails

我有一个名为 Person 的 Groovy class,它有一个名为 identities 的一对多关联,其中每个元素都是 Identity class。 Identity class 有一个名为 channel 的 属性,它有一个名为 channelName 的属性。这是一个精简的例子:

class Person {
    static hasMany = [identities: Identity]

    String username
    String firstName
    String lastName
    ...
}

class Identity {
    Channel channel

    ...
}

class Channel {
    String channelName

    ...
}

有一种方法可以根据传入的参数动态构建条件来搜索人员。一个精简的例子:

Person[] findPeople(String username, String channelName, int limit) {
    return buildCriteria(username, channelName).list(max: limit) {
        order('lastName')
        order('firstName')
    }
}

buildCriteria 方法如下所示:

private DetachedCriteria<Person> buildCriteria(String username, String channelName) {
    def criteria = Person.where {  }

    if(username) {            
        criteria = criteria.where {
            eq('username', username)
        }
    }

    if(channelName) {
        criteria = criteria.where {
            identities {
                eq('channel.channelName', channelName)
            }
        }
    }

    return criteria
}

如果传入 channelName,如果该人的任何身份具有 channelName 属性 匹配的频道,我想获得一条 Person 记录通过的那个。我为此编写了一个单元测试,一切似乎都按预期工作,并且我得到了预期的结果。测试能够成功解析'channel.channelName'。但是当我最终在运行时使用该方法时(它是通过控制器端点调用的),我看到以下错误:

could not resolve property: channel.channelName of: com.example.domain.Identity

我能看到的唯一区别是在单元测试环境中,ChannelPersonIdentity 被模拟。但我不明白为什么这会导致这种行为差异。我有两个问题:

据我所知,正确的方法是 channel { eq('channelName', channelName) },但也许在分离条件(我通常不使用它们)或较新版本的 Grails 中,点语法也适用。

无论如何,我试图在我拥有的类似域 class 层次结构中重现您的问题,并且使用 where 方法发现了非常奇怪的结果。检查 MySQL 查询日志我可以看到最内部的查询(在您的情况下是 eq('channel.channelName', channelName))被完全忽略,即使更改为 channel { eq('channelName', channelName) }.

where { ... }.list([max:10])更改为createCriteria().list([max:10])解决了。不过,它不使用分离标准。

无论如何,以下对我有用并且避免了分离标准的需要。

List<People> findPeople(String username, String channelName, int limit) {
    People.createCriteria().list(max: limit) {
        with(buildCriteria(username, channelName))
        order('lastName')
        order('firstName')
    }
}

private Closure buildCriteria(String username, String channelName) {
    return {
        if(username) {            
            eq('username', username)
        }
        if(channelName) {
            identities {
                channel {
                    eq('channelName', channelName)
                }
            }
        }
    }
}

我喜欢这种创建标准的方式是 不可知 以后执行它们的方式。你得到一个你挂钩到标准的闭包,不管它是 Hibernate Criteria 还是 Grails 分离标准。它还允许您非常轻松地编写条件,因为您可以根据需要应用任意数量的闭包。

我知道它并不能真正回答您的问题,但也许您可以试一试并将结果与​​您当前的结果进行比较。你可能会从中得到一些线索。