On this page
article
7.Go-GORM
Go-GORM介绍
1.什么是ORM?
orm是一种术语而不是软件
- orm英文全称object relational mapping,就是 ==对象映射关系== 程序;
- 简单来说类似python这种面向对象的程序来说一切皆对象,但是我们使用的数据库却都是关系型的;
- 为了保证一致的使用习惯,通过 orm将编程语言的对象模型和数据库的关系模型建立映射关系;
- 这样我们直接 使用编程语言的对象模型进行操作数据库 就可以了,而不用直接使用sql语言;
2.什么是GORM?
- 全功能 ORM (无限接近)
- 关联 (Has One, Has Many, Belongs To, Many To Many, 多态)
- 钩子 (在创建/保存/更新/删除/查找之前或之后)
- 预加载
- 事务
- 复合主键
- SQL 生成器
- 数据库自动迁移
- 自定义日志
- 可扩展性, 可基于 GORM 回调编写插件
- 所有功能都被测试覆盖
- 开发者友好
3.GORM(v2)基本使用
3.1安装
go get -u gorm.io/gorm
4.准备MySQL环境
4.1docker拉起一个mysql
docker run --name fly-mysql -e MYSQL_ROOT_PASSWORD=123456 -p 3306:3306 -d mysql:8.0
4.2创建数据库
create database test_db charset utf8mb4;
use test_db;
show tables;
4.3连接数据库
package main
import (
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
// 数据库初始化
func main() {
//定义db连接串
//parseTime=True&loc=Local
//parseTime查询结果示范自带解析为时间。查询数据时将mysql_datetime字段字段转成go_time.time
//loc是mysql的时区设置
dsn := "root:123456@tcp(127.0.0.1:3306)/test_db?charset=utf8mb4&parseTime=True&loc=Local"
//建立连接
mdb, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
panic(err)
}
}
4.4自动迁移表
package main
import (
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
// 1.表对应的struct
type User struct {
ID int64 `gorm:"primary_key"`
Username string
Password string
}
// 2.自定义表名
func (*User) TableName() string {
return "user_t"
}
// 数据库初始化
func main() {
dsn := "root:123456@tcp(127.0.0.1:3306)/test_db?charset=utf8mb4&parseTime=True&loc=Local"
//0.建立连接
mdb, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
panic(err)
}
//3.自带创建表,AutoMigrate没有建立映射关系
// 用于项目初始化自动创建表
mdb.AutoMigrate(User{})
}
5.增删改查
- 增
package main
import (
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
// 1.表对应的struct
type User struct {
ID int64 `gorm:"primary_key"`
Username string
Password string
}
// 2.自定义表名
func (*User) TableName() string {
return "user"
}
// 数据库初始化
func main() {
dsn := "root:123456@tcp(127.0.0.1:3306)/test_db?charset=utf8mb4&parseTime=True&loc=Local"
//0.建立连接
mdb, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
panic(err)
}
//3.自带创建表,AutoMigrate没有建立映射关系
// 用于项目初始化自动创建表,已存在不会重复创建
mdb.AutoMigrate(User{})
//新增
mdb.Create(&User{
//ID: 0, 自增属性不需要控制,所以注释掉
Username: "sky",
Password: "123456",
})
}
测试
fei@feideMBP 03crud % go run main.go
fei@feideMBP 03crud %
mysql> select * from user;
+----+----------+----------+
| id | username | password |
+----+----------+----------+
| 1 | sky | 123456 |
+----+----------+----------+
1 row in set (0.00 sec)
- 改
package main
import (
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
// 1.表对应的struct
type User struct {
ID int64 `gorm:"primary_key"`
Username string
Password string
}
// 2.自定义表名
func (*User) TableName() string {
return "user"
}
// 数据库初始化
func main() {
dsn := "root:123456@tcp(127.0.0.1:3306)/test_db?charset=utf8mb4&parseTime=True&loc=Local"
//0.建立连接
mdb, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
panic(err)
}
//3.自带创建表,AutoMigrate没有建立映射关系
// 用于项目初始化自动创建表,已存在不会重复创建
mdb.AutoMigrate(User{})
//新增 (必须接收指针类型)
mdb.Create(&User{
//ID: 0, 自增属性不需要控制,所以注释掉
Username: "sky02",
Password: "123456",
})
//修改
id := 1
//where 里面是原生的查询条件
mdb.Model(User{}).Where("id = ?", id).Update("username", "fei")
/*
等价于:
UPDATE user SET username='fei' WHERE id=1;
Model()传入User结构体,自动去找该结构体小写复数即users,如果指定了func (*User) TableName() string 方法则找方法中对应的表名。
其他写法
mdb.Model(User{ID: 1,}).Update("username", "fei")
mdb.Table("user").Where("id = ?", id).Update("username", "fei")
*/
}
测试
fei@feideMBP 03crud % go run main.go
fei@feideMBP 03crud %
mysql> select * from user;
+----+----------+----------+
| id | username | password |
+----+----------+----------+
| 1 | fei | 123456 |
| 2 | sky02 | 123456 |
+----+----------+----------+
2 rows in set (0.00 sec)
//将第一次加的sky改名成了fei
//第二次运行的create 又插入了一条数据
- 查
package main
import (
"gorm.io/driver/mysql"
"gorm.io/gorm"
"log"
)
// 1.表对应的struct
type User struct {
ID int64 `gorm:"primary_key"`
Username string
Password string
}
// 2.自定义表名
func (*User) TableName() string {
return "user"
}
// 数据库初始化
func main() {
dsn := "root:123456@tcp(127.0.0.1:3306)/test_db?charset=utf8mb4&parseTime=True&loc=Local"
//0.建立连接
mdb, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
panic(err)
}
//3.自带创建表,AutoMigrate没有建立映射关系
// 用于项目初始化自动创建表,已存在不会重复创建
mdb.AutoMigrate(User{})
//查询 第一种
u := &User{ID: 1}
mdb.First(u)
log.Printf("第一种查询写法:%v", u)
//查询 第二种
u2 := &User{}
mdb.Where("id = ?", 2).First(u2)
log.Printf("第二种查询写法:%v", u2)
//查询所有数据
users := []User{}
mdb.Find(&users)
log.Printf("查询全部:%v", users)
}
测试
fei@feideMBP 03crud % go run main.go
2024/10/05 22:47:25 第一种查询写法:&{1 fei 123456}
2024/10/05 22:47:25 第二种查询写法:&{2 sky02 123456}
2024/10/05 22:47:25 查询全部:[{1 fei 123456} {2 sky02 123456}]
- 删
package main
import (
"gorm.io/driver/mysql"
"gorm.io/gorm"
"log"
)
// 1.表对应的struct
type User struct {
ID int64 `gorm:"primary_key"`
Username string
Password string
}
// 2.自定义表名
func (*User) TableName() string {
return "user"
}
// 数据库初始化
func main() {
dsn := "root:123456@tcp(127.0.0.1:3306)/test_db?charset=utf8mb4&parseTime=True&loc=Local"
//0.建立连接
mdb, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
panic(err)
}
//3.自带创建表,AutoMigrate没有建立映射关系
// 用于项目初始化自动创建表,已存在不会重复创建
mdb.AutoMigrate(User{})
users := []User{}
mdb.Find(&users)
log.Printf("删除前查询全部:%v", users)
//删除,传入结构体表示在哪张表中删除
mdb.Where("id = ?", 1).Delete(&User{})
log.Printf("删除完成")
//查询所有数据
mdb.Find(&users)
log.Printf("删除后查询全部:%v", users)
}
测试
fei@feideMBP 03crud % go run main.go
2024/10/05 22:53:38 删除前查询全部:[{1 fei 123456} {2 sky02 123456}]
2024/10/05 22:53:38 删除完成
2024/10/05 22:53:38 删除后查询全部:[{2 sky02 123456}]
5.1错误处理
package main
import (
"gorm.io/driver/mysql"
"gorm.io/gorm"
"log"
)
// 1.表对应的struct
type User struct {
ID int64 `gorm:"primary_key"`
Username string
Password string
}
// 2.自定义表名
func (*User) TableName() string {
return "user"
}
// 数据库初始化
func main() {
dsn := "root:123456@tcp(127.0.0.1:3306)/test_db?charset=utf8mb4&parseTime=True&loc=Local"
//0.建立连接
mdb, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
panic(err)
}
//3.自带创建表,AutoMigrate没有建立映射关系
// 用于项目初始化自动创建表,已存在不会重复创建
mdb.AutoMigrate(User{})
users := []User{}
mdb.Find(&users)
log.Printf("查询全部:%v", users)
//错误处理演示
u2 := &User{}
tx := mdb.Where("id = ?", 1).First(u2)
//注意,tx.Error。查询出的数据为空也会算成err "record not found"
if tx.Error != nil {
log.Printf("查询报错")
}
//处理方式
if tx.Error != nil && tx.Error != gorm.ErrRecordNotFound {
log.Printf("查询报错2")
}
}
测试
fei@feideMBP 03crud % go run main.go
2024/10/05 23:03:24 查询全部:[{2 sky02 123456}]
2024/10/05 23:03:24 /Volumes/data/go/src/test/03crud/main.go:41 record not found
[1.660ms] [rows:0] SELECT * FROM `user` WHERE id = 1 ORDER BY `user`.`id` LIMIT 1
2024/10/05 23:03:24 查询报错
6.模型定义
6.1模型定义
- 模型一般都是普通的 Golang 的结构体,Go的基本数据类型,或者指针。
package main
import (
"gorm.io/driver/mysql"
"gorm.io/gorm"
"time"
)
// 不建议将字段属性放到结构体中定义
type User struct {
Id int64 `gorm:"primary_key" json:"id"`
Name string
CreatedAt *time.Time `json:"createdAt" gorm:"column:create_at"` //定义字段名
Email string `gorm:"type:varchar(100);unique_index"` // 唯一索引
Role string `gorm:"size:255"` //设置字段的大小为255个字节
MemberNumber *string `gorm:"unique;not null"` // 设置字段唯一,且不为空
Num int `gorm:"AUTO_INCREMENT"` // 设置 Num字段自增
Address string `gorm:"index:addr"` // 给Address 创建一个名字是 `addr`的索引
IgnoreMe int `gorm:"-"` //忽略这个字段
}
func main() {
dsn := "root:123456@tcp(127.0.0.1:3306)/test_db?charset=utf8mb4&parseTime=True&loc=Local"
//0.建立连接
mdb, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
panic(err)
}
//3.自带创建表,AutoMigrate没有建立映射关系
// 用于项目初始化自动创建表,已存在不会重复创建
mdb.AutoMigrate(User{})
}
测试
fei@feideMBP 04mode % go run main.go
fei@feideMBP 04mode %
mysql> desc users;
+---------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------------+--------------+------+-----+---------+----------------+
| id | bigint | NO | PRI | NULL | auto_increment |
| name | longtext | YES | | NULL | |
| create_at | datetime(3) | YES | | NULL | |
| email | varchar(100) | YES | | NULL | |
| role | varchar(255) | YES | | NULL | |
| member_number | varchar(191) | NO | UNI | NULL | |
| num | bigint | YES | | NULL | |
| address | varchar(191) | YES | MUL | NULL | |
+---------------+--------------+------+-----+---------+----------------+
8 rows in set (0.01 sec)
6.2支持结构标签
- 标签是声明模型时可选的标记。
标签 | 说明 |
---|---|
Column | 指定列的名称 |
Type | 指定列的类型 |
Size | 指定列的大小,默认是 255 |
PRIMARY_KEY | 指定一个列作为主键 |
UNIQUE | 指定一个唯一的列 |
DEFAULT | 指定一个列的默认值 |
PRECISION | 指定列的数据的精度 |
NOT NULL | 指定列的数据不为空 |
AUTO_INCREMENT | 指定一个列的数据是否自增 |
INDEX | 创建带或不带名称的索引,同名创建复合索引 |
UNIQUE_INDEX | 类似 索引 ,创建一个唯一的索引 |
EMBEDDED | 将 struct 设置为 embedded |
EMBEDDED_PREFIX | 设置嵌入式结构的前缀名称 |
- | 忽略这些字段 |
7.一对多
7.1has many
介绍
https://gorm.io/zh_CN/docs/has_many.html
has many
关联就是创建和另一个模型的一对多关系。不同于has one
,拥有者可以有零或多个关联模型。- 例如,例如每一个用户都拥有多张信用卡,这样就是生活中一个简单的一对多关系。
// 用户有多张信用卡,UserID 是外键
type User struct {
gorm.Model
CreditCards []*CreditCard
}
// 必须要写UserID这个外键属性
type CreditCard struct {
gorm.Model
Number string
UserID uint // 默认会在 CreditCard 表中生成 UserID 字段作为 与User表关联的外键ID
}
// mdb.AutoMigrate(User{}, CreditCard{})
7.2外键
- 为了定义一对多关系, 外键是必须存在的,默认外键的名字是所有者类型的名字加上它的主键(UserId) 。
- 就像上面的例子,为了定义一个属于 User 的模型,外键就应该为 UserID。
- 使用其他的字段名作为外键, 可以通过
foreignkey
来定制它。
type User struct {
gorm.Model
// foreignkey:UserRefer 可以自己指定外键关联字段名为:UserRefer
// 默认外键字段名是 UserId,你也可以自己修改
CreditCards []CreditCard `gorm:"foreignkey:UserRefer"`
}
type CreditCard struct {
gorm.Model
Number string
UserRefer uint
}
// mdb.AutoMigrate(User{}, CreditCard{})
7.3外键关联
- GORM通常使用所有者的主键作为外键的值,例如, User的ID,
- 当您将信用卡分配给用户时,GORM将保存用户的信用卡 ID 放入信用卡’ UserID 字段中。
- 您可以使用标签进行更改 references
type User struct {
ID uint `gorm:"primarykey"`
MemberNumber string `gorm:"type:varchar(255);unique"` //一对多关联需要唯一所以这里设置unique约束,否则mysql创建报错
// 默认CreditCard会使用User表的Id作为外键,references:MemberNumber 指定使用 MemberNumber 作为外键关联
CreditCards []CreditCard `gorm:"foreignkey:UserMember;references:MemberNumber"`
}
type CreditCard struct {
ID uint `gorm:"primarykey"`
Number string
UserMember string
}
// mdb.AutoMigrate(User{}, CreditCard{})
7.4一对多查询DEMO
package main
import (
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
/*
constraint:OnUpdate:CASCADE 【当User表更新,也会同步给CreditCards】 // 外键约束
OnDelete:SET NULL 【当User中数据被删除时,CreditCard关联设置为 NULL,不删除记录】
*/
type User struct {
gorm.Model
Username string `json:"username" gorm:"column:username"`
CreditCards []CreditCard `gorm:"constraint:OnUpdate:CASCADE,OnDelete:SET NULL;"`
}
type CreditCard struct {
gorm.Model
Number string
UserID uint
}
func main() {
dsn := "root:123456@tcp(127.0.0.1:3306)/test_db?charset=utf8mb4&parseTime=True&loc=Local"
//0.建立连接
mdb, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
panic(err)
}
//3.自带创建表,AutoMigrate没有建立映射关系
// 用于项目初始化自动创建表,已存在不会重复创建
mdb.AutoMigrate(User{}, CreditCard{})
// 1、创建一对多
user := User{
Username: "zhangsan",
CreditCards: []CreditCard{
{Number: "0001"},
{Number: "0002"},
},
}
mdb.Create(&user)
// 2、为已存在用户添加信用卡
u := User{Username: "zhangsan"}
mdb.First(&u)
//fmt.Println(u.Username)
mdb.Model(&u).Association("CreditCards").Append([]CreditCard{
{Number: "0003"},
})
}
mysql> select * from users;
+----+-------------------------+-------------------------+------------+----------+
| id | created_at | updated_at | deleted_at | username |
+----+-------------------------+-------------------------+------------+----------+
| 1 | 2024-10-06 04:43:29.747 | 2024-10-06 04:43:29.758 | NULL | zhangsan |
+----+-------------------------+-------------------------+------------+----------+
1 row in set (0.00 sec)
mysql> select * from credit_cards;
+----+-------------------------+-------------------------+------------+--------+---------+
| id | created_at | updated_at | deleted_at | number | user_id |
+----+-------------------------+-------------------------+------------+--------+---------+
| 1 | 2024-10-06 04:43:29.749 | 2024-10-06 04:43:29.749 | NULL | 0001 | 1 |
| 2 | 2024-10-06 04:43:29.749 | 2024-10-06 04:43:29.749 | NULL | 0002 | 1 |
| 3 | 2024-10-06 04:43:29.760 | 2024-10-06 04:43:29.760 | NULL | 0003 | 1 |
+----+-------------------------+-------------------------+------------+--------+---------+
3 rows in set (0.00 sec)
一对多 Association(不推荐) https://gorm.io/zh_CN/docs/associations.html
- 使用 Association 方法, 需要把 User 查询好, 然后根据 User 定义中指定的 AssociationForeignKey 去查找 CreditCard。
package main
import (
"encoding/json"
"fmt"
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
/*
constraint:OnUpdate:CASCADE 【当User表更新,也会同步给CreditCards】 // 外键约束
OnDelete:SET NULL 【当User中数据被删除时,CreditCard关联设置为 NULL,不删除记录】
*/
type User struct {
gorm.Model
Username string `json:"username" gorm:"column:username"`
CreditCards []CreditCard `gorm:"constraint:OnUpdate:CASCADE,OnDelete:SET NULL;"`
}
type CreditCard struct {
gorm.Model
Number string
UserID uint
}
func main() {
dsn := "root:123456@tcp(127.0.0.1:3306)/test_db?charset=utf8mb4&parseTime=True&loc=Local"
//0.建立连接
mdb, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
panic(err)
}
//3.自带创建表,AutoMigrate没有建立映射关系
// 用于项目初始化自动创建表,已存在不会重复创建
mdb.AutoMigrate(User{}, CreditCard{})
// 1、查找 用户名为 zhangsan 的所有信用卡信息
u := User{Username: "zhangsan"} // Association必须要先查出User才能关联查询对应的CreditCard
mdb.First(&u)
err = mdb.Model(&u).Association("CreditCards").Find(&u.CreditCards)
if err != nil {
fmt.Println(err)
}
strUser, _ := json.Marshal(&u)
fmt.Println(string(strUser))
}
fei@feideMBP 04mode % go run main.go
{
"ID": 1,
"CreatedAt": "2024-10-06T04:43:29.747+08:00",
"UpdatedAt": "2024-10-06T04:43:29.758+08:00",
"DeletedAt": null,
"username": "zhangsan",
"CreditCards": [{
"ID": 1,
"CreatedAt": "2024-10-06T04:43:29.749+08:00",
"UpdatedAt": "2024-10-06T04:43:29.749+08:00",
"DeletedAt": null,
"Number": "0001",
"UserID": 1
}
...
]
}
一对多 Preload https://gorm.io/zh_CN/docs/preload.html
- 使用 Preload 方法, 在查询 User 时先去获取 CreditCard 的记录。
package main
import (
"encoding/json"
"fmt"
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
/*
constraint:OnUpdate:CASCADE 【当User表更新,也会同步给CreditCards】 // 外键约束
OnDelete:SET NULL 【当User中数据被删除时,CreditCard关联设置为 NULL,不删除记录】
*/
type User struct {
gorm.Model
Username string `json:"username" gorm:"column:username"`
CreditCards []CreditCard `gorm:"constraint:OnUpdate:CASCADE,OnDelete:SET NULL;"`
}
type CreditCard struct {
gorm.Model
Number string
UserID uint
}
func main() {
dsn := "root:123456@tcp(127.0.0.1:3306)/test_db?charset=utf8mb4&parseTime=True&loc=Local"
//0.建立连接
mdb, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
panic(err)
}
//3.自带创建表,AutoMigrate没有建立映射关系
// 用于项目初始化自动创建表,已存在不会重复创建
mdb.AutoMigrate(User{}, CreditCard{})
// 1、预加载: 查找 user 时预加载相关 CreditCards
users := []User{}
mdb.Preload("CreditCards").Find(&users)
strUser, _ := json.Marshal(&users)
fmt.Println(string(strUser))
}
[{
"ID": 1,
"CreatedAt": "2024-10-06T04:43:29.747+08:00",
"UpdatedAt": "2024-10-06T04:43:29.758+08:00",
"DeletedAt": null,
"username": "zhangsan",
"CreditCards": [{
"ID": 1,
"CreatedAt": "2024-10-06T04:43:29.749+08:00",
"UpdatedAt": "2024-10-06T04:43:29.749+08:00",
"DeletedAt": null,
"Number": "0001",
"UserID": 1
},
...
]
}]
目的:
- Preload: 用于预加载关联数据,通常在查询时使用。
- Association: 用于管理已经加载模型的关联数据,包括增删改查。
使用场景:
- 当你需要一次性加载主模型及其关联数据时,使用 Preload。
- 当你需要对已经加载的模型的关联进行进一步操作时,使用 Association。
8.多对多
8.1many to many
介绍
https://gorm.io/zh_CN/docs/many_to_many.html
- Many to Many 会在两个 model 中添加一张连接表。
- 例如,您的应用包含了 user 和 language,且一个 user 可以说多种 language,多个 user 也可以说一种 language。
- 当使用 GORM 的 AutoMigrate 为 User 创建表时,GORM 会自动创建第三张连接表
// User 拥有并属于多种 language,`user_languages` 是连接表
type User struct {
gorm.Model
Languages []Language `gorm:"many2many:user_languages;"`
}
type Language struct {
gorm.Model
Name string
}
8.2反向连接
// User 拥有并属于多种 language,`user_languages` 是连接表
/*
gorm:"many2many:user_languages; 用于声明多对多关系,以及中间表的名称
*/
type User struct {
gorm.Model
Languages []*Language `gorm:"many2many:user_languages;"`
}
type Language struct {
gorm.Model
Name string
Users []*User `gorm:"many2many:user_languages;"`
}
8.3重写外键
- 对于 many2many 关系,连接表会同时拥有两个模型的外键
type User struct {
gorm.Model
Languages []Language `gorm:"many2many:user_languages;"`
}
type Language struct {
gorm.Model
Name string
}
// 连接表:user_languages
// 外键字段user_id,引用了users表的id字段
// foreign key: user_id, reference: users.id
// foreign key: language_id, reference: languages.id
- 若要重写它们,可以使用标签 foreignKey、references、joinforeignKey、joinReferences。
- 当然,您不需要使用全部的标签,你可以仅使用其中的一个重写部分的外键、引用。
type User struct {
gorm.Model
//foreignKey:Refer 指定本地表字段名,joinForeignKey:UserReferID 指定关联表字段名
Profiles []Profile `gorm:"many2many:user_profiles;foreignKey:Refer;joinForeignKey:UserReferID;References:UserRefer;joinReferences:ProfileRefer"`
Refer uint `gorm:"index:,unique"`
}
type Profile struct {
gorm.Model
Name string
UserRefer uint `gorm:"index:,unique"`
}
// 会创建连接表:user_profiles
// foreign key: user_refer_id, reference: users.refer
// foreign key: profile_refer, reference: profiles.user_refer
8.4自定义第三张表
https://gorm.io/zh_CN/docs/many_to_many.html#自定义连接表
type User struct {
gorm.Model
Languages []*Language `gorm:"many2many:user_languages"`
}
type Language struct {
gorm.Model
Name string
Users []*User `gorm:"many2many:user_languages"`
}
type UserLanguage struct {
UserID int `gorm:"primaryKey;"`
LanguageID int `gorm:"primaryKey;"`
CreatedAt time.Time
DeletedAt gorm.DeletedAt
}
//mdb.AutoMigrate(User{}, Language{}, UserLanguage{})
- 此时创建出的表经过测试没有外键约束
debug
2024/10/06 17:51:36 /Volumes/data/go/src/test/many2many/main.go:43
[37.939ms] [rows:0] CREATE TABLE `users` (`id` bigint unsigned AUTO_INCREMENT,`created_at` datetime(3) NULL,`updated_at` datetime(3) NULL,`deleted_at` datetime(3) NULL,PRIMARY KEY (`id`),INDEX `idx_users_deleted_at` (`deleted_at`))
2024/10/06 17:51:36 /Volumes/data/go/src/test/many2many/main.go:43
[35.273ms] [rows:0] CREATE TABLE `user_languages` (`user_id` bigint,`language_id` bigint,`created_at` datetime(3) NULL,`deleted_at` datetime(3) NULL,PRIMARY KEY (`user_id`,`language_id`))
2024/10/06 17:51:37 /Volumes/data/go/src/test/many2many/main.go:43
[37.921ms] [rows:0] CREATE TABLE `languages` (`id` bigint unsigned AUTO_INCREMENT,`created_at` datetime(3) NULL,`updated_at` datetime(3) NULL,`deleted_at` datetime(3) NULL,`name` longtext,PRIMARY KEY (`id`),INDEX `idx_languages_deleted_at` (`deleted_at`))
8.5多对多查询DEMO
package main
import (
"encoding/json"
"fmt"
"gorm.io/driver/mysql"
"gorm.io/gorm"
"gorm.io/gorm/logger"
"time"
)
type User struct {
gorm.Model
Languages []*Language `gorm:"many2many:user_languages"`
}
type Language struct {
gorm.Model
Name string
Users []*User `gorm:"many2many:user_languages"`
}
type UserLanguage struct {
UserID int `gorm:"primaryKey;"`
LanguageID int `gorm:"primaryKey;"`
CreatedAt time.Time
DeletedAt gorm.DeletedAt
}
func main() {
dsn := "root:123456@tcp(127.0.0.1:3306)/test_db?charset=utf8mb4&parseTime=True&loc=Local"
gormConfig := &gorm.Config{
Logger: logger.Default.LogMode(logger.Info), // 启用详细日志
}
//0.建立连接
mdb, err := gorm.Open(mysql.Open(dsn), gormConfig)
if err != nil {
panic(err)
}
//3.自带创建表,AutoMigrate没有建立映射关系
// 用于项目初始化自动创建表,已存在不会重复创建
mdb.AutoMigrate(User{}, Language{}, UserLanguage{})
//添加数据
user := &User{
Model: gorm.Model{},
Languages: []*Language{
{Name: "vue"},
{Name: "golang"},
},
}
mdb.Create(user)
/*
INSERT INTO `languages` (`created_at`,`updated_at`,`deleted_at`,`name`) VALUES ('2024-10-06 18:07:06.874','2024-10-06 18:07:06.874',NULL,'vue'),('2024-10-06 18:07:06.874','2024-10-06 18:07:06.874',NULL,'golang') ON DUPLICATE KEY UPDATE `id`=`id`
*/
//Preload获取数据
users := []*User{}
mdb.Preload("Languages").Find(&users)
userBytes, _ := json.Marshal(users)
fmt.Println(string(userBytes))
/*
input:
SELECT * FROM `users` WHERE `users`.`deleted_at` IS NULL
output:
[{"ID":1,"CreatedAt":"2024-10-06T18:07:06.871+08:00","UpdatedAt":"2024-10-06T18:07:06.871+08:00","DeletedAt":null,"Languages":[{"ID":1,"CreatedAt":"2024-10-06T18:07:06.874+08:00","UpdatedAt":"2024-10-06T18:07:06.874+08:00","DeletedAt":null,"Name":"vue","Users":null},{"ID":2,"CreatedAt":"2024-10-06T18:07:06.874+08:00","UpdatedAt":"2024-10-06T18:07:06.874+08:00","DeletedAt":null,"Name":"golang","Users":null}]}]
*/
//Preload获取单条数据
user = &User{
Model: gorm.Model{
ID: 1,
},
}
mdb.Preload("Languages").Find(&user)
userByte, _ := json.Marshal(user)
fmt.Println(string(userByte))
/*
input:
SELECT * FROM `users` WHERE `users`.`deleted_at` IS NULL AND `users`.`id` = 1
output:
{"ID":1,"CreatedAt":"2024-10-06T18:07:06.871+08:00","UpdatedAt":"2024-10-06T18:07:06.871+08:00","DeletedAt":null,"Languages":[{"ID":1,"CreatedAt":"2024-10-06T18:07:06.874+08:00","UpdatedAt":"2024-10-06T18:07:06.874+08:00","DeletedAt":null,"Name":"vue","Users":null},{"ID":2,"CreatedAt":"2024-10-06T18:07:06.874+08:00","UpdatedAt":"2024-10-06T18:07:06.874+08:00","DeletedAt":null,"Name":"golang","Users":null}]}
*/
user = &User{
Model: gorm.Model{
ID: 1,
},
}
mdb.Model(&user).Association("Languages").Find(&user.Languages)
userAssByte, _ := json.Marshal(user)
fmt.Println(string(userAssByte))
/*
input:
SELECT `languages`.`id`,`languages`.`created_at`,`languages`.`updated_at`,`languages`.`deleted_at`,`languages`.`name` FROM `languages` JOIN `user_languages` ON `user_languages`.`language_id` = `languages`.`id` AND `user_languages`.`user_id` = 1 WHERE `languages`.`deleted_at` IS NULL
output:
{"ID":1,"CreatedAt":"0001-01-01T00:00:00Z","UpdatedAt":"0001-01-01T00:00:00Z","DeletedAt":null,"Languages":[{"ID":1,"CreatedAt":"2024-10-06T18:07:06.874+08:00","UpdatedAt":"2024-10-06T18:07:06.874+08:00","DeletedAt":null,"Name":"vue","Users":null},{"ID":2,"CreatedAt":"2024-10-06T18:07:06.874+08:00","UpdatedAt":"2024-10-06T18:07:06.874+08:00","DeletedAt":null,"Name":"golang","Users":null}]}
*/
/*
解析:
Preload:
mdb.Preload("Languages"): 此方法用于指定在查找用户时,同时加载与用户相关联的 Languages 数据。
.Find(&user): 此方法从数据库中检索用户记录,并将结果填充到 user 变量中。
Association:
mdb.Model(&user): 这里的 mdb 表示 GORM 数据库连接实例,而 Model(&user) 指定了要操作的模型,即 user。
.Association("Languages"): 此方法用于访问与 user 关联的 Languages 集合。GORM 会根据模型中的定义自动处理这种关系。
.Find(&user.Languages): 这个调用会将与该用户相关的所有语言记录加载到 user.Languages 切片中。
*/
}
Last updated 06 Oct 2024, 19:17 +0800 .