【问题标题】:Create bi-directional relationship with Grails hasMany and belongsTo doesn't work使用 Grails hasMany 和 belongsTo 创建双向关系不起作用
【发布时间】:2016-02-21 05:58:11
【问题描述】:

我正在尝试在 Grails(父子)中的两个域之间创建双向关系,但我似乎无法使其工作。 根据Grails GORM documentation oneToMany,我应该能够在父母和孩子之间创建一个hasMany(Parent) 和一个belongsTo(Child) 以创建双向关系,但它对我不起作用。

我有以下两个域:

class Game {
   String name
   String description
   Double price
}

class Review {
   static belongsTo = [game: Game]
   String reviewText
   Date reviewDate

}

然后我创建一个grails dbm-gorm-diff file.groovy 我得到一个包含以下内容的文件

databaseChangeLog = {

 changeSet(author: "efx (generated)", id: "1456032538941-1") {
    createTable(tableName: "game") {
        column(name: "id", type: "int8") {
            constraints(nullable: "false", primaryKey: "true", primaryKeyName: "gamePK")
        }

        column(name: "version", type: "int8") {
            constraints(nullable: "false")
        }

        column(name: "description", type: "varchar(255)") {
            constraints(nullable: "false")
        }

        column(name: "name", type: "varchar(255)") {
            constraints(nullable: "false")
        }

        column(name: "price", type: "float8") {
            constraints(nullable: "false")
        }

        column(name: "reviews_id", type: "int8") {
            constraints(nullable: "false")
        }
    }
}

 changeSet(author: "efx (generated)", id: "1456032538941-2") {
    createTable(tableName: "review") {
        column(name: "id", type: "int8") {
            constraints(nullable: "false", primaryKey: "true", primaryKeyName: "reviewPK")
        }

        column(name: "version", type: "int8") {
            constraints(nullable: "false")
        }

        column(name: "review_date", type: "timestamp") {
            constraints(nullable: "false")
        }

        column(name: "review_text", type: "varchar(255)") {
            constraints(nullable: "false")
        }
    }
}

 changeSet(author: "efx (generated)", id: "1456032538941-4") {
    createSequence(sequenceName: "hibernate_sequence")
}

changeSet(author: "efx (generated)", id: "1456032538941-3") {
    addForeignKeyConstraint(baseColumnNames: "reviews_id", baseTableName: "game", constraintName: "FK_jnjkmccicsjmsvqub534xcnnm", deferrable: "false", initiallyDeferred: "false", referencedColumnNames: "id", referencedTableName: "review", referencesUniqueColumn: "false")
}

}

此时一切都“完美”,可以这么说,所以我运行grials dbm-update,因此更改被传输到数据库,但随后我想使这种关系成为双向关系,因此我使用“hasMany”更新我的游戏域'如下

class Game {
   static hasMany = [reviews: Review]
   String name
   String description
   Double price
}

对游戏域进行更改后,我继续运行grails dbm-gorm-diff fileupdated.groovy,这样我终于可以创建双向关系,但我得到一个空的迁移文件

databaseChangeLog = {
}

注意:即使将“hasMany”放在 Game 域的第一次迁移中,我也会收到相同的结果,但会创建关系 child-parent(reviews to game),但 parent-没有创建子(游戏到评论)。在我试图遵循的教程中,它确实有效。我使用的是 grails 2.4.4。

为什么没有创建 oneToMany 关系?

谢谢你,

efx

编辑:

-我创建一个如下所示的游戏示例并接收 id = 1

    groovy:000> g = new com.pluralsight.Game([description: 'Game Desc', name: 'The Game', price: '1.99'])
===> com.pluralsight.Game : (unsaved)
groovy:000> g.save()
===> com.pluralsight.Game : 1

-然后我继续为该游戏创建 2 条评论并收到评论 id 2 和 3

    groovy:000> r = new com.pluralsight.Review([game: g, reviewDate: new Date(), reviewText: 'Review 1'])
===> com.pluralsight.Review : (unsaved)
groovy:000> r.save()
===> com.pluralsight.Review : 2

groovy:000> r2 = new com.pluralsight.Review([game: g, reviewDate: new Date(), reviewText: 'Review 2'])
===> com.pluralsight.Review : (unsaved)
groovy:000> r2.save()
===> com.pluralsight.Review : 3

-现在,如果我的双向域一切正常,我应该能够从游戏中查询我的所有评论,但我得到一个空值

    groovy:000> retrieve = com.pluralsight.Game.get(1)
===> com.pluralsight.Game : 1
groovy:000> retrieve.reviews
===> null

因此我不确定为什么我的从游戏到评论的 oneToMany 不起作用

