Grails:如何最好地构建休眠条件构建器来搜索 'hasMany' 与域实例的关系
Grails : how to best construct a hibernate criteria builder to search 'hasMany' relationships with domain instance
我正在从事一个 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
我有几个场景需要介绍。
场景一
- 查找蓝色、小号和 brandx 的产品。所以在这种情况下,我应该只 return 产品 2。
场景二
- 查找红色或蓝色的小号商品。所以产品 1 和产品 2 都应该 returned.
场景三
- 查找 brandx 或 brandz 的产品。所以所有产品都应该 returned.
我希望这涵盖所有场景。
这是当前尝试的示例。
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 criteria builder 来实现这个吗?如果其他信息有帮助,请告诉我。
提前感谢您提供的任何指导。
您要查找的内容相当于 Groovy 的 Object.every(Closure)。
断言 [1, 2, 3].every { it < 4 } == true
断言 [1, 2, 3].every { it < 3 } == false
every()
方法 returns 一个布尔值,指示闭包是否对集合中的每个项目 计算为真。
不幸的是,none 的查询方法(where、criteria 和 HQL)提供了 every()
的等价物。但是...您可以使用 HQL 作弊。
注意:Where 和 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()]
工作原理
查询首先选择所有 Product
,其中 any Option
在 ids 列表中。只要产品至少有一个选项,它就会被退回。
这会产生副作用,即为它具有的每个匹配选项列出一个 Product
。例如,如果 Product
有三个 Option
,则 Product 返回三次。 GROUP BY 子句使查询过滤掉那些重复的列表。
然而,这些重复项是此 hack 的关键:如果 ID 列表是唯一列表,并且 Product
没有多次相同的 Option
,则 [=如果重复数等于 ID 数,则 14=] 具有所有必需的 Option
。这就是 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() }))
}
}
}
总是需要or参数,但是if块只添加了and如果指定了 and 参数,则为约束。请注意,所有 ID 都只是选项 ID,因此您具有一定的灵活性。例如,您可以搜索没有尺寸限制的任何颜色。
关于 ID...
您会注意到,在我的示例中,我将 IDS 从整数转换为长整数。如果您的 ID 来自数据库,那么它们已经是 Longs,因此您可以取出该代码。
我正在从事一个 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
我有几个场景需要介绍。
场景一
- 查找蓝色、小号和 brandx 的产品。所以在这种情况下,我应该只 return 产品 2。
场景二
- 查找红色或蓝色的小号商品。所以产品 1 和产品 2 都应该 returned.
场景三
- 查找 brandx 或 brandz 的产品。所以所有产品都应该 returned.
我希望这涵盖所有场景。
这是当前尝试的示例。
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 criteria builder 来实现这个吗?如果其他信息有帮助,请告诉我。
提前感谢您提供的任何指导。
您要查找的内容相当于 Groovy 的 Object.every(Closure)。
断言 [1, 2, 3].every { it < 4 } == true 断言 [1, 2, 3].every { it < 3 } == false
every()
方法 returns 一个布尔值,指示闭包是否对集合中的每个项目 计算为真。
不幸的是,none 的查询方法(where、criteria 和 HQL)提供了 every()
的等价物。但是...您可以使用 HQL 作弊。
注意:Where 和 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()]
工作原理
查询首先选择所有 Product
,其中 any Option
在 ids 列表中。只要产品至少有一个选项,它就会被退回。
这会产生副作用,即为它具有的每个匹配选项列出一个 Product
。例如,如果 Product
有三个 Option
,则 Product 返回三次。 GROUP BY 子句使查询过滤掉那些重复的列表。
然而,这些重复项是此 hack 的关键:如果 ID 列表是唯一列表,并且 Product
没有多次相同的 Option
,则 [=如果重复数等于 ID 数,则 14=] 具有所有必需的 Option
。这就是 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() }))
}
}
}
总是需要or参数,但是if块只添加了and如果指定了 and 参数,则为约束。请注意,所有 ID 都只是选项 ID,因此您具有一定的灵活性。例如,您可以搜索没有尺寸限制的任何颜色。
关于 ID...
您会注意到,在我的示例中,我将 IDS 从整数转换为长整数。如果您的 ID 来自数据库,那么它们已经是 Longs,因此您可以取出该代码。