GORM的关联
关联
belongs to和has one的区别就是一个是正向引用,一个是反向引用
- Has One 一对一的模型(比如1个user对应1个account)
- Has Many 一对多的模型(比如1个video对应多个comment)
- Many To Many 多对多模型(比如1个people会多个language 多个people也可以会同一个language) 比如一个视频网站 一个用户对应一个账号密码就是Has One 一个视频多个评论就是has many 一个视频可以对应多个tag,多个视频也可以同属于一个tag就是many to many
对于关联的思考 实际上如果需要频繁的使用关联性的操作,我们才应该进行表关联
Has One
type User struct{
ID uint
CreatedAt int
UpdatedAt int
Name string
Nid int
Account Account
}
type Account struct{
UserID int // 约定表名+ID为外键
Admin string
Password string
}
Has Many
type Video struct{
ID uint
CreatedAt int
UpdatedAt int
Link string
Pic string
Comment []Comment
}
type Comment struct{
VideoID uint // 约定表名+ID为外键
PubData int
Content string
PubUser string
}
Many To Tany
- 还需要一张关联表将两张表连接在一起
type Video struct{
ID uint
CreatedAt int
UpdatedAt int
Link string
Pic string
Tag []tag `gorm:"many2many:videos_tags"`
// 包含videos_tags数据表
// videos_tags包含video_id和tag_id外键
}
type Tag struct{
ID uint // 使用ID作为引用
Tagname string
}
关联表的创建
type User struct {
ID uint
Name string
Account Account
Videos []Video
Positions []Position `gorm:"many2many:users_positions"`
}
type Video struct { // 用户有多个视频 Has Many
UserID uint // 表名+ID为外键
CreatedAt int
UpdateAt int
Playurl string
Play int
}
type Account struct { // 用户只有一个账号 Has One
UserID uint // 表名+ID为外键
Admin string
Password string
}
type Position struct { // 用户有多个职能,多个用户又有同一个职能 Many To Many
ID uint // 使用ID作为引用
Duty string
}
// 创建数据表
db.AutoMigrate(&User{}, &Video{}, &Account{}, &Position{})
关联的创建
一对一和一对多只需要按照嵌入结构体的方式进行创建插入即可
多对多需要先查找判断是否有相应的关联,选择已有关联或再创造新的关联
如果该字段已经存在,即拿出相应id对于
如果不存在,则需要创建新的关联
一对多直接创建
多对多需要提前进行判断id
duty := []string{"update", "delete"}
positions := []Position{}
position := Position{}
for _,i := range duty {
if result := db.Where("Duty = ?", i).First(&position); result.Error != nil {
positions = append(positions, {Position{i})
} else {
positions = append(positions, position)
}
}
user2 := User{
Name: "Mark",
Account: Account{Admin: "admin", Password: "123456"},
// 一对多直接创建
Videos: []Video{
{Playurl: "video/mark", Play: 69847},
{Playurl: "video/aaa", Play: 14558},
},
// 多对多需要提前进行判断id
Positions: positions,
}
db.Create(&user2)
关联的增加
一对多的关联增加
如果希望使用gorm的实体关联操作,需要将ID设为外键
但是此时ID仍为主键,不能重复,所以还需要设置主键
name := "Tom" // 关联user表的name为Tom的数据
playurl := "video/abc"
play := 45865
user := User{}
if result := db.Where("name = ?", name).First(&user); result.Error != nil {
fmt.Println("find error")
} else {
db.Create(&Video{
Playurl: playurl,
Play: play,
UserID: user.ID, // 使用查询出来的user表的id
})
}
// 多对多关联的增加
name := "Mark"
duty := "new"
position := Position{}
user := User{}
if result:=db.Where("Duty = ?",duty).First(&position);result.Error != nil {
position := Position{Duty: duty}
db.Where("Name = ?", name).First(&user)
db.Model(&user).Association("Positions").Append(&position)
fmt.Println(user, position)
} else {
db.Where("Name = ?", name).First(&user)
// gorm给的association操作需要两个非空结构体
// 适用于
db.Model(&user).Association("Positions").Append(&position)
fmt.Println(user, position)
}
关联的查询
- 通过Preload预加载某一列
// 一对一查询
user := User{}
db.Preload("Account").Where("Name = ?", "Mark").First(&user)
// 一对多查询
user := User{}
db.Preload("Videos").Where("Name = ?", "Mark").First(&user)
// 多对多查询
position := Position{} // 从position查询users对应关系
db.Preload("Users").Where("Duty = ?", "updata").First(&position)
user := User{} // 从user查询positions对应关系
db.Preload("Positions").Where("Name = ?", "Tom").First(&user)
关联的删除
// 一对一和一对多直接选择删除数据段即可
// 多对多使用association
name := "Mark"
duty := "new"
position := Position{}
user := User{}
if result:=db.Where("Duty = ?",duty).First(&position);result.Error!=nil {
fmt.Println("find error")
} else {
db.Where("Name = ?", name).First(&user)
db.Model(&user).Association("Positions").Delete(&position)
fmt.Println(user, position)
}
总结
一对一和一对多,不需要中间表进行关联,只需要不让外键和主键重复即可 遇到crud只需要找到外键对应的id即可删除
多对多,带中间表,可以使用gorm自带的实体关联让操作更简洁方便的操纵关联表。