从 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
我能看到的唯一区别是在单元测试环境中,Channel
、Person
和 Identity
被模拟。但我不明白为什么这会导致这种行为差异。我有两个问题:
- Groovy 如何能够在测试环境中解析 属性,但在实际运行时却不能?
- 这是从子属性列表(在父对象上)查询子属性 属性 的 属性 的正确方法吗?如果不是,正确的方法是什么?
据我所知,正确的方法是 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 分离标准。它还允许您非常轻松地编写条件,因为您可以根据需要应用任意数量的闭包。
我知道它并不能真正回答您的问题,但也许您可以试一试并将结果与您当前的结果进行比较。你可能会从中得到一些线索。
我有一个名为 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
我能看到的唯一区别是在单元测试环境中,Channel
、Person
和 Identity
被模拟。但我不明白为什么这会导致这种行为差异。我有两个问题:
- Groovy 如何能够在测试环境中解析 属性,但在实际运行时却不能?
- 这是从子属性列表(在父对象上)查询子属性 属性 的 属性 的正确方法吗?如果不是,正确的方法是什么?
据我所知,正确的方法是 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 分离标准。它还允许您非常轻松地编写条件,因为您可以根据需要应用任意数量的闭包。
我知道它并不能真正回答您的问题,但也许您可以试一试并将结果与您当前的结果进行比较。你可能会从中得到一些线索。