Golang中的ORM-Gorm的关联模型

Author Avatar
Yangzzz 7月 10, 2020
1.6k字 | 7分 |
  • 在其它设备中阅读本文章

Golang中的ORM-Gorm的关联模型

对于gorm的基础CRUD用法,这里就不论述了,这里主要说下关联模型的问题,因为我自己在查看官方文档进行关联模型操作的时候,总是感觉官方的例子很奇怪,用着很不明白。对于gorm的基础CRUD用法,不明白的可以看看官方文档:https://gorm.io/zh_CN/docs/models.html

创建数据库层面的外键:

models.MysqlHandler.Model(&models.Order{}).AddForeignKey("user_id", "user(id)", "RESTRICT", "RESTRICT")

说说这里的RESTRICT,这里还可以填CASCADE、NO ACTION、RESTRICT、SET NULL。分别的意思是:

CASCADE:父表delete、update的时候,子表会delete、update掉关联记录;

SET NULL:父表delete、update的时候,子表会将关联记录的外键字段所在列设为null,所以注意在设计子表时外键不能设为not null;

RESTRICT:如果想要删除父表的记录时,而在子表中有关联该父表的记录,则不允许删除父表中的记录;

NO ACTION:同 RESTRICT,也是首先先检查外键;

Belongs To

belongs to 关联建立一个和另一个模型的一对一连接,使得模型声明每个实例都属于另一个模型的一个实例 。

定义模型

例如,如果你的应用包含了用户和用户所属部门, 并且每一个用户所属只分配给一个用户。模型定义:

type User struct {
    gorm.Model
    Name string
}

// `Department` 属于 `User`, 外键是`UserID`
type Department struct {
    gorm.Model
    UserID uint
    User   User
    Name   string
}

生成的表结构:

外键

默认的外键使用所有者类型名称加上其主键。像上面的例子,为了声明一个模型属于 User,它的外键应该为 UserID。

GORM 提供了一个定制外键的方法,例如:

type User struct {
    gorm.Model
    Name string
}

type Department struct {
  gorm.Model
  Name      string
  User      User `gorm:"foreignkey:UserDepartment"` // 使用 UserRefer 作为外键
  UserDepartment uint
}

后面其他连接方式也一样

关联外键

GORM 默认使用所有者的主键作为外键值,在上面的例子中,就是 User 的 ID。当你分配一个资料给一个用户, GORM 将保存用户表的 ID 值 到 用户资料表的 UserID 字段里。你可以通过改变标签 association_foreignkey 外键值:

type User struct {
    gorm.Model
    Department uint
    Name string
}

type Department struct {
  gorm.Model
  Name      string
  User      User `gorm:"association_foreignkey:Refer"` // use Department 作为关联外键
  UserDepartment uint
}

后面其他连接方式也一样

Belongs To 的使用

这里就是重点了,这里我直接放代码:

type User struct {
    gorm.Model
    Name string
}

// `Department` 属于 `User`, 外键是`UserID`
type Department struct {
    gorm.Model
    UserID uint
    User   User
    Name   string
}
//这里的MysqlHandler就是gorm中的数据库操作DB
func TestGormBelongsTo(t *testing.T) {
    MysqlHandler = mysqlBuild()
    MysqlHandler.AutoMigrate(User{})
    MysqlHandler.AutoMigrate(Department{})

    //链表添加数据
    //profile := Profile{
    //    User: User{
    //        Name: "张三",
    //    },
    //    Name: "项目组",
    //}
    //MysqlHandler.Create(&profile)

    //链表查询
    //var infoList []Department
    //var user User
    //MysqlHandler.Preload("User").Find(&infoList)
    //for _, value := range infoList {
    //    fmt.Println(value.Name)
    //    fmt.Println(value.User.Name)
    //}

    // 查询添加条件 这里的条件就是 Department id为2 对应的数据
    //var info Department
    //info.ID = 2
    //MysqlHandler.Debug().Preload("User").Find(&info)
    //fmt.Println(info.Name, info.User.Name)

    //USER name为张三A 对应的数据
    //var info Department
    //MysqlHandler.Debug().Preload("User", func(query *gorm.DB) *gorm.DB {
    //    return query.Where("name =? ", "张三A")
    //}).First(&info)
    //fmt.Println(info.Name, info.User.Name, info.User.ID)
    //    or
    //var infoList []Department
    //MysqlHandler.Debug().Preload("User", "name =?","张三A").First(&infoList)
    //for _, value := range infoList {
    //    fmt.Println(value.Name, value.User.Name, value.User.ID)
    //}

    // 使用 Related 查找 belongs to 关系
    var department Department               //需要查找总的结构体
    department.ID = 3                       //定义查询条件
    MysqlHandler.Debug().First(&department) //首先查询总表
    /// SELECT * FROM `department`  WHERE `department`.`deleted_at` IS NULL AND `department`.`id` = 3 ORDER BY `department`.`id` ASC LIMIT 1
    MysqlHandler.Debug().Model(&department).Related(&department.User) //查询子表进行赋值
    /// SELECT * FROM `user`  WHERE `user`.`deleted_at` IS NULL AND ((`id` = 2))
    fmt.Println(department.Name, "---", department.User.Name, "---", department.ID, "---", department.User.ID)
}

