一对多关系
一对多关系
我们先从一对多开始多表关系的学习
因为一对多的关系生活中到处都是
例如
老板与员工
女神和舔狗
老师和学生
班级与学生
用户与文章
...
一对多关系 表结构建立
在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 `gorm:"size:4"` // 属于 这里的类型要和引用的外键类型一致,包括大小
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 `gorm:"size:4"` // 属于
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"` // 属于
}CopyErrorOK!
比如有1个用户
id | name |
1 | 枫枫 |
之前的外键关系是这样表示文章的
id | title | user_id |
1 | python | 1 |
2 | javascript | 1 |
3 | golang | 1 |
如果改成直接关联Name,那就变成了这样
id | title | user_name |
1 | python | 枫枫 |
2 | javascript | 枫枫 |
3 | golang | 枫枫 |
虽然这样很方便,但是非常不适合在实际项目中这样用
我们还是用第一版的表结构做一对多关系的增删改查
一对多的添加
创建用户,并且创建文章
a1 := Article{Title: "python"}
a2 := Article{Title: "golang"}
user := User{Name: "枫枫", Articles: []Article{a1, a2}}
DB.Create(&user)
// 或者
DB.Create(&User{
Name: "lcx",
Articles: []Article{
{Title: "Python"},
{Title: "Golang"},
},
})
gorm自动创建了两篇文章,以及创建了一个用户,还将他们的关系给关联上了
创建文章,关联已有用户
DB.Create(&Article{
Title: "Java",
UserID: 1,
})
// 会创建一个张三的用户并绑定
DB.Create(&Article{
Title: "PHP",
User: User{
Name: "张三",
},
})
// 给现有的用户绑定文章
var user User
DB.Take(&user, 2)
DB.Create(&Article{
Title: "C##",
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, 1)
var article Article
DB.Take(&article, 6)
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)
// {1 lcx []}
直接这样,是显示不出文章列表
预加载
我们必须要使用预加载来加载文章列表
var user User
DB.Preload("Articles").Take(&user, 1)
fmt.Println(user)CopyErrorOK!
预加载的名字就是外键关联的属性名
查询文章,显示文章用户的信息
同样的,使用预加载
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)