在按多对多关系查询时从结果列表中排除项目

时间:2014-09-01 21:08:17

标签: hibernate grails many-to-many subquery gorm

我是Grails的新用户,我很难通过GORM设置进行更复杂的查询。

我有两个域对象/表,LOCATION和REASON,它们有多对多的映射,由LOCATION_REASON表管理:

Location.groovy:

class Location {
    String description
    // other fields

    static hasMany = [reasons: Reason]

    static mapping = {
        sort description:"asc"
        id generator:'native', params:[sequence:'ATTENDANCE_TRACKER.ID_SEQ']
        reasons joinTable: [name: 'LOCATION_REASON', key: 'LOCATION_ID', column: 'REASON_ID'], cascade: 'none'
    }

    static namedQueries = {
        findAllWhereReasonIsNot { reasonId ->
            reasons { ne('id', reasonId) }
        }
    }
    //Other stuff
}

Reason.groovy:

class Reason {
    String description
    //other fields

    static hasMany = [locations: Location]

    static mapping = {
        id generator:'native', params:[sequence:'ATTENDANCE_TRACKER.ID_SEQ']
        locations joinTable: [name: 'LOCATION_REASON', key: 'REASON_ID', column: 'LOCATION_ID'], cascade: 'none'
    }
    //Other stuff
}

命名查询'findAllWhereReasonIsNot',功能不如我所愿。我想要一个查询来完成获取尚未与指定原因相关联的所有位置。

在SQL术语中,我想要这个:

select * from location where id not in(select location_id from location_reason where reason_id = :reasonId);

但是,当生成hibernate SQL时,当前实现的内容看起来或多或少(编辑为仅包含相关信息,并且具有合理的'select as'名称):

select * from 
( 
   select 
    this_.id as loc_location_id, 
    this_.description as location_description,
    reasons3_.LOCATION_ID as loc_reas_location_id, 
    reasons3_.REASON_ID as loc_reas_reason_id,
    reasons_al1_.id as reas_reason_id,    
    reasons_al1_.description as reason_description
  from location this_ 
  inner join location_reason reasons3_ on this_.id=reasons3_.LOCATION_ID 
  inner join reason reasons_al1_ on reasons3_.REASON_ID=reasons_al1_.id 
  where (reasons_al1_.id <> :reasonId) 
  order by lower(this_.description) asc 
);

这会生成每个位置的多个副本的列表,每个位置对应一个副本。这可以防止生成的列表实际排除与原始​​reasonId关联的位置,因为其他原因与该位置相关联。

我对Criteria和HQL进行了一些研究,但是无法获得生成的查询...似乎Criteria和HQL有一些限制,所以我甚至不确定它是否可行。有谁知道如何在命名查询中完成那个简单的'not in'子查询?

2 个答案:

答案 0 :(得分:0)

因为有许多关系,你需要创建一个别名:

示例:

Location.createCriteria().list(){
    createAlias("reasons","r")
    not{'in'("r.id", arrayList)}
}

这也适用于命名查询。

答案 1 :(得分:0)

因此,经过多次试验和错误,我得到了一个有效的HQL查询:

Location.findAll("from Location loc where loc not in (select l from Location l join l.reasons as r where r.id = ?) order by loc.description")

非常感谢cfrick让我指出了正确的方向。之前我曾简要介绍过HQL,但误解了一些Grails文档,认为它只支持HQL的'where'子句。我还试图直接访问联接表

这是我学到的东西:

  • Grails通过find(),findAll()和executeQuery()方法支持HQL。后者支持自定义结果集,而前两个绑定到域实例
  • Groovy控制台非常棒。之前不知道它存在,但它是加快程序化查询排查的一种很好的方法
  • HQL仅适用于您的域类,但有一些时髦的语法允许您访问您的连接表记录(在我的情况下为LOCATION_REASON):“从位置l加入l.reasons为r其中r.id =?”
  • 为了从该连接记录中隔离域实例(在我的示例中为Location或Reason),您需要选择表名(或其别名):“select l from Location l”