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

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

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表建立了一个关系记录
在这里插入图片描述
有了关系后,尝试通过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)
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
GoLang学习笔记主要包括以下几个方面: 1. 语法规则:Go语言要求按照语法规则编写代码,例如变量声明、函数定义、控制结构等。如果程序中违反了语法规则,编译器会报错。 2. 注释:Go语言中的注释有两种形式,分别是行注释和块注释。行注释使用`//`开头,块注释使用`/*`开头,`*/`结尾。注释可以提高代码的可读性。 3. 规范代码的使用:包括正确的缩进和空白、注释风格、运算符两边加空格等。同时,Go语言的代码风格推荐使用行注释进行注释整个方法和语句。 4. 常用数据结构:如数组、切片、字符串、映射(map)等。可以使用for range遍历这些数据结构。 5. 循环结构:Go语言支持常见的循环结构,如for循环、while循环等。 6. 函数:Go语言中的函数使用`func`关键字定义,可以有参数和返回值。函数可以提高代码的重用性。 7. 指针:Go语言中的指针是一种特殊的变量,它存储的是另一个变量的内存地址。指针可以实现动态内存分配和引用类型。 8. 并发编程:Go语言提供了goroutine和channel两个并发编程的基本单位,可以方便地实现多线程和高并发程序。 9. 标准库:Go语言提供了丰富的标准库,涵盖了网络编程、文件操作、加密解密等多个领域,可以帮助开发者快速实现各种功能。 10. 错误处理:Go语言中的错误处理使用`defer`和`panic`两个关键字实现,可以有效地处理程序运行过程中出现的错误。 通过以上内容的学习,可以掌握Go语言的基本语法和编程思想,为进一步学习和应用Go语言打下坚实的基础。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [Golang学习笔记](https://blog.csdn.net/weixin_52310067/article/details/129467041)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* *3* [golang学习笔记](https://blog.csdn.net/qq_44336275/article/details/111143767)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值