golang学习笔记(12)-gorm多对多关系建立与关联模式

多对多关联模式只与前几种关联模式有少许区别,本文着重的利用Many2Many关系学习并实验关联模式的相关操作,包括建立关系,更换关系等等。

目录

  • gorm中多对多关系
    • 准备工作
    • 建立Many2Many关系表
  • 关联模式
    • 添加关联
    • 查找关联
    • 替换关联
    • 删除关联
    • 清空关联
    • 关联计数
    • 带 Select 的删除

gorm中多对多关系

准备工作

建立数据库连接

import (
	"fmt"
	_ "github.com/go-sql-driver/mysql"
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
	"log"
)

var db *gorm.DB

func OpenDB() {
	dsn := "root:adss123@tcp(127.0.0.1:3306)/go_db?charset=utf8mb4&parseTime=True&loc=Local"
	res, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
	db = res
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("成功:%v\n", db)
}

建立Many2Many关系表

Many to Many 会在两个 model 中添加一张连接表。

例如,您的应用包含了 user 和 language,且一个 user 可以说多种
language,多个 user 也可以说一种 language。

// User 拥有并属于多种 language,`user_languages` 是连接表
type User struct {
  gorm.Model
  Languages []Language `gorm:"many2many:user_languages;"`
}

type Language struct {
  gorm.Model
  Name string
}

当使用 GORM 的 AutoMigrate 为 User 创建表时,GORM 会自动创建连接表


与其他关系中最大不同是,两个关系表中没有直接的外键字段来连接,而是通过 gorm:"many2many:tb1_tb2;" 标签,生成连接表,来创建联系。
实验案例如下

type Students struct {
	gorm.Model
	Name    string
	Classes []Classes `gorm:"many2many:students_classes;"`
}

type Classes struct {
	gorm.Model
	Name     string
	Students []Students `gorm:"many2many:students_classes;"`
}

func CreateMany2Many() {
	OpenDB()
	db.AutoMigrate(&Classes{})

}

发现在构造时,两个表相互嵌套的话,需要在每个嵌套字段后加上标签,不能单独在其中一个嵌套字段上加标签,另一个不加。
并且创建的时候,创建其中一个表,另一个表与连接表,都会自动创建。

关联模式

利用Association()方法建立两张表的关联,并通过Association下的方法经行关联操作。

添加关联

利用Append方法为 many to many、has many 添加新的关联;为 has one, belongs to 替换当前的关联

db.Model(&user).Association("Languages").Append([]Language{languageZH, languageEN})

db.Model(&user).Association("Languages").Append(&Language{Name: "DE"})

db.Model(&user).Association("CreditCard").Append(&CreditCard{Number: "411111111111"})

实验如下
首先创建两条记录,创建的过程省略,详细方法可以参考之前的利用gorm方法创建记录的笔记。
classes表
在这里插入图片描述
students表
在这里插入图片描述
students_classes表
在这里插入图片描述
此时两条信息未建立关系。
调用Association().Append()方法为两个记录创建关联。

func AssociationMany2Many() {
	OpenDB()
	class := &Classes{Model: gorm.Model{
		ID: 1,
	}}
	stu := &Students{Model: gorm.Model{
		ID: 1,
	}}
	db.Model(stu).Association("Classes").Append(class)
}

流程为建立id为1的student模型,在此模型下,连接Classes表,并与id为1的class记录创建模型。
在student表与class表上没有任何变化,但连接表students_classes表建立了一个关系记录
golang学习笔记(12)-gorm多对多关系建立与关联模式_第1张图片
有了关系后,尝试通过student查找其关联的class

func QueryMany2Many() {
	OpenDB()
	stu := &Students{}
	db.Preload("Classes").First(stu, 1)
	fmt.Println(stu)
}

在这里插入图片描述
成功查询。

查找关联

查找所有匹配的关联记录

db.Model(&user).Association("Languages").Find(&languages)

查找带条件的关联

codes := []string{"zh-CN", "en-US", "ja-JP"}
db.Model(&user).Where("code IN ?", codes).Association("Languages").Find(&languages)

db.Model(&user).Where("code IN ?", codes).Order("code desc").Association("Languages").Find(&languages)

相当于只查询出目标记录的关联信息
实验如下

func QueryMany2Many() {
	OpenDB()
	stu := &Students{Model: gorm.Model{
		ID: 1,
	}}
	class := &Classes{}
	db.Model(stu).Association("Classes").Find(class)
	fmt.Println(class)
}

