【问题标题】:Grails named queries: find parents of child A and child BGrails 命名查询:查找孩子 A 和孩子 B 的父母
【发布时间】:2017-03-06 06:38:27
【问题描述】:

我有这个:

class Parent {
  String name
  static hasMany = [children: Child]
}

class Child {
  String name
}

我想找到所有有一个孩子叫“汤姆”和另一个孩子叫“山姆”的父母。

id | parentName
---------------
1  | Peter 
2  | Joe
3  | Ann

id | childName
---------------
1  | Tom 
2  | Sam

idParent | idChild
---------------
1        | 1 
2        | 2
3        | 1
3        | 2

在示例中,它将是 Ann。

我试过了:

static namedQueries = {
    findParents{nameList ->
        children{
            nameList.each{childName->
                and{
                     ilike('nombre', '%'+childName+'%')                 
                    }
            }           
     }

但是这样我只搜索了一个以 Tom 和 Sam 的名字命名的孩子。在示例中,它不会返回任何内容。

我已经尝试使用'in' 'name', ["Tom","Sam"] 代替 ilike 运算符,但现在我将让所有的父母都拥有一个名为“Tom”的孩子或一个名为“Sam”的孩子。在示例中,它将返回 Peter、Joe 和 Ann

有什么想法吗?

提前致谢!

【问题讨论】:

    标签: grails criteria named-query


    【解决方案1】:

    所以我现在明白了,我为你重新创建并做出了答案

    idParent | idChild
    ---------------
    1        | 1 
    2        | 2
    *3        | 1
    *3        | 2
    

    所以你追求的真的是 3 或 Ann 吗?

    Peter [test.Child : 1]
    Joe [test.Child : 2]
    Ann [test.Child : 1, test.Child : 2]
    

    你想要的是:

    list is [[test.Parent : 3, test.Child : 2, test.Parent : 3, test.Child : 1]]
    

    2016 年 12 月的 E2A 比所有早期版本都要好得多

    String query="""select new map(p.name as parentName,
                case when (select count(*) from p.children as pc where pc.name in (:children)) >= 2 then 'true' 
                else 'false' end as childMatch) from Parent p
                """
            def inputs=[:]
            inputs.children=['Tom','Sam']
            def listing = Parent.executeQuery(query,inputs,[readonly:true]) 
    

    生产:

    println "list is $listing"
    list is [[childMatch:false, parentName:Peter], 
    [childMatch:false, parentName:Joe], 
    [childMatch:true, parentName:Ann]]
    

    现在如果我们简单地改变:

    def listing = Parent.executeQuery(query,inputs,[readonly:true])?\
    .findAll{it.childMatch=='true'}
        println "list is $listing"
    list is [[childMatch:true, parentName:Ann]]
    

    您可以看到比以前的方法复杂得多

    上面的替代品,但仍然不如上面 您还可以使用in elements 绑定到真实对象而不是您的连接,如下所示:

    (:children) in elements(p.children)

    但即使使用这种方法,您也会遇到下面讨论的问题。这个答案的第一个方法是相当强大的,使用计数你可以用它作为一种权重的形式来查看有多少记录有 2 多少 1 多少 0 等等,所以更多的用法来自它 -

    早期方法

    我使用了 hql 并交叉连接:

    String query="""from Parent p1 left join p1.children pc1 , 
            Parent p left join p.children pc where 
            pc.name =:input1 and pc1.name= :input2 and 
            p1.id=p.id group by p.name"""
    
    def inputs=[:]
    inputs.input1='Tom'
    inputs.input2='Sam'
    def list = Parent.executeQuery(query,inputs,[readonly:true])
    
    println "list is ${list}"
    

    但由于所有内容都称为 name,因此如果您想使用 grails groupBy 为每个 parentName 获取更紧凑的列表,则管理起来会有些困难:

    String query="""select new map(p1.name as parentName, pc1.name as childName, pc.name as childName2) from Parent p1 left join p1.children pc1 , 
    Parent p left join p.children pc where pc.name =:input1 and pc1.name= :input2 and p1.id=p.id """ //group by p1.name, p.name"""
    def inputs=[:]
    inputs.input1='Tom'
    inputs.input2='Sam'
    def list = Parent.executeQuery(query,inputs,[readonly:true])
    def list1=list.groupBy{it.parentName}
    println "list is ${list} vs ${list1}"
    

    像上面那样将这个返回到控制台:

    Joe [test.Child : 2]
    Ann [test.Child : 2, test.Child : 1]
    list is [[childName:Sam, childName2:Tom, parentName:Ann]] vs [Ann:[[childName:Sam, childName2:Tom, parentName:Ann]]]
    

    并且要最终确定这一点,完全取决于您或给定的过程/复杂性,您是否认为该处理应该是原样(可能相当 db 费力与更轻量级的查询以及来自 grails 的一些进一步的查询查找)

    // Find all the parents that have 1 of our matches 
    // Tom but all that also have children greater than 1 
    def parents = Parent.findAll{children.name=='Tom' && children.size()>1}
    //This already returns our only other option 3
    println "found ${parents}"
    
    
    // Now to confirm that the childrens the 
    // parent has contains our other scenario:
    
    // Lets build our own list from it
    def finalList=[]
    //Iteration through our listing of parents
    parents?.each { Parent p ->
      // If the child name matches other case
      if (p.children.name.contains('Sam')) {
        // add this to our finalList
        finalList << p
      }             
    }
    // We have correct results in this list
    println "we now have ${finalList}"
    

    这可能不会产生巨大的数据库查询交叉连接,但最终对我们感兴趣的每个父母的惰性列表(他们有超过 1 个孩子)进行了大量小型查找

    这就是场景 - 我想这取决于您的数据模型以及最适合您的方式。最初的 hql 可能是一个巨大的一次性查询,它产生一个轻量级列表。具有大量轻量级查找的第二个轻量级查询。

    第二个例子输出:

    found [test.Parent : 3]
    we now have [test.Parent : 3]
    

    上面的 finalList 是为了举例说明你将如何手动完成,整个段可以转换为 1 班轮:

    def parents = Parent.findAll{children.name=='Tom' && children.size()>1}.collect{[name:it.name, children:it.children]}.findAll{it.children.name.contains('Sam')}
    

    产生:

    found [[name:Ann, children:[test.Child : 1, test.Child : 2]]]
    

    VS:

    def parents = Parent.findAll{children.name=='Tom' && children.size()>1}.collect{[name:it.name, children:it.children.name]}.findAll{it.children.contains('Sam')}
    

    产生:

    found [[name:Ann, children:[Tom, Sam]]]
    

    最后一个示例仅收集特定字段,因此最终的父列表不包含整个子/父对象,而是包含其名称的轻列表。

    所以根据需要使用。

    【讨论】:

    • 感谢 Vahid,但这样它会找到有孩子 Tom 或 Sam 的父母。我正在寻找有孩子汤姆和山姆的父母。
    • 我认为这只是一个改变的例子|| && 在第二个示例中,我用建议的另一种变体更新了答案
    • Grails 似乎不喜欢用 && || 连接的两个参数与 findAll。无论如何,我认为它会像这样: Select * from parent p inner join parent_child pc on p.id=pc.id inner join child c on pc.id=c.id where c.name like '%Tom%'和 c.name like '%Sam%' 此选择不起作用,因为 Child 中没有同时名称为 Tom 和 Sam 的行。
    • 我已经让它通过交叉加入返回正确的答案,但之前的早期副本是在我处于其他事情之中并且遇到问题的时候。我已经在上面测试过,一切都很好
    • 谢谢瓦希德!!将它与我搜索的其他参数结合起来会很疯狂。我认为只允许用户搜索一个孩子会更好(至少对我的心理健康而言)。 ;)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-07-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多