【问题讨论】:

  • 您必须从游戏的角度保存您的评论,方法是添加它:g.addToReviews(new Review(review:date new Date(), reviewText: 'Review 1')。然后当你保存游戏时,评论也会被保存:g.save()
  • @EmmanuelRosa 我得到一个错误,我猜想这与我在上面的最后一段代码中尝试从 Game 中检索评论时得到的 null 完全相同。 groovy:000> retrieve.addToReviews(new Review(reviewDate: new Date(), reviewText: 'Review 3')) ERROR org.codehaus.groovy.control.MultipleCompilationErrorsException: startup failed: groovysh_evaluate: 2: unable to resolve class Review @ line 2, column 23. retrieve.addToReviews(new Review(reviewDate: new Date(), reviewText: 'Review 3'))
  • 不,问题是在我的示例中,我根本没有使用完全限定的类名。试试:g.addToReviews(new com.pluralsight.Review(review:date new Date(), reviewText: 'Review 1')
  • @EmmanuelRosa 我收到以下错误groovy:000> retrieve.addToReviews(new com.pluralsight.Review(reviewDate: new Date(), reviewText: 'Review 3')) ERROR groovy.lang.MissingMethodException: No signature of method: com.pluralsight.Game.addToReviews() is applicable for argument types: (com.pluralsight.Review) values: [com.pluralsight.Review : (unsaved)] Possible solutions: getReviews() 如果游戏与评论之间没有关系,我认为它永远不会起作用。目前唯一的关系是从评论到游戏,反之则不然
  • 是的,你是对的。您需要从 GameReview 的关联。使用我的答案中的任何一个示例。 many 被添加到 one 中,而不管关联是单向的还是双向的。顺便说一句,请随时注册我的 Grails Noob Slack 社区(emmanuelrosa.com)。 StackOverflow 聊天很快就会开始,而且很糟糕。

标签: grails grails-orm


【解决方案1】:

您为什么期望数据库发生变化?

重要的是理解 Grails/GORM(和 Hibernate)在谈论双向关系时的含义。

当关系的两端都有对另一方的引用时,称为双向关系。所以Game 有一个Reviews 的集合,Review 有一个对Game 的引用。这只是在谈论应用程序代码。数据库只需要 1 个外键即可完全存储关系。

您也可以使用连接表,但在这种情况下(在我看来)这实在是太过分了。但是 Grails/GORM 允许它,只需在文档中查看如何在 static mapping 部分中映射它。

【讨论】:

  • 我希望有一种方法可以调用游戏域中的所有评论。让我更新一下我的例子,这样我就可以说明我的意思了
  • 我对您的编辑以及您对 Emmanuel Rosa 的反应感到有些困惑。看来您正在一个 groovy shell 中尝试 grails 代码。我认为这将无法正常工作。如果你想进行实验,你应该使用 grails 控制台来确保所有 grails/gorm 的“魔法”都能正常工作。 Emmanuel Rosa 给出的代码应该可以工作。我们有几个应用程序以相同的结构运行。请记住,hasMany 方是关系的所有者。对关系的所有更改都应该通过那一侧。
  • 我不明白为什么使用 shell 而不是控制台会给我带来问题,因为我读到(我可能误解了)控制台是 shell 的一个漂亮的 GUI,因此我使用的任何一个都应该给我同样的结果。
  • 你试过了吗,使用控制台?也许我错了,也许不是。我不熟悉使用 groovy shell 进行这样的实验。我总是会使用控制台或编写服务或控制器来测试这样的代码。
【解决方案2】:

如您所知,一对多关联有两种形式:单向和双向。两者都需要static hasMany = ...。只有在双向一对多关联的情况下才使用static belongsTo = ...

单向

class Game {
   String name
   String description
   Double price

   static hasMany = [reviews: Review]
}

class Review {
   String reviewText
   Date reviewDate    
}

双向

class Game {
   String name
   String description
   Double price

   static hasMany = [reviews: Review]
}

class Review {
   String reviewText
   Date reviewDate

   static belongsTo = [game: Game]
}

数据库实现

在单向一对多的情况下,您最终会得到三个表:gamereviewgame_reviewgame_review 表通过使用两列加入其他两个表:游戏 ID 和评论 ID。 gamereview 表保持隔离;它们只有通过game_review 表的关系。

在双向一对多中,您将获得两个表:gamereview。但是,review 表将包含一个外键列:游戏 ID。没有连接表保持gamereview 表彼此独立。

我希望这会有所帮助。您可以在我的articles 中了解更多信息。

【讨论】:

  • 我没有得到 game_review 表,如果这是我做双向配置后应该创建的表。我将更新问题以显示我的“问题”
  • 不,您将获得具有 单向 配置的 game_review 表。
【解决方案3】:

我要感谢 @hansboogards 和 @emmanuelrosa 抽出宝贵时间,我发现了一篇非常有趣的文章,关于休眠及其引起的头痛 GORM Gotchas 并找到了我无法从游戏域调用我的评论的原因是因为尽管我已经完成了保存,但休眠并没有将它们放在数据库中。

即使在 save() 之后(没有显式刷新),这也会打印空值。只有当您刷新会话时,子域实例才会设置其 ID。这与我们之前看到的多对一情况完全不同,在这种情况下,您不需要显式刷新即可将 Location 实例持久化到数据库!认识到差异的存在很重要,否则你会遇到困难。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-09-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-02-14
    相关资源
    最近更新 更多