GORM 一对多关系 HasMany

一对多关系 Has Many部分

官方文档
has many 与另一个模型建立了一对多的连接。 不同于 has one,拥有者可以有零或多个关联模型。

老板与员工
女神和舔狗
老师和学生
班级与学生
用户与文章…

  例如,您的应用包含 user 和 article 模型,且每个 user 可以有多篇 article。

声明

  user为主表,由于一对多,在user结构中增加article切片;
  article表为副表,在其中加入外键关联,即主表名+ID,再加入user结构体、

// User 用户表 一个用户可以有多篇文章
type User struct {
	ID       uint   `gorm:"size:4"`
	Name     string `gorm:"size:8"`
	Articles []Article
}

// Article 文章表 一篇文章属于一个用户
type Article struct {
	ID     uint   `gorm:"size:4"`
	Title  string `gorm:"size:16"`
	UserID uint   `gorm:"size:4"`
	User   User
}
//使用automigrate进行表的迁移
DB.AutoMigrate(&User{}, &Article{})

  默认的外键名是拥有者的类型名加上其主键字段名,这里外键名称就是关联表名+ID,不加备注不可以随便更改,会报错;且外键类型要和主表关联的字段类型一直,这里都为unit,size:4
可以得到建立的表的关系如图:

在这里插入图片描述

重写外键关联

  想要使用另一个字段作为外键,您可以使用 foreignKey 标签自定义它:

//重写外键关联----------------------------
//gorm的foreignKey备注写在对应的两个表的关联上
type User1 struct {
	ID       uint       `gorm:"size:4"`
	Name     string     `gorm:"size:8"`
	Articles []Article1 `gorm:"foreignKey:UID"`
}

type Article1 struct {
	ID    uint   `gorm:"size:4"`
	Title string `gorm:"size:16"`
	UID   uint   `gorm:"size:4"`
	User  User1  `gorm:"foreignKey:UID"`
}

关联外键的数据类型仍需一致,但是字段名不再受限制

重写引用

这里博主大失败,最后也没有搞出来,希望过路大神解答
这里贴官方文档

  GORM 通常使用拥有者的主键作为外键的值。 对于上面的例子,它是 User 的 ID 字段。
  为 user 添加 credit card 时,GORM 会将 user 的 ID 字段保存到 credit card 的 UserID 字段。
  同样的,您也可以使用标签 references 来更改它,例如:

//重写引用----------------------------
//备注写在对应的两个表的关联上

type User2 struct {
	ID       uint       `gorm:"size:4"`
	Name     string     `gorm:"size:8"`
	Articles []Article2 `gorm:"foreignKey:UserName;references:Name"`
}

type Article2 struct {
	ID       uint   `gorm:"size:4"`
	Title    string `gorm:"size:16"`
	UserName string `gorm:"size:8"`
	User     User2  `gorm:"references:Name"`
}

报错信息

Error 1170 (42000): BLOB/TEXT column ‘company_id’ used in key specification without a key length

一对多的添加

//创建用户的同时创建文章,并将两者关联
DB.Save(&User{
	Name: "wang2",
	Articles: []Article{
		{Title: "golang"},
		{Title: "python"},
	},
})

//这里save、create方法都可以
//创建文章,关联已有用户
//1.直接传入关联外键部分
DB.Save(&Article{Title: "easyGo", UserID: 2})

//2.查询过后传入对应结构体中
var user User
DB.Take(&user, 1)                           //查询已有用户
DB.Save(&Article{Title: "c++", User: user}) //将关联部分的User结构体传入

外键添加

常规方法

// 常规方法
//常规方法-----------------------将id为8的文章和id为2的用户绑定
//现有用户,这里id=2
var user User
DB.Take(&user, 2)

//现有文章,id=8,未设置用户
var article Article
DB.Take(&article, 8)

//方法1.给现有用户绑定文章
user.Articles = []Article{article}//[{8 c语言 2 {0  []}}]
DB.Save(&user)

//方法2.给现有文章关联用户
article.User = user //{2 wang2 []}
DB.Save(&article)

append方法

association方法中为关联二表的部分,这里为articels和User

//append方法--------将id为8的文章和id为2的用户绑定
var user User
DB.Take(&user, 2)
var article Article
DB.Take(&article, 8)
//1.用户绑定文章
//model在选表的同时也在选择对象
//DB.Model(&User{ID: 2}).Association("Articles").Append(&article)
DB.Model(&user).Association("Articles").Append(&article)
//2.文章关联用户
DB.Model(&article).Association("User").Append(&user)

