Доступ к таблице соединений в hql-запросе для отношения «многие ко многим» в Grails

У меня есть 2 доменных класса с отношениями "многие ко многим" в Grails: колоды и карты.

Настройка выглядит следующим образом:

class Deck {
static hasMany = [cards: Card]
}

class Card {
static hasMany = [decks: Deck]
static belongsTo = Deck
}

После удаления колоды я также хочу удалить все карты, которые больше не принадлежат колоде. Самый простой способ добиться этого — написать что-то вроде следующего sql:

delete from card where card.id not in(select card_id from deck_cards);

Однако я не могу понять, как написать HQL-запрос, который будет разрешаться в этот SQL, потому что таблица соединений, deck_cards, не имеет соответствующего доменного класса Grails. Я не могу написать этот оператор, используя обычные соединения, потому что HQL не позволяет вам использовать соединения в операторах удаления, и если я использую подзапрос, чтобы обойти это ограничение, mySQL жалуется, потому что вам не разрешено ссылаться на таблицу, которую вы удаление из в разделе "от" подзапроса.

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

редактировать Кажется, существует некоторая путаница в этом конкретном использовании «колод» и «карт». В этом приложении «карточки» — это карточки, и в колоде их может быть несколько десятков тысяч. Также иногда необходимо сделать копию колоды, чтобы пользователи могли редактировать ее по своему усмотрению. В этом сценарии вместо копирования всех карт новая колода будет просто ссылаться на те же карты, что и старая колода, и только если карта будет изменена, будет создана новая карта. Кроме того, хотя я могу выполнить это удаление в цикле в groovy, оно будет очень медленным и ресурсоемким, поскольку будет генерировать десятки тысяч операторов удаления sql, а не только 1 (используя приведенный выше sql). Нет ли способа получить доступ к свойству таблицы соединений в HQL?


person David Chanin    schedule 07.01.2010    source источник


Ответы (2)


Во-первых, я не вижу смысла в ваших сущностях. Нелогично заставлять карту принадлежать более чем одной колоде. И нелогично иметь и belongTo, и hasMany.

В любом случае, не используйте HQL для удаления.

Если вам действительно нужен OneToMany, используйте session.remove(deck) и установите cascade из cards на REMOVE или ALL.

Если вы действительно хотите ManyToMany, выполните проверки вручную объектов. В псевдокоде (поскольку я не знаю Grails):

for (Card card : deck.cards} {
    if (card.decks.size == 0) {
        session.remove(card);
    }
}
person Bozho    schedule 07.01.2010
comment
Я должен был более подробно рассказать о том, как работают колоды и карты в этом приложении. Это приложение для карточек, а в колоде могут быть десятки тысяч карточек. Кроме того, иногда необходимо клонировать колоду, чтобы пользователь мог создать свою собственную версию. В этом случае вместо того, чтобы копировать десятки тысяч карт, мы просто добавляем их в другую колоду, поэтому возникает отношение «многие ко многим». Кроме того, в Grails по-прежнему корректно наличие принадлежности к множеству ко многим. Я не хочу вручную перебирать каждую карту в groovy, потому что это очень неэффективно. - person David Chanin; 08.01.2010
comment
не беспокойтесь об эффективности для таких небольших чисел. Или хотя бы попробуйте сделать бенчмарк и посмотреть, сколько времени это займет. - person Bozho; 08.01.2010

Я не буду отвечать на техническую сторону, но оспариваю модель. Я надеюсь, что это также будет ценным для вас :-)


Функционально мне кажется, что ваши два объекта не имеют одинакового жизненного цикла:

  • Колоды меняются: они создаются, заполняются картами, модифицируются и удаляются. Они, безусловно, должны быть сохранены в вашей базе данных, потому что в противном случае вы не сможете воссоздать их с помощью кода.
  • Карты постоянны: набор всех карт известен с самого начала, они продолжают существовать. Если вы один раз удалите Карту в базе данных, то вам нужно будет воссоздать эту же Карту позже, когда кому-то нужно будет положить ее в Колоду, поэтому во всех случаях у вас будет структура данных, отвечающая за предоставление списка возможных Карт. . Если они не сохранены в вашей базе данных, вы можете воссоздать их...

В модели, которую вы даете, у карт есть набор колод, в которых они хранятся. Но эта информация имеет тот же жизненный цикл, что и колоды (изменение), поэтому я предлагаю удерживать ассоциацию только на стороне колоды (однонаправленное отношение «многие ко многим»).

Теперь, когда вы это сделали, ваши карты представляют собой постоянную информацию, поэтому их даже не нужно сохранять в базе данных. У вас по-прежнему будет вторая таблица (в дополнение к колоде), но эта таблица карт будет содержать только идентифицирующую информацию для карты (может быть простое целое число от 1 до 52 или два значения, в зависимости от то, что вам нужно «выбрать» в ваших запросах), а не другие поля (изображение, сила, некоторые точки и т. д.).


В Hibernate эти варианты превращают отношение «многие ко многим» в набор значений (см. справочник по спящему режиму).

С коллекцией значений карта является не сущностью, а компонентом. И вам не нужно их удалять, Hibernate автоматически позаботится обо всем.

person KLE    schedule 07.01.2010
comment
Я прошу прощения за то, что не разъяснил это, когда я впервые сделал сообщение, но этот жизненный цикл неверен. Карта не постоянна — их можно создавать только внутри колод. Единственный способ переместить карту из одной колоды в другую — это клонировать колоду, и в этом случае карта будет принадлежать обеим колодам. Я отредактировал исходный пост, чтобы уточнить вариант использования. - person David Chanin; 08.01.2010