Bootstrap

gorm基础-5.一对多关系

我们先从一对多开始多表关系的学习

因为一对多的关系生活中到处都是

例如

老板与员工
女神和舔狗
老师和学生
班级与学生
用户与文章
...

一对多关系 表结构建立

在gorm中,官方文档是把一对多关系分为了两类,

Belongs To 属于谁

Has Many 我拥有的

他们本来是一起的,本教程把它们合在一起讲

我们以用户和文章为例

一个用户可以发布多篇文章,一篇文章属于一个用户

type User struct {
  ID       uint      `gorm:"size:4"`
  Name     string    `gorm:"size:8"`
  Articles []Article // 用户拥有的文章列表
}

type Article struct {
  ID     uint   `gorm:"size:4"`
  Title  string `gorm:"size:16"`
  UserID uint   // 属于   这里的类型要和引用的外键类型一致,包括大小
  User   User   // 属于
}

关于外键命名,外键名称就是关联表名+ID,类型是uint

重写外键关联

type User struct {
  ID       uint      `gorm:"size:4"`
  Name     string    `gorm:"size:8"`
  Articles []Article `gorm:"foreignKey:UID"` // 用户拥有的文章列表
}

type Article struct {
  ID    uint   `gorm:"size:4"`
  Title string `gorm:"size:16"`
  UID   uint   // 属于
  User  User   `gorm:"foreignKey:UID"` // 属于
}

这里有个地方要注意

我改了Article 的外键,将UID作为了外键,那么User这个外键关系就要指向UID

与此同时,User所拥有的Articles也得更改外键,改为UID

重写外键引用

type User struct {
  ID       uint      `gorm:"size:4"`
  Name     string    `gorm:"size:8"`
  Articles []Article `gorm:"foreignKey:UserName;references:Name"` // 用户拥有的文章列表
}

type Article struct {
  ID       uint   `gorm:"size:4"`
  Title    string `gorm:"size:16"`
  UserName string
  User     User `gorm:"references:Name"` // 属于
}

这一块的逻辑比较复杂

比如有1个用户

idname
1枫枫

之前的外键关系是这样表示文章的

idtitleuser_id
1python1
2javascript1
3golang1

如果改成直接关联Name,那就变成了这样

idtitleuser_name
1python枫枫
2javascript枫枫
3golang枫枫

虽然这样很方便,但是非常不适合在实际项目中这样用

我们还是用第一版的表结构做一对多关系的增删改查

一对多的添加

创建用户,并且创建文章

a1 := Article{Title: "python"}
a2 := Article{Title: "golang"}
user := User{Name: "枫枫", Articles: []Article{a1, a2}}
DB.Create(&user)

gorm自动创建了两篇文章,以及创建了一个用户,还将他们的关系给关联上了

创建文章,关联已有用户

a1 := Article{Title: "golang零基础入门", UserID: 1}
DB.Create(&a1)
var user User
DB.Take(&user, 1)
DB.Create(&Article{Title: "python零基础入门", User: user})

外键添加

给现有用户绑定文章

var user User
DB.Take(&user, 2)

var article Article
DB.Take(&article, 5)

user.Articles = []Article{article}
DB.Save(&user)

也可以用Append方法

var user User
DB.Take(&user, 2)

var article Article
DB.Take(&article, 5)

//user.Articles = []Article{article}
//DB.Save(&user)

DB.Model(&user).Association("Articles").Append(&article)

给现有文章关联用户

var article Article
DB.Take(&article, 5)

article.UserID = 2
DB.Save(&article)

也可用Append方法

var user User
DB.Take(&user, 2)

var article Article
DB.Take(&article, 5)

DB.Model(&article).Association("User").Append(&user)

查询

查询用户,显示用户的文章列表

var user User
DB.Take(&user, 1)
fmt.Println(user)

直接这样,是显示不出文章列表

预加载

我们必须要使用预加载来加载文章列表

var user User
DB.Preload("Articles").Take(&user, 1)
fmt.Println(user)

预加载的名字就是外键关联的属性名

查询文章,显示文章用户的信息

同样的,使用预加载

var article Article
DB.Preload("User").Take(&article, 1)
fmt.Println(article)

嵌套预加载

查询文章,显示用户,并且显示用户关联的所有文章,这就得用到嵌套预加载了

var article Article
DB.Preload("User.Articles").Take(&article, 1)
fmt.Println(article)

带条件的预加载

查询用户下的所有文章列表,过滤某些文章

var user User
DB.Preload("Articles", "id = ?", 1).Take(&user, 1)
fmt.Println(user)

这样,就只有id为1的文章被预加载出来了

自定义预加载

var user User
DB.Preload("Articles", func(db *gorm.DB) *gorm.DB {
  return db.Where("id in ?", []int{1, 2})
}).Take(&user, 1)
fmt.Println(user)

删除

级联删除

删除用户,与用户关联的文章也会删除

var user User
DB.Take(&user, 1)
DB.Select("Articles").Delete(&user)

清除外键关系

删除用户,与将与用户关联的文章,外键设置为null

var user User
DB.Preload("Articles").Take(&user, 2)
DB.Model(&user).Association("Articles").Delete(&user.Articles)

;