前文user、article定义部分

查询、预加载

  GORM 可以通过 Preload 预加载 has many 关联的记录,查看 预加载 获取详情
  预加载的名字就是外键关联的属性名,大小写、复数形式敏感

//查询 不加载,无法查看
var userList []User
DB.Find(&userList)
fmt.Println(userList) 
//[{1 wang []} {2 wang2 []}]

user = User{}
//查询 预加载
DB.Preload("Articles").Take(&user, 1)
fmt.Println(user) 
//{1 wang [{1 golang 1 {0  []}} {2 python 1 {0  []}} {5 c++ 1 {0  []}}]}

嵌套预加载

//嵌套预加载 结构体中内容再展示一层
DB.Preload("Articles.User").Take(&user, 1)
fmt.Println(user)
//{1 wang [{1 golang 1 {1 wang []}} {2 python 1 {1 wang []}} {5 c++ 1 {1 wang []}}]}

查询文章,显示用户,并且显示用户关联的所有文章

带条件的预加载

//带条件的预加载
//这里只预加载id=1的文章
DB.Preload("Articles", "id=?", 1).Take(&user) //
fmt.Println(user)//{1 wang [{1 golang 1 {0  []}}]}

自定义预加载等更深入内容请查看官方文档

删除

级联删除

删除用户,与用户相关的文章也清除

//级联删除
user = User{}
DB.Preload("Articles").Take(&user, 1)
DB.Select("Articles").Delete(&user)

清楚外键关系

将与用户关联的文章,外键设置为null;删除用户
用户作为主表中数据无法直接删除

// 将id=2的用户的文章与其断开关系,并将用户删除
user = User{}
DB.Preload("Articles").Take(&user, 2)
DB.Model(&user).Association("Articles").Delete(&user.Articles)
DB.Delete(&user)

所有代码集合:

package main

import (
	"fmt"
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
	"gorm.io/gorm/logger"
)

var DB *gorm.DB

func init() {
	username := "root"
	password := "123456"
	host := "127.0.0.1"
	port := 3306
	Dbname := "gorm"
	timeout := "10s"

	dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8mb4&parseTime=True&loc=Local&timeout=%s", username, password, host, port, Dbname, timeout)
	db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
		Logger: logger.Default.LogMode(logger.Info),
	})
	if err != nil {
		fmt.Println("连接数据库失败, error=", err)
		return
	}
	DB = db
	fmt.Println("数据库连接成功")
}

//表结构关联-----------------------
// User 用户表 一个用户可以有多篇文章

type User struct {
	ID       uint   `gorm:"size:4"`
	Name     string `gorm:"size:8"`
	Articles []Article
}

// Article 文章表 一篇文章属于一个用户
type Article struct {
	ID     uint   `gorm:"size:4"`
	Title  string `gorm:"size:16"`
	UserID uint   `gorm:"size:4"`
	User   User
}

//重写外键关联----------------------------
//gorm的foreignKey备注写在对应的两个表的关联上
//
//type User1 struct {
//	ID       uint       `gorm:"size:4"`
//	Name     string     `gorm:"size:8"`
//	Articles []Article1 `gorm:"foreignKey:UID"`
//}
//
//type Article1 struct {
//	ID    uint   `gorm:"size:4"`
//	Title string `gorm:"size:16"`
//	UID   uint   `gorm:"size:4"`
//	User  User1  `gorm:"foreignKey:UID"`
//}

//重写引用----------------------------
//备注写在对应的两个表的关联上

//type User2 struct {
//	ID       uint       `gorm:"size:4"`
//	Name     string     `gorm:"size:8"`
//	Articles []Article2 `gorm:"foreignKey:UserName;references:Name"`
//}
//
//type Article2 struct {
//	ID       uint   `gorm:"size:4"`
//	Title    string `gorm:"size:16"`
//	UserName string `gorm:"size:8"`
//	User     User2  `gorm:"references:Name"`
//}

