GORM的关联操作| 青训营

90 阅读3分钟

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自带的实体关联让操作更简洁方便的操纵关联表。