【问题标题】:Cross dependencies issue: object references an unsaved transient instance save the transient instance before flushing交叉依赖问题:对象引用未保存的瞬态实例在刷新之前保存瞬态实例
【发布时间】:2015-08-13 05:21:49
【问题描述】:

似乎这个问题被一遍又一遍地问到我仍然找不到解决我的问题的答案。我在下面有一个域模型。我需要为每个新创建或更新的 Sec 用户确保它有一个配置文件,如果没有,则创建一个新的并分配给它。

配置文件的要求相同 - 每次保存期间检查它是否有工作区,如果没有,则创建一个。这样做是为了确保所有 Sec User-s 至少有一个工作空间。除了所有者本人之外,工作空间可能有不同的贡献者。

当我尝试保存 Sec 用户时,我总是得到:

object references an unsaved transient instance save the transient instance before flushing

总的来说,我确实了解问题的性质。构造函数中传递的实例没有持久化,因此没有 id。我仍然不知道如何强制这 3 个插入成为单个事务的一部分并正确处理引用。

class SecUser implements Serializable {

    String email

    static hasOne = [profile: Profile]

    def beforeValidate() {
        if (profile == null) {
            profile = new Profile(secUser: this)
        }
        profile.validate()
    }

}

class Profile {

    static belongsTo = [secUser: SecUser]

    static hasMany = [workspaces: Workspace]

    static constraints = {
        workspaces minSize: 1
    }

    def beforeValidate() {
        if (workspaces == null || workspaces.isEmpty()) {
            def workspace = new Workspace(owner: this).save()
            workspaces = [workspace]
        }
    }
}

class Workspace {

    String title = "Workspace ${10000 + new Random().nextInt(89999)}"

    static hasOne = [owner: Profile]

    static belongsTo = Profile

    static hasMany = [contributors: Profile]

}

更新 1

我换了

workspaces = [workspace]

this.addToWorkspaces(workspace)

现在我明白了:

ERROR spi.SqlExceptionHelper - 参照完整性约束 违规:“FK_MYFCABJIQ83AG2CLBDF61MPA5:PUBLIC.PROFILE_WORKSPACES FOREIGN KEY(WORKSPACE_ID) REFERENCES PUBLIC.WORKSPACE(ID) (0)"; SQL 语句:插入 profile_workspaces (profile_id, owner_id) 值 (?, ?) [23506-176]。

已生成一个包含 3 列的新连接表。我看到它试图进行错误的插入。它应该插入到 owner_id、workspace_id(profile_id 应该为空)

【问题讨论】:

  • 您应该调用this.addToWorkspaces(workspace) 而不是workspaces = [workspace]。您还应该在验证后保存配置文件。
  • 好的,现在我明白了:错误 spi.SqlExceptionHelper - 违反参照完整性约束:“FK_MYFCABJIQ83AG2CLBDF61MPA5: PUBLIC.PROFILE_WORKSPACES FOREIGN KEY(WORKSPACE_ID) REFERENCES PUBLIC.WORKSPACE(ID) (0)”; SQL 语句:插入 profile_workspaces (profile_id, owner_id) 值 (?, ?) [23506-176]。已生成一个新的连接表,它有 3 列。我看到它试图进行错误的插入。它应该插入到 owner_id、workspace_id @defectus
  • Profile 和 Workspace 之间有两种关联:多对多和一对一。多对多应该产生一个连接表,在您的情况下,它看起来像名为 profile_workspaces。 profile_workspaces 应该只有两列:profile_id 和workspace_id。我认为您在 Profile 和 Workspace 之间的一对一操作不起作用。您应该在拥有方(例如个人资料)上有一个 hasOne(),在拥有/从属方(例如工作区)上有一个属性(例如所有者)或 belongsTo()。就像 SecUser 和 Profile 之间的一对一一样。

标签: hibernate grails grails-orm


【解决方案1】:

当我处理涉及多个域对象的业务事务时,我创建了一个 Grails 服务。使用服务将简化域单元测试,它还有助于描述您的代码试图完成的任务。将其视为事务脚本设计模式(四人组)。

您的服务可以具有 create() 和 update() 方法,然后您可以从控制器中调用它们。然后,您的服务将根据需要连接 SecUser、Profile 和 Workspace。

【讨论】:

  • 这样你就有了服务。整个域仍然有交叉引用。你不能简单地保存没有关系的东西。这与 GoF 和设计模式无关。 @Emmanuel
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-11-08
  • 2015-09-27
  • 2021-03-03
  • 2013-11-30
  • 2017-04-12
  • 1970-01-01
相关资源
最近更新 更多