2010-06-29 84 views
4

我已经在一个域中以下关联我的工作性质(实际上不是不幸盗)。一艘船有许多海盗。我希望能够找到所有有船长的船只,而且船员没有任何陆地润滑油。查询上的的hasMany关系

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

回答

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

(我还使用了编译 - 测试 - 数据插件懒构建,而实例不是newing起来,因为它所做的脚本更容易运行多次,而无需始终重新启动grails控制台)。

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

+0

我想我可能不得不回到HQL,感谢您的详细描述。 buildLazy看起来很有用,我的脚本的顶部有Ship.list()*。delete()。 – 2010-06-30 08:33:59