我正在一个grails项目中工作,希望利用hibernate标准构建器来搜索域对象的实例。我想找到其中一个"hasMany"关系包含具有特定id的域对象的实例。下面是我的意思的一个例子
域对象
class Product {
static hasMany = [ productOptions: ProductOption ]
}
class ProductOption{
Option option
static belongsTo = [ product: Product ]
}
class Option{
String name
}
这是我的域结构的一个简化的例子,不包括所有的关系。
Option
可以是尺寸、颜色、品牌等。
我想要实现的示例
假设有3个产物。
Product 1 is red, small and by brandx
Product 2 is blue, small and by brandx
Product 3 is yellow, medium and by brandz
我有几个场景需要介绍。
场景1- 寻找蓝色,小品牌的产品。在这种情况下,我应该只返回Product 2。
场景2
- 查找红色或蓝色且尺寸较小的产品。所以Product 1和Product 2都应该被退回。
场景3
- 查找brandx或brandz的产品。所以所有的产品都应该被退回。
我希望这能涵盖所有的场景。
这是一个当前尝试的例子。
def c = Product.createCriteria()
def products = c.list{
and {
productOptions {
'option' {
idEq(1)//1 is the id of the blue option
}
}
productOptions {
'option' {
idEq(5)//5 is the id of the small size option
}
}
productOptions {
'option' {
idEq(10)//10 is the id of the brandx brand option
}
}
}
}
本例的and
部分不包括所有选项,因此失败。我怎样才能最好地做到这一点?我可以使用Grails hibernate标准构建器来实现这一点吗?请让我知道额外的信息是否有帮助。
提前感谢您提供的指导。
你要找的是Groovy的Object.every(Closure)的等号。
assert[1,2,3]。每{它<4} == true断言[1,2,3]。每{它<3} == false
every()
方法返回一个布尔值,指示对于集合中的每个项,Closure的计算结果是否为true。
不幸的是,没有任何查询方法(where、criteria和HQL)提供等价的every()
。但是…你可以用HQL作弊。
注意:nor Criteria查询也可以,因为它们不支持等价的HQL HAVING子句
场景#1 -黑客
def ids = [4, 5, 6] // List of Option ids.
Product.executeQuery '''
select prd from Product as prd
join prd.productOptions as prdopts
join prdopts.option as opt
where opt.id in :ids
group by prd
having count(prd) = :count''', [ids: ids.collect { it.toLong() }, count: ids.size().toLong()]
工作原理
查询首先选择所有在id 列表中具有任何的Option
的Product
。只要一个产品至少有一个选项,它将被返回。
这会产生为每个匹配选项列出Product
的副作用。例如,如果一个Product
有三个Option
,那么Product返回三次。GROUP BY子句使查询过滤掉那些重复的列表。
然而,这些重复是这个hack的关键:如果id列表是一个唯一的列表,并且Product
s没有超过一次具有相同的Option
,那么Product
具有所有需要的Option
s,如果重复的数量等于id的数量。这就是HAVING子句通过计算Product
s的数量所做的。
场景2 &3
场景2 &3可以用同一个查询来处理。我将放弃一致性,选择Criteria查询,因为它最适合这个目的。
// Example params for scenario 2
def qparams = [
or: [1, 2], // These are color Option IDs
and: 5 // This is a size Option ID
]
// Example params for scenario 3
def qparams = [
or: [10, 11] // These are brand Option IDs
]
Product.withCriteria {
productOptions {
option {
if(qparams.and) eq('id', qparams.and.toLong())
inList('id', qparams.or.collect({ it.toLong() }))
}
}
}
总是需要或参数,但是如果指定了和参数,则if块只添加和约束。注意,这些id都只是Option id,所以您有一些灵活性。例如,您可以搜索任何颜色而不受大小限制。
关于id…
您会注意到,在我的示例中,我将IDS从整数转换为long。如果你的id来自数据库,那么它们已经是long了,所以你可以把代码拿出来。