在这里插入图片描述
添加条件测试
先为stu1增加一个Eng信息的记录关联
普通查询:

func QueryMany2Many() {
	OpenDB()
	stu := &Students{Model: gorm.Model{
		ID: 1,
	}}
	class := &[]Classes{}
	db.Model(stu).Association("Classes").Find(class)
	fmt.Println(class)
}

在这里插入图片描述
增加条件后:

func QueryMany2Many() {
	OpenDB()
	stu := &Students{Model: gorm.Model{
		ID: 1,
	}}
	class := &[]Classes{}
	db.Model(stu).Where("id=2").Association("Classes").Find(class)
	fmt.Println(class)
}

在这里插入图片描述
由此可以发现,where语句的条件是用来限制后面的Find方法的,而不是限制前面Model的建立的,展示的实验还不足以论证结果,但是本人私下尝试,删除构造的stu中的Id,改变where中的条件,让name指向stu中的name,等方法,均证明where条件是约束后面find方法,前面model的选择,似乎只能在创建对象的时候,指定好目标


替换关联

用一个新的关联替换当前的关联

db.Model(&user).Association("Languages").Replace([]Language{languageZH, languageEN})

db.Model(&user).Association("Languages").Replace(Language{Name: "DE"}, languageEN)

实验如下
如上文所述,现在stu1有两个关联的class,分别为math与eng,现在将增加一个class记录PE,将stu1的关联改为pe与eng。

func AssociationMany2Many() {
	OpenDB()
	stu := &Students{Model: gorm.Model{
		ID: 1,
	}}
	db.Model(stu).Association("Classes").Replace(&Classes{Name: "PE"})
}

在这里插入图片描述
与预期结果不符,发现如果Replace中只有一个参数的时候,方法会直接将所有关系全部替换为目标参数


尝试,有没有可能添加两个参数,前一个参数为被替换目标,后一个参数为替换目标

func AssociationMany2Many() {
	OpenDB()
	stu := &Students{Model: gorm.Model{
		ID: 1,
	}}
	db.Model(stu).Association("Classes").Replace(&Classes{Name: "math"}, &Classes{Name: "Eng"})
	QueryMany2Many()
}

在这里插入图片描述
失败,结果是两个参数全部被替换进去,发现无法指定替换。
似乎只能通过,指定删除关联,指定添加关联,来达到指定替换关联功能。

删除关联

如果存在,则删除源模型与参数之间的关系,只会删除引用,不会从数据库中删除这些对象。

db.Model(&user).Association("Languages").Delete([]Language{languageZH, languageEN})
db.Model(&user).Association("Languages").Delete(languageZH, languageEN)

实验代码
删除连接方法

func AssociationMany2Many() {
	OpenDB()
	stu := &Students{Model: gorm.Model{
		ID: 1,
	}}
	db.Model(stu).Association("Classes").Delete(&Classes{Model: gorm.Model{ID: 1}})
	QueryMany2Many()
}

注重实验指定替换关联方法。
在这里插入图片描述
现在stu1有两个关联信息,想把eng更换为pe。通过delete与append方法联合实现。

func AssociationMany2Many() {
	OpenDB()
	stu := &Students{Model: gorm.Model{
		ID: 1,
	}}
	db.Model(stu).Association("Classes").Delete(&Classes{Model: gorm.Model{ID: 2}})
	db.Model(stu).Association("Classes").Append(&Classes{Model: gorm.Model{ID: 3}})
	QueryMany2Many()
}

在这里插入图片描述
方法成功。

清空关联

删除源模型与关联之间的所有引用,但不会删除这些关联

db.Model(&user).Association("Languages").Clear()

关联计数

返回当前关联的计数

db.Model(&user).Association("Languages").Count()

// 条件计数
codes := []string{"zh-CN", "en-US", "ja-JP"}
db.Model(&user).Where("code IN ?", codes).Association("Languages").Count()

带 Select 的删除

// 删除 user 时,也删除 user 的 account
db.Select("Account").Delete(&user)

// 删除 user 时,也删除 user 的 Orders、CreditCards 记录
db.Select("Orders", "CreditCards").Delete(&user)

// 删除 user 时,也删除用户所有 has one/many、many2many 记录
db.Select(clause.Associations).Delete(&user)

// 删除 users 时,也删除每一个 user 的 account
db.Select("Account").Delete(&users)

你可能感兴趣的:(golang,学习,mysql)