【问题标题】:Accessing the join table in a hql query for a many-to-many relationship in grails访问 hql 查询中的连接表以获取 grails 中的多对多关系
【发布时间】:2011-01-02 09:48:00
【问题描述】:

我在 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 会抱怨,因为不允许您引用您所在的表从子查询的“from”部分删除。

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

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

【问题讨论】:

    标签: sql hibernate grails hql grails-orm


    【解决方案1】:

    首先,我看不出你的实体有什么意义。 让一张牌属于多个套牌是不合逻辑的。同时拥有belongTohasMany 是不合逻辑的。

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

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

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

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

    【讨论】:

    • 我应该更具体地了解套牌和卡片在此应用程序中的工作方式。这是一个抽认卡应用程序,一个“牌组”可以有数万张卡片。此外,有时需要克隆套牌,以便用户制作自己的版本。在这种情况下,与其复制数以万计的卡片,我们只需将它们添加到另一个套牌中,这就是为什么存在多对多关系的原因。此外,在 grails 中,在多对多中拥有 belongsTo 仍然是正确的。我不想在 groovy 中手动循环遍历每张卡,因为这非常低效。
    • 不用担心这么小的数字的效率。或者至少尝试做一个基准测试,看看需要多长时间。
    【解决方案2】:

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


    在我看来,您的两个对象的生命周期不同:

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

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

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


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

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

    【讨论】:

    • 我很抱歉在我第一次发帖时没有澄清这一点,但这个生命周期是不正确的。一张牌不是一成不变的——它们只能在牌组内创建。将一张牌从一个牌组移动到另一个牌组的唯一方法是“克隆”牌组,在这种情况下,这张牌将属于两个牌组。我编辑了原始帖子以进一步阐明用例。
    猜你喜欢
    • 2014-08-09
    • 2021-12-10
    • 2014-09-03
    • 2012-09-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-03-31
    • 2018-10-17
    相关资源
    最近更新 更多