在hql查询中访问连接表以获取grails中的多对多关系

时间:2010-01-07 08:28:48

标签: sql hibernate grails hql gorm

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

但是,我无法弄清楚如何编写将解析为此SQL的HQL查询,因为连接表deck_cards没有相应的grails域类。我不能使用普通连接编写这个语句,因为HQL不允许你在delete语句中使用连接,如果我使用子查询来解决这个限制mySQL会抱怨因为你不允许引用你的表从子查询的“from”部分删除。

我也尝试过使用hibernate“delete-orphan”级联选项,但是当删除牌组时,即使这些牌也属于其他牌组,也会删除所有牌。我疯了 - 这似乎应该是一个简单的任务。

修改的 关于“甲板”和“卡片”的这种特定用途似乎有些混乱。在这个应用程序中,“卡”是抽认卡,并且在一副牌中可以有成千上万的卡。此外,有时需要复制一个卡座,以便用户可以根据需要进行编辑。在这种情况下,新牌组不会复制所有牌,而是只引用与旧牌组相同的牌,如果只更换牌,则会创建新牌。此外,虽然我可以在groovy的循环中删除它,但它将非常缓慢和资源密集,因为它将生成成千上万的sql删除语句而不仅仅是1(使用上面的sql)。是否无法在HQL中访问连接表的属性?

2 个答案:

答案 0 :(得分:1)

首先,我没有看到您实体中的重点。 制作属于多个牌组的牌是不合逻辑的。同时拥有belongTohasMany

是不合逻辑的

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

如果您确实需要OneToMany,请使用session.remove(deck)并将cascade cards设置为REMOVEALL

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

for (Card card : deck.cards} {
    if (card.decks.size == 0) {
        session.remove(card);
    }
}

答案 1 :(得分:0)

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


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

  • 卡片正在更改:它们已创建,已填充卡片,已修改并已删除。它们肯定需要持久保存到您的数据库中,因为否则无法使用代码重新创建它们。
  • 卡片是恒定的:所有卡片的集合从一开始就知道,它们保持存在。如果您在数据库中删除一次卡,那么当有人需要将其放入甲板时,您需要稍后重新创建相同的卡,因此在所有情况下,您将拥有一个数据结构,负责提供可能的卡列表。如果它们没有保存在您的数据库中,您可以重新创建它们......

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

现在你已经完成了,你的卡片确实是不变的信息,因此它们甚至不需要持久存储到数据库中。您仍然会有第二个表(除了Deck),但卡表只包含卡的识别信息(可以是一个简单的整数1到52,或两个值,具体取决于你需要在你的查询中“选择”,而不是其他领域(图像,力量,一些点等......)。


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

使用值集合,卡不是实体而是组件。并且您不必删除它们,Hibernate会自动处理所有内容。