默认情况下,条件使用"内连接"而不是"左连接"方法,使我的查询工作不符合我的计划

And*_*bak 22 grails grails-orm

问题是:在这个特定的例子中,如何使GORM生成左连接而不是内连接?

测试平台:

鉴于A,B和C类:

class A{
    B someObject
}

class B{
    C importantObject
}

class C{
    boolean interestingFlag
}
Run Code Online (Sandbox Code Playgroud)

我想列出A类的所有元素:

  • 他们的BC对象为空OR
  • 他们的BC对象interestingFlag值是假的

到目前为止我尝试了什么:

这种方法产生正确的A列表,其中BC为空(条件2注释掉)或正确的A列表,其中BCinterestingFlag = false(无论条件1是否被注释掉).当两个条件都被取消注释时,它只返回ABCinterestingFlag = false的元素列表(ABC = null条件被忽略)

// approach 1 (conditional 1 is ignored)
def result = A.withCriteria{
    someObject{
        or{
            isNull('importantObject') // conditional 1, works well when conditional 2 is commented out
            importantObject{
                eq('interestingFlag', false) // conditional 2, works well alone, discards conditional 1 when both of them are uncommented
            }
        }  
    } 
}
Run Code Online (Sandbox Code Playgroud)

编辑:根据评论中的要求我粘贴了一个hibernate生成的sql:

Hibernate: select this_.id as id1_2_, this_.version as version1_2_, 
this_.some_object_id as some3_1_2_, someobject1_.id as id2_0_, 
someobject1_.version as version2_0_, someobject1_.important_object_id as 
important3_2_0_, importanto2_.id as id0_1_, importanto2_.version as version0_1_, 
importanto2_.interesting_flag as interest3_0_1_ from a this_ 
inner join b someobject1_ on this_.some_object_id=someobject1_.id 
inner join c importanto2_ on someobject1_.important_object_id=importanto2_.id 
where ((someobject1_.important_object_id is null or (importanto2_.interesting_flag=?)))
Run Code Online (Sandbox Code Playgroud)

当我直接将它复制并粘贴到pgAdmin查询工具中并更改了一些内容(内部联接更改为左联接,并提供了interestingFlag ="false"参数)时,一切都按照我想要的方式工作(我得到ABC = null和ABCimportantFlag =虚假物品)

Hibernate: select this_.id as id1_2_, this_.version as version1_2_, 
this_.some_object_id as some3_1_2_, someobject1_.id as id2_0_, 
someobject1_.version as version2_0_, someobject1_.important_object_id as 
important3_2_0_, importanto2_.id as id0_1_, importanto2_.version as version0_1_, 
importanto2_.interesting_flag as interest3_0_1_ from a this_ 
left join b someobject1_ on this_.some_object_id=someobject1_.id 
left join c importanto2_ on someobject1_.important_object_id=importanto2_.id 
where ((someobject1_.important_object_id is null or (importanto2_.interesting_flag=false)))
Run Code Online (Sandbox Code Playgroud)

And*_*bak 42

经过测试的工作解决方案:

    def result = A.withCriteria{
        createAlias('someObject', 'so', CriteriaSpecification.LEFT_JOIN)
        createAlias('so.importantObject', 'imp', CriteriaSpecification.LEFT_JOIN)
        or {
            isNull('so.importantObject')
            eq('imp.interestingFlag', false)
        } 

    }
Run Code Online (Sandbox Code Playgroud)

  • CriteriaSpecification.LEFT_JOIN已被弃用.对于最新版本,请使用JoinType.LEFT_OUTER_JOIN.资料来源:https://docs.jboss.org/hibernate/orm/5.0/javadocs/org/hibernate/criterion/CriteriaSpecification.html (4认同)

Tom*_*ski 5

使用左连接来实现此目的.这应该有效,但我没有对它进行现场测试.

def result = A.withCriteria{
    someObject {
        createAlias("importantObject", "io", CriteriaSpecification.LEFT_JOIN)
        or{
            isNull('importantObject') // conditional 1
            eq('io.interestingFlag', false) // conditional 2            
        }  
    } 
}
Run Code Online (Sandbox Code Playgroud)

Eidt:基于这篇文章:http://grails.1312388.n4.nabble.com/CriteriaBuilder-DSL-Enhancements-td4644831.html这也应该有效:

def result = A.withCriteria{
    someObject {
        isNull('importantObject') // conditional 1
        importantObject(JoinType.LEFT) {
            eq('interestingFlag', false) // conditional 2
        }  
    } 
}
Run Code Online (Sandbox Code Playgroud)

请在两种解决方案上发布您的结果.

编辑2:这是一个完全是您所描述的情况的查询,但与您的示例不同.你必须从这里开始,使用showSql来调试生成的SQL并调整左连接.

def result = A.withCriteria{
    or {
        isNull('someObject')
        eq('someObject.importantObject.interestingFlag', false)
    } 
}
Run Code Online (Sandbox Code Playgroud)