2

我在 grails 中有 2 个具有多对多关系的域类:套牌和卡片。

设置如下所示:

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

但是,我不知道如何编写将解析为该 SQL 的 HQL 查询,因为连接表 deck_cards 没有相应的 grails 域类。我无法使用普通连接编写此语句,因为 HQL 不允许您在删除语句中使用连接,并且如果我使用子查询来绕过此限制,mySQL 会抱怨,因为不允许您引用您所在的表从子查询的“从”部分中删除。

我还尝试使用休眠“删除-孤儿”级联选项,但这会导致在删除牌组时删除所有牌,即使这些牌也属于其他牌组。我快疯了——这似乎应该是一个简单的任务。

编辑 似乎对“甲板”和“卡片”的这种特定用途有些混淆。在这个应用程序中,“卡片”是抽认卡,一副卡片中可能有数万张。此外,有时需要制作套牌的副本,以便用户可以根据需要对其进行编辑。在这种情况下,新牌组不会复制所有牌,而是只引用与旧牌组相同的牌,如果只有一张牌被更改,就会创建一张新牌。此外,虽然我可以在 groovy 的循环中执行此删除操作,但它会非常缓慢且占用大量资源,因为它会生成数万条 sql 删除语句,而不仅仅是 1 条(使用上面的 sql)。有没有办法访问 HQL 中连接表的属性?

4

2 回答 2

1

首先,我在您的实体中看不到重点。让一张牌属于多个套牌是不合逻辑的。并且同时拥有belongTo和是不合逻辑的hasMany

无论如何,不​​要使用 HQL 进行删除。

如果您确实需要 a OneToMany,请使用session.remove(deck)并将cascadeof设置cardsREMOVEor ALL

如果您确实需要ManyToMany,请手动对实体进行检查。在伪代码中(因为我不知道 grails):

for (Card card : deck.cards} {
    if (card.decks.size == 0) {
        session.remove(card);
    }
}
于 2010-01-07T09:37:08.040 回答
0

我不会回答技术方面的问题,而是挑战模型。我希望这对您也很有价值:-)


在功能上,在我看来,您的两个对象没有相同的生命周期:

  • 牌组正在发生变化:它们被创建、填充卡片、修改和删除。它们当然需要持久化到您的数据库中,因为否则您将无法使用代码重新创建它们。
  • 卡片是不变的:所有卡片的集合从一开始就已知,它们保持存在。如果您在数据库中删除了一张卡片,那么稍后当有人需要将其放入卡片组时,您将需要重新创建同一张卡片,因此在所有情况下,您都将拥有一个负责提供可能卡片列表的数据结构. 如果它们没有保存在您的数据库中,您可以重新创建它们...

在您提供的模型中,卡片有一组可以容纳它们的牌组。但是该信息与 Decks 的生命周期相同(更改),因此我建议仅在 Deck 一侧保持关联(单向多对多关系)。

现在你已经做到了,你的卡片确实是恒定的信息,所以它们甚至不需要持久化到数据库中。您仍然会有第二个表(除了 Deck),但该卡表将仅包含卡的识别信息(可以是 1 到 52 的简单整数,或两个值,具体取决于您需要“选择”的内容在您的查询中),而不是其他字段(图像,强度,一些点等......)。


在 Hibernate 中,这些选择将多对多关系转换为值的集合(请参阅Hibernate 参考)。

对于值的集合,卡片不是实体,而是组件。而且您不必删除它们,一切都由 Hibernate 自动处理。

于 2010-01-07T09:37:21.520 回答