【问题标题】:Querying on property of a hasMany relationship查询 hasMany 关系的属性
【发布时间】:2011-03-09 17:53:03
【问题描述】:

我在我正在处理的域中有以下关联(不幸的是,实际上不是海盗)。一艘船有许多海盗。我希望能够找到所有有船长但船员中没有任何陆上润滑剂的船只。

class Pirate {

  String name
  Rank rank

  enum Rank {
    CAPTAIN,
    DECK_HAND,
    LAND_LUBBER
  }

}

class Ship {

  static hasmany = [crew:Pirate]

}

我正在尝试使用 Hibernate 标准,因为从长远来看,我希望这是一个可以与其他查询链接的命名查询。这是我使用的标准。

def pirateShips = Ship.withCriteria {
  crew {
    eq('rank', Pirate.Rank.CAPTAIN)
    not {
      eq('rank', Pirate.Rank.LAND_LUBBER)
    }
  }
}

当我创建以下实体时,我没有得到预期的结果。

def blackbeard = new Pirate(name:'Blackbeard', rank:Pirate.Rank.CAPTAIN).save()
def deckHand = new Pirate(name:'Deck Hand', rank:Pirate.Rank.DECK_HAND).save()
def landLubber = new Pirate(name:'Land Lubber', rank:Pirate.Rank.LAND_LUBBER).save()

def ship = new Ship(name:'Ship 1', crew:[blackbeard]).save()
def infiltrator = new Ship(name:'Ship 2', crew:[blackbeard, landLubber]).save()
def normalShip = new Ship(name:'Ship 3', crew:[blackbeard, deckHand]).save(flush:true)

运行上述查询会返回所有 3 艘船,而我预计只有 1 号船和 3 号船会被退回(2 号船有一个讨厌的陆地润滑脂,对我没有用处)。

有没有办法使用条件 API 来生成这样的查询?如果不是,我将如何在 HQL 中编写查询?

【问题讨论】:

    标签: hibernate grails grails-orm


    【解决方案1】:

    我认为您必须为此使用 HQL,但我确实遇到了以下文章,它可能会让您对如何解决问题有另一种想法。

    http://adhockery.blogspot.com/2009/04/associations-and-criteria-queries.html

    【讨论】:

    • 谢谢,那篇文章确实解释了为什么只返回一定级别的海盗,而不是船上的所有船员。
    【解决方案2】:

    您的查询不起作用的原因主要是因为 SQL 联接的性质。如果任何单独的行符合您的条件,它将返回一艘船。连接中有船长但不是陆地 lubbers 的行(即每个黑胡子行),所以你让所有的船都回来了。这样写,标准不是针对所有机组人员,而是针对每个机组人员。

    有几种不同的方法可以解决这个问题。一种是使用子选择查询。可能可以将其写成标准,但我对它们并不熟悉,因为我喜欢 HQL,因为它看起来更像 SQL,我对此感到满意。

    这是一个返回预期船舶 1 和 3 的 HQL 查询示例:

    def blackbeard = Pirate.buildLazy(name: 'Blackbeard', rank: Pirate.Rank.CAPTAIN)
    def deckHand = Pirate.buildLazy(name: 'Deck Hand', rank: Pirate.Rank.DECK_HAND)
    def landLubber = Pirate.buildLazy(name: 'Land Lubber', rank: Pirate.Rank.LAND_LUBBER)
    
    def ship = Ship.buildLazy(name: 'Ship 1').with { crew = [blackbeard] }
    def infiltrator = Ship.buildLazy(name: 'Ship 2').with { crew = [blackbeard, landLubber] }
    def normalShip = Ship.buildLazy(name: 'Ship 3').with { crew = [blackbeard, deckHand] }
    
    def pirateShips = Ship.executeQuery( """
        select s from Ship s 
        join s.crew as p 
        where p.rank = :captain 
        and s not in 
            (select s1 from Ship s1 
             join s1.crew as p1 
             where p1.rank = :landLubber)
    """, 
    [captain: Pirate.Rank.CAPTAIN, landLubber: Pirate.Rank.LAND_LUBBER] )
    assert( ["Ship 1", "Ship 3"] == pirateShips.name.sort() )
    

    (我还使用 build-test-data 插件来延迟构建实例,而不是新建实例,因为它使脚本更容易多次运行,而无需一直重新启动 grails 控制台)。

    在这个查询中,我找到了所有有船长的船只,并从该集合中删除了所有有陆地润滑脂的船只。

    【讨论】:

    • 我想我可能不得不退回到 HQL,感谢您的详细描述。 buildLazy 看起来很有用,我的脚本顶部有 Ship.list()*.delete()。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-12-22
    • 2012-03-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-10-07
    • 1970-01-01
    相关资源
    最近更新 更多