func main() {
	//DB.AutoMigrate(&User{}, &Article{})
	//DB.AutoMigrate(&User1{}, &Article1{})
	//DB.AutoMigrate(&User2{}, &Article2{})

	创建用户的同时创建文章,并将两者关联
	//DB.Save(&User{
	//	Name: "wang2",
	//	Articles: []Article{
	//		{Title: "golang"},
	//		{Title: "python"},
	//	},
	//})

	创建文章,关联已有用户
	1.直接传入关联外键部分
	//DB.Save(&Article{Title: "easyGo", UserID: 2})
	//
	2.查询过后传入对应结构体中
	//var user User
	//DB.Take(&user, 1)                           //查询已有用户
	//DB.Save(&Article{Title: "c++", User: user}) //将关联部分的User结构体传入

	//外键添加
	//常规方法-----------------------将id为8的文章和id为2的用户绑定
	//现有用户,这里id=2
	//var user User
	//DB.Take(&user, 2)

	//现有文章,id=8,未设置用户
	//var article Article
	//DB.Take(&article, 8)

	//方法1.给现有用户绑定文章
	//user.Articles = []Article{article}//[{8 c语言 2 {0  []}}]
	//DB.Save(&user)

	//方法2.给现有文章关联用户
	//article.User = user //{2 wang2 []}
	//DB.Save(&article)
	//-------------------------------------
	//append方法--------将id为8的文章和id为2的用户绑定
	var user User
	DB.Take(&user, 2)
	var article Article
	DB.Take(&article, 8)
	//1.用户绑定文章
	//model在选表的同时也在选择对象
	//DB.Model(&User{ID: 2}).Association("Articles").Append(&article)
	DB.Model(&user).Association("Articles").Append(&article)
	//2.文章关联用户
	DB.Model(&article).Association("User").Append(&user)

	//查询 不加载,无法查看
	var userList []User
	DB.Find(&userList)
	fmt.Println(userList) //[{1 wang []} {2 wang2 []}]

	user = User{}
	//查询 预加载
	DB.Preload("Articles").Take(&user, 1)
	fmt.Println(user) //{1 wang [{1 golang 1 {0  []}} {2 python 1 {0  []}} {5 c++ 1 {0  []}}]}

	user = User{}
	//嵌套预加载 结构体中内容再展示一层
	DB.Preload("Articles.User").Take(&user, 1)
	fmt.Println(user)
	//{1 wang [{1 golang 1 {1 wang []}} {2 python 1 {1 wang []}} {5 c++ 1 {1 wang []}}]}

	user = User{}
	//带条件的预加载
	//这里只预加载id=1的文章
	DB.Preload("Articles", "id=?", 1).Take(&user) //
	fmt.Println(user)
	//{1 wang [{1 golang 1 {0  []}}]}

	// 将id=2的用户的文章与其断开关系,并将用户删除
	user = User{}
	DB.Preload("Articles").Take(&user, 2)
	DB.Model(&user).Association("Articles").Delete(&user.Articles)
	DB.Delete(&user)

	//级联删除
	user = User{}
	DB.Preload("Articles").Take(&user, 1)
	DB.Select("Articles").Delete(&user)
}

  • 9
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
GORM是一个Go语言的ORM库,支持很多数据库,包括MySQL、PostgreSQLSQLite等等。 在GORM中,一对多关系可以通过定义结构体中的slice来实现。例如,一个班级有多个学生,班级和学生之间就是一对多关系。可以这样定义: ```go type Class struct { gorm.Model Name string Students []Student } type Student struct { gorm.Model Name string ClassID uint } ``` 在上面的代码中,`Class`结构体中有一个`Students`字段,类型为`[]Student`,表示班级中有多个学生。而`Student`结构体中有一个`ClassID`字段,类型为`uint`,表示学生属于哪个班级。通过这种方式,就可以实现一对多关系。 多对多关系也可以通过定义结构体中的slice来实现。例如,一个学生可以选择多个课程,一个课程也可以被多个学生选择,学生和课程之间就是多对多关系。可以这样定义: ```go type Student struct { gorm.Model Name string Courses []Course `gorm:"many2many:student_courses;"` } type Course struct { gorm.Model Name string Students []Student `gorm:"many2many:student_courses;"` } ``` 在上面的代码中,`Student`结构体中有一个`Courses`字段,类型为`[]Course`,表示学生选择了哪些课程。而`Course`结构体中有一个`Students`字段,类型为`[]Student`,表示选择了该课程的学生。需要注意的是,在结构体的`gorm`标签中,需要指定`many2many`关系的表名,这里是`student_courses`。 以上就是在GORM中实现一对多和多对多关系的方式。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值