查询hasMany关系的属性

时间:2010-06-29 16:54:24

标签: hibernate grails gorm

我正在处理的域名中存在以下关联(不幸的是,实际上并不是盗版者)。一艘船上有很多海盗。我希望能够找到所有有船长的船只,船上没有任何陆地润滑油。

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中编写查询?

2 个答案:

答案 0 :(得分:3)

您的查询无法正常工作的原因主要是因为SQL连接的性质。如果任何单独的行符合您的条件,它将返回一艘船。在连接中有行是船长但不是陆地润滑剂(即每个黑胡子行),所以你得到了所有船只。这样写的标准就是不是所有的机组人员,而是每个机组成员都是单独的。

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

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

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

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

在这个查询中,我找到了所有带船长的船只,并从那套船上拆除了所有有陆地润滑油的船只。

答案 1 :(得分:0)

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

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