2010-06-29 80 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