在GORM中是否可以自动迁移具有循环关系的表?

w6lpcovy  于 2023-06-19  发布在  Go
关注(0)|答案(1)|浏览(144)

我一直试图在我们的Golang项目中实现GORM Orm,但似乎我有一个小问题。
其中一个结构体有一个循环依赖项,所以现在当我尝试AutoMigrate创建表时,我会得到错误,因为GORM试图按顺序创建表。
示例原型:

message Person{
  optional string name= 1;
  optional Company company = 2;
}

message Company{
  optional string name= 1;
  optional Workers workers= 2;
}

message Workers {
  optional string name= 1;
  optional Person person= 2;
}

这是一个简单的例子,但正是我的循环依赖。当我使用gorm插件生成proto时,它会生成带有所有gorm注解的模型,包括foregin键。当然,当我尝试自动迁移它们时,它会崩溃。
我找到的唯一解决办法就是
1.从“公司”中删除“员工”字段。
1.生成GORM模型。
1.运行autoMigrate。
1.重构proto文件并将Workers字段返回给Company。
1.运行autoMigrate。
1.我们所有的table都有合适的FK。
我试着在网上搜索任何想法,但似乎找不到任何想法。
任何帮助/想法都很感激!

uurv41yg

uurv41yg1#

我能够通过这种方法实现你所需要的。不幸的是,我不太熟悉proto消息,所以我只分享你应该使用的相对Go代码。如果我没有记错的话,您在proto消息中定义的关联将被转换为GORM中的belongsTo。否则,您应该使用repeated关键字(我说的对吗?).
在前提之后,我将分享代码,然后是解释。

package main

import (
    "github.com/samber/lo"
    "gorm.io/driver/postgres"
    "gorm.io/gorm"
)

type Person struct {
    ID        int
    Name      string
    CompanyID *int
    Company   *Company
}

func (p Person) TableName() string {
    return "people"
}

type Company struct {
    ID       int
    Name     string
    WorkerID *int
    Worker   *Worker
}

type Worker struct {
    ID       int
    Name     string
    PersonID *int
    Person   *Person
}

func main() {
    dsn := "host=localhost port=54322 user=postgres password=postgres dbname=postgres sslmode=disable"
    db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{
        DisableForeignKeyConstraintWhenMigrating: true,
    })
    if err != nil {
        panic(err)
    }

    db.AutoMigrate(&Person{}, &Company{}, &Worker{})
    db.Migrator().CreateConstraint(&Company{}, "Worker")
    db.Migrator().CreateConstraint(&Company{}, "fk_companies_people")
    db.Migrator().CreateConstraint(&Person{}, "Company")
    db.Migrator().CreateConstraint(&Person{}, "fk_people_companies")
    db.Migrator().CreateConstraint(&Worker{}, "Person")
    db.Migrator().CreateConstraint(&Worker{}, "fk_workers_people")

    db.Create(&Person{ID: 1, Name: "John", Company: &Company{ID: 1, Name: "ACME", Worker: &Worker{ID: 1, Name: "Worker 1"}}})
    db.Model(&Person{ID: 1}).Update("company_id", lo.ToPtr(1))
    db.Model(&Company{ID: 1}).Update("worker_id", lo.ToPtr(1))
    db.Model(&Worker{ID: 1}).Update("person_id", lo.ToPtr(1))

    // WRONG section!!!!!! uncomment any of these to try
    // db.Model(&Worker{ID: 1}).Update("person_id", lo.ToPtr(2)) // id "2" breaks as it doesn't exist
    // db.Model(&Person{ID: 1}).Update("company_id", lo.ToPtr(2)) // id "2" breaks as it doesn't exist
    // db.Model(&Company{ID: 1}).Update("worker_id", lo.ToPtr(2)) // id "2" breaks as it doesn't exist
}

好吧,让我带你浏览一下相关的部分。

structs定义

在这里,您必须预测每个关联都可能是NULL。这就是为什么我使用指针来定义它们的原因。因此,你可以创建一个像这样的循环依赖:
人员=>公司=>工人=>人员=>....
另外,我通过设置people覆盖了结构Person的表名。也许,GORM足够聪明,可以自己做到这一点,但从未尝试过。

SQL对象定义

当你示例化一个gorm客户端时,你必须确保在你迁移的时候没有创建外键。要实现这一点,必须在gorm.Config结构中将字段DisableForeignKeyConstraintWhenMigrating设置为true。因此,外键的创建取决于您。后者是通过CreateConstraint方法完成的,在该方法中指定:

  • 所涉及的表
  • 上一表涉及的关联
  • 如何命名外键约束

最后,您可以注意到,我运行AutoMigrate方法来创建没有外键的表。

写入逻辑

由于表的布局,INSERT逻辑必须分为两部分。在第一个中,您将记录插入到它们自己的表中(例如Personpeople表中,Companycompanies中,依此类推)。我们故意将外键留给NULL,否则我们会得到一个错误。如果相关的记录还没有被插入,第一个总是会引发一个错误。
然后,我们使用Update方法将每个外键设置为正确的值。

最后的想法

我在代码中留下了一些注解语句,以证明如果您试图将某个不存在的值指定为外键,它会中断。这意味着您可以在这些列中插入NULL或右值。
我使用这个软件包"github.com/samber/lo"来轻松地从一个字面量(例如:1)。
让我知道这是否有助于解决您的问题,谢谢!

相关问题