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

Posted

技术标签:

【中文标题】默认情况下,条件使用“内连接”而不是“左连接”方法使我的查询工作不是我计划的方式【英文标题】:criteria uses "inner join" instead "left join" approach by default making my query work not the way I planned 【发布时间】:2013-06-09 14:39:08 【问题描述】:

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

测试平台:

给定 A、B 和 C 类:

class A
    B someObject


class B
    C importantObject


class C
    boolean interestingFlag

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

他们的 B.C 对象为 null 或 他们的 B.C 对象interestingFlag 值为假

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

此方法生成正确的 A 列表,其中 B.C 为空(条件 2 已注释掉)或正确的 A 列表,其中 B.C.interestingFlag = false(无论条件 1 是否被注释掉)。当两个条件都未注释时,它仅返回 A.B.C.interestingFlag = false 的元素列表(忽略 A.B.C = 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 = false 对象)

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

【问题讨论】:

尝试在您的数据源中启用 logSql=true 并查看此 qquery 生成的 sql。请张贴在这里。 @TomaszKalkosiński 这里要求的是休眠 sql 输出。我知道如何调整查询以按我的意愿工作。我不知道如何让 GORM 产生正确的查询 【参考方案1】:

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

    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)
         

    

评论中建议的解决方案更新:

    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)
         

    

【讨论】:

你先生,是救命恩人!! CriteriaSpecification.LEFT_JOIN 已被弃用。对于最新版本,请使用 JoinType.LEFT_OUTER_JOIN。来源:docs.jboss.org/hibernate/orm/5.0/javadocs/org/hibernate/…【参考方案2】:

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

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)
     

【讨论】:

它们都产生了错误。解决方案 1:类 org.hibernate.QueryException 消息无法解析属性:importantObject of:A. 解决方案 2:groovy.lang.MissingMethodException 消息没有方法签名:AController.importantObject() 适用于参数类型:(javax.persistence. criteria.JoinType, AController$_list_closure1_closure2_closure3) 值:[左,AController$_list_closure1_closure2_closure3@40230d] 这很奇怪,因为消息表明 A 对象有错误,而 importantObject 在 someObject 闭包中。至于解决方案 1,请尝试在 someObject 上创建别名,看看是否有帮助。 将别名更改为 createAlias("someObject.importantObject", "io", CriteriaSpecification.LEFT_JOIN) 后没有运气。现在找不到“io() 方法” @AndrzejBobak 我认为您的问题有问题,它也会影响您的查询。您提供的结构与您的问题和查询不匹配。如果没有正确的结构,我们将无法继续前进,因此我们可以解决您的问题。 这是我的示例项目中的完整副本。出于问题的目的设置它并生成 sql 输出花了我大约 10 分钟。我使用 Grails 2.2.1 和 Postgresql 8.3。如果你有兴趣,我可以给你发一份这个项目的副本,但它太小了,可以在几分钟内创建(3 个类,每个类有 1 个字段,生成的视图 + 脚手架)。我真的不明白你所说的“我提出的结构与我的问题和查询不符”是什么意思。【参考方案3】:

我只能使用 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])

【讨论】:

以上是关于默认情况下,条件使用“内连接”而不是“左连接”方法使我的查询工作不是我计划的方式的主要内容,如果未能解决你的问题,请参考以下文章

解释 MySQL 外连接内连接与自连接的区别?

sql连接查询中的分类

MySQL 的内连接、左连接、右连接有啥区别?

mysql连接内连接左连接右连接全连接

内连接左外连接右外连接交叉连接区别

sql 各种连接的使用条件,