22

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

试验台:

给定 A、B 和 C 类:

class A{
    B someObject
}

class B{
    C importantObject
}

class C{
    boolean interestingFlag
}

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

  • 他们的 BC 对象为空或
  • 他们的 BC 对象interestingFlag 值为 false

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

这种方法产生正确的 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
            }
        }  
    } 
}

编辑:根据评论中的要求,我正在粘贴休眠生成的 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=?)))

当我直接将它复制并粘贴到 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)))
4

3 回答 3

43

Tested and working solution:

    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)
        } 

    }

Solution update as suggested in comment:

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

    }
于 2013-06-17T11:00:39.613 回答
5

使用左连接来实现这一点。这应该可以,但我还没有现场测试过。

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

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
        }  
    } 
}

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

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

def result = A.withCriteria{
    or {
        isNull('someObject')
        eq('someObject.importantObject.interestingFlag', false)
    } 
}
于 2013-06-13T10:19:51.007 回答
1

我只能使用 HQL 实现左外连接

Class Transaction {
    String someProperty
    static hasMany = [reviews: Review]
    static hasOne = [reviewQueue: ReviewQueue]
}

Class ReviewQueue {
   Transaction transaction
   Boolean isComplete
   Boolean isReady
}

Class Review {
  Transaction transaction
  String reviewResult
}

def list = Transaction.executeQuery(
    "select  t.id, rq.isReady, rq.isComplete, count(r.transaction.id) " +
    "from Transaction t " +
    "join t.reviewQueue rq " +
    "left outer join t.reviews r " +
    "where rq.isComplete = false " +
    "and rq.isReady = true " +
    "group by t.id " +
    "having count(r.transaction.id) = 0 " +
    "order by rq.transaction.id ",
[max: 10, offset: 0])
于 2016-05-18T16:59:00.000 回答