【问题标题】:Excluding Item from Result List When Querying by Many to Many Relationship多对多关系查询时从结果列表中排除项目
【发布时间】:2014-09-01 21:08:17
【问题描述】:

我是一个相当新的 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);

但是一旦生成了休眠 SQL,当前实现正在完成的工作看起来或多或少(编辑为仅包含相关信息,并具有合理的“选择为”名称):

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 有一些限制,所以我什至不确定它是否可能。有谁知道如何在命名查询中完成那个简单的“不在”子查询?

【问题讨论】:

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


【解决方案1】:

对于有很多关系你需要创建一个别名:

例子:

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

这应该也适用于命名查询。

【讨论】:

  • 我假设查询会给我所有与 reason_id 不具有相同 location_id 的位置...这不是我想要的。我没有使用 Aliases,因为我真正关心的是连接表,而不是原因集合。如果可以在不是属性的连接表上使用别名,那么这实际上可能有效。我得试一试。
【解决方案2】:

经过反复试验,我得到了一个有效的 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 as r where r.id = ?”
  • 为了将域实例(在我的示例中为 Location 或 Reason)与该连接记录隔离,您需要选择表名(或其别名):“select l from Location l”

【讨论】:

  • HQL 很好,但不太容易与其他标准链接。不过,如果您需要构建这样简单的查询,那也没关系。 Grails 2.4 引入了相关子查询(我害怕目前没有很好的记录)查看与此问题相关的提交:jira.grails.org/browse/GRAILS-9223
猜你喜欢
  • 2015-02-21
  • 2015-10-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-12-03
  • 2023-03-06
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多