由于gorm支持链式操作,后续需要什么操作 再往上加就行。

Has One

定义模型

//车
type Car struct {
    gorm.Model
    Host         string //车主人名字
    LicensePlate LicensePlate
}

//车牌
type LicensePlate struct {
    gorm.Model
    Number string //车牌号
    CarID  uint
}

操作

Has One的操作很Belongs To相同,可以自己实操一下。就明白了。

Belongs To跟Has One的区别

同样是一对一 Belongs To跟Has One的区别是什么呢?从两个结构体不难看出差别。区别: 外键属性存在位置不同,foreignKey 指定源不同,targetKey 指定源不同

Has Many

has many 关联就是创建和另一个模型的一对多关系, 不像 has one,所有者可以拥有0个或多个模型实例。

定义模型

如果你的业务数据库包含学校和专业, 并且每一个学校都拥有多门专业。

//学校
type School struct {
    gorm.Model
    Name       string
    Profession []Profession
}

//专业
type Profession struct {
    gorm.Model
    Name     string
    SchoolId uint
}

操作

func TestGormHasMany(t *testing.T) {
    MysqlHandler = mysqlBuild()
    MysqlHandler.AutoMigrate(School{})
    MysqlHandler.AutoMigrate(Profession{})

    //创建
    //profession1 := Profession{Name: "信息工程"}
    //profession2 := Profession{Name: "计算机科学"}
    //var professionList []Profession
    //professionList = append(professionList, profession1)
    //professionList = append(professionList, profession2)
    //school := School{
    //    Name:       "成都大学",
    //    Profession: professionList,
    //}
    //MysqlHandler.Save(&school) //or Create

    //查询单列
    //var school School
    //MysqlHandler.Preload("Profession").First(&school)
    //fmt.Println(school)

    //var school School
    //MysqlHandler.First(&school)
    //MysqlHandler.Model(&school).Related(&school.Profession)
    //fmt.Println(school)

    //查询多列
    var school []School
    //MysqlHandler.First(&school)
    MysqlHandler.Model(&school).Preload("Profession").Find(&school)
    for _, value := range school {
        fmt.Println(value.Name, value.Profession)
    }
}

Many To Many

多对多为两个模型增加了一个中间表。

定义模型

例如,如果你的应用包含用户和语言, 一个用户会说多种语言,并且很多用户会说一种特定的语言。

// 用户拥有并属于多种语言, 使用  `user_languages` 作为中间表
type User struct {
    gorm.Model
    Languages         []Language `gorm:"many2many:user_languages;"`
}

type Language struct {
    gorm.Model
    Name string
    Users               []User     `gorm:"many2many:user_languages;"`
}

操作

func TestGormManyToMany(t *testing.T) {
    MysqlHandler = mysqlBuild()
    MysqlHandler.AutoMigrate(Users{})
    MysqlHandler.AutoMigrate(Language{})

    //    创建
    //langEN := Language{Name: "EN"}
    //langCN := Language{Name: "CN"}
    //u1 := &Users{
    //    Name: "user1",
    //    Languages: []Language{
    //        langEN,
    //        langCN,
    //    },
    //}
    //MysqlHandler.Create(u1)

    //LangCN := Language{}
    //MysqlHandler.Where("name=?","CN").First(&LangCN)
    //u2 := &Users{
    //    Name: "user2",
    //    Languages: []Language{
    //        LangCN,
    //    },
    //}
    //MysqlHandler.Create(u2)

    //查询
    //获取 用户id 为 3 的 user 的语言:
    //var users Users
    //MysqlHandler.Find(&users, 3)
    //MysqlHandler.Model(&users).Related(&users.Languages, "Languages")


    //查询
    //获取 使用语言 为 CN 的 user:
    //var language Language
    //MysqlHandler.Find(&language, "name=?","CN")
    //MysqlHandler.Model(&language).Related(&language.Users, "Users")
    //fmt.Println(language)
}

参考连接:https://gorm.io/zh_CN/docs/

文章代码地址:https://github.com/myxy99/shopping/blob/master/test/gorm_test.go

本文使用CC BY-NC-SA 3.0 中国大陆协议许可
本文链接:https://myxy99.cn/posts/golang/54a7f546.html