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

我有几个场景需要介绍。

场景一

场景二

场景三

我希望这涵盖所有场景。

这是当前尝试的示例。

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 Optionids 列表中。只要产品至少有一个选项,它就会被退回。

这会产生副作用,即为它具有的每个匹配选项列出一个 Product。例如,如果 Product 有三个 Option,则 Product 返回三次。 GROUP BY 子句使查询过滤掉那些重复的列表。

然而,这些重复项是此 hack 的关键:如果 ID 列表是唯一列表,并且 Product 没有多次相同的 Option,则 [=如果重复数等于 ID 数,则 14=] 具有所有必需的 Option。这就是 HAVING 子句通过计算 Products.

的数量所做的事情

场景 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,因此您可以取出该代码。