Запросы на собственность отношения hasMany - PullRequest
4 голосов
/ 29 июня 2010

У меня есть следующая ассоциация в домене, над которым я работаю (к сожалению, это не пираты).На корабле много пиратов.Я хочу быть в состоянии найти все корабли, которые имеют капитана и не имеют каких-либо наземных смазчиков в команде.

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 ]

3 голосов
/ 30 июня 2010

Причина, по которой ваш запрос не работает, в основном из-за природы соединений 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() )

(я также использовал плагин build-test-data для отложенной сборки экземпляров, а не для их обновления, поскольку это облегчало запуск сценария несколько раз, без перезапуска консоли grails)

В этом запросе я нахожу все корабли с капитанами и из этого набора удаляю все корабли, у которых есть наземные смазчики.

0 голосов
/ 29 июня 2010

Я думаю, вам придется использовать HQL для этого, но я наткнулся на следующую статью, которая может дать вам другое представление о том, как решить проблему.

...