📦GORMv1.25.6
我这里创建表自动加前缀是因为在连接开始就设置了
🏆 GORM中表与表的关系
🍅 一对一
🌟 Belongs to
Belongs to
会与另一个模型建立了一对一的连接。 这种模型的每一个实例都“属于”另一个模型的一个实例,也就是类中包含另外一个类比如这个用户拥有一个任务列表
要定义一个 Belongs to
关系,数据库的表中必须存在外键。默认情况下,外键的名字,使用拥有者的类型名称加上表的主键的字段名字
Belongs to
它主要在 子项
模型的角度来出发,建立 model
这里我定义了一个这个任务属于哪个用户的,这里的 UserID
就表示为一个隐式的外键,也就是逻辑外键
// User 创建结构体对应表
type User struct {
ID string `gorm:"primaryKey"`
CreatedAt time.Time
UpdatedAt time.Time
DeletedAt gorm.DeletedAt `gorm:"index"`
Name string
Password string `gorm:"not null"`
}
// Tasks 用户任务表
type Tasks struct {
gorm.Model
Title string `gorm:"not null;comment: 任务标题"`
Content string `gorm:"not null;comment: 任务内容"`
Status int `gorm:"not null;comment: 任务状态"`
UserID string //外键,如果不指定默认就是和主键
User User //表示属于的用户
}
此时就可以创建表了,这里的顺序是先创建 User
在创建 Tasks
,应为 Tasks
表需要依赖于 User
表
err = migrator.AutoMigrate(&model.User{}, &model.Tasks{})
⭐️ 在 GROM
中会自动创建、更新,也就是说如果你只创建 Tasks
表,但是它有依赖于 User
,他就会帮你自动也把 User Modle
也创建了
// User 创建结构体对应表
type User struct {
gorm.Model
Name string
Password string `gorm:"not null"`
}
// Tasks 用户任务表
type Tasks struct {
gorm.Model
Title string `gorm:"not null;comment: 任务标题"`
Content string `gorm:"not null;comment: 任务内容"`
Status int `gorm:"not null;comment: 任务状态"`
UserID string //外键,如果不指定默认就是和主键
User User //表示属于的用户
}
func main() {
// 连接数据库,这里的连接是被我封装的
db, err := untils.Dbuntils()
if err != nil {
println("连接数据库失败", err.Error())
}
println(db.Name())
err = db.AutoMigrate(&Tasks{})
}
此时在 Belongs to
中 User
可能并不知情有 Tasks
,只有 Tasks
知道它是属于哪个 User
,这就是 belong to
的关系,查看表结构
MariaDB [gorm]> desc gormuser;
+------------+---------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+---------------------+------+-----+---------+----------------+
| id | bigint(20) unsigned | NO | PRI | NULL | auto_increment |
| created_at | datetime(3) | YES | | NULL | |
| updated_at | datetime(3) | YES | | NULL | |
| deleted_at | datetime(3) | YES | MUL | NULL | |
| name | varchar(256) | YES | | NULL | |
| password | varchar(256) | NO | | NULL | |
+------------+---------------------+------+-----+---------+----------------+
6 rows in set (0.015 sec)
MariaDB [gorm]> desc gormtasks;
+------------+---------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+---------------------+------+-----+---------+----------------+
| id | bigint(20) unsigned | NO | PRI | NULL | auto_increment |
| created_at | datetime(3) | YES | | NULL | |
| updated_at | datetime(3) | YES | | NULL | |
| deleted_at | datetime(3) | YES | MUL | NULL | |
| title | varchar(256) | NO | | NULL | |
| content | varchar(256) | NO | | NULL | |
| status | bigint(20) | NO | | NULL | |
| user_id | bigint(20) unsigned | YES | | NULL | |
+------------+---------------------+------+-----+---------+----------------+
8 rows in set (0.014 sec)
1️⃣ 重新外键和引用
默认情况下外键的名字,使用拥有者的类型名称加上表的主键的字段名字,如果你想修改,就直接在结构体中修改
下面代码 foreignKey
用于指定当前模型(从表)中的外键字段,而 references
用于指定该外键字段所关联的主表中的字段。
foreignKey:UserName
表示 Tasks
结构体中的 UserName
字段是外键字段。
⚠️ 如果外键名恰好在拥有者类型中存在,
GORM
通常会错误的认为它是has one
关系。我们需要在belongs to
关系中指定references
references:Name
表示这个外键字段 UserName
关联到主表(User
结构体对应的表)中的 Name
字段。
// User 创建结构体对应表
type User struct {
ID string `gorm:"primaryKey"`
CreatedAt time.Time
UpdatedAt time.Time
DeletedAt gorm.DeletedAt `gorm:"index"`
Name string
Password string `gorm:"not null"`
}
// Tasks 用户任务表
type Tasks struct {
gorm.Model
Title string `gorm:"not null;comment: 任务标题"`
Content string `gorm:"not null;comment: 任务内容"`
Status int `gorm:"not null;comment: 任务状态"`
UserName string
User User `gorm:"foreignKey:UserName;references:Name"`
}
2️⃣关系创建
在创建 Tasks
时需要先创建 User
,应为它依赖于 User
//创建Task
user := User{
Name: "张三",
Password: "123456",
}
task := Tasks{
Title: "学习Gorm",
Content: "一对一,一对多关系",
Status: 0,
User: user,
}
db.Create(&task)
执行创建,可见 gormtasks
表和 gromuser
中都增加了数据
MariaDB [gorm]> select * from gormuser;
+----+-------------------------+-------------------------+------------+--------+----------+
| id | created_at | updated_at | deleted_at | name | password |
+----+-------------------------+-------------------------+------------+--------+----------+
| 1 | 2024-06-29 21:44:25.178 | 2024-06-29 21:44:25.178 | NULL | 张三 | 123456 |
+----+-------------------------+-------------------------+------------+--------+----------+
1 row in set (0.000 sec)
MariaDB [gorm]> select * from gormtasks;
+----+-------------------------+-------------------------+------------+------------+-----------------------------+--------+---------+
| id | created_at | updated_at | deleted_at | title | content | status | user_id |
+----+-------------------------+-------------------------+------------+------------+-----------------------------+--------+---------+
| 1 | 2024-06-29 21:44:25.200 | 2024-06-29 21:44:25.200 | NULL | 学习Gorm | 一对一,一对多关系 | 0 | 1 |
+----+-------------------------+-------------------------+------------+------------+-----------------------------+--------+---------+
1 row in set (0.000 sec)
此时你去查找一下这个任务表,查询需要一个接收对象,那么这个 tasks
中又包含了 user
,试一下查询 task
的 model
的时候可不可以将属于的 user
也查询出来
//接收对象
var task Tasks
//查询代码
db.First(&task, 1)
fmt.Println(task)
执行查看,会发现后面user属性那部分它是空的 {{0 0001-01-01 00:00:00 +0000 UTC 0001-01-01 00:00:00 +0000 UTC {0001-01-01 00:00:00 +0000 UTC false}} }
,这是因为,你创建表存在在关联是在软件中,也就是在模型中,查询数据库的时候,数据库也无法将关联也直接查询出来,需要 预加载
{{1 2024-06-29 21:44:25.2 +0800 CST 2024-06-29 21:44:25.2 +0800 CST {0001-01-01
00:00:00 +0000 UTC false}} 学习Gorm 一对一,一对多关系 0 1 {{0 0001-01-01 00:00:
00 +0000 UTC 0001-01-01 00:00:00 +0000 UTC {0001-01-01 00:00:00 +0000 UTC false}
} }}
使用预加载查询,预加载又两种方法一种是 Joins
一种是 Preload
//Joins
var task Tasks
db.Joins("User").First(&task, 1)
fmt.Println(task)
//Preload
var task Tasks
db.Preload("User").First(&task, 1)
fmt.Println(task)
//执行结果
{{1 2024-06-29 21:44:25.2 +0800 CST 2024-06-29 21:44:25.2 +0800 CST {0001-01-01
00:00:00 +0000 UTC false}} 学习Gorm 一对一,一对多关系 0 1 {{1 2024-06-29 21:44:
25.178 +0800 CST 2024-06-29 21:44:25.178 +0800 CST {0001-01-01 00:00:00 +0000 UT
C false}} 张三 123456}}
3️⃣外键约束
关联可以被看作是一个逻辑外键,所以可以直接在结构体中设置外键约束,你可以通过
constraint
标签配置 OnUpdate
、OnDelete
实现外键约束,这里设置了同步更新和删除后设置为NULL
⚠️ 注意如果在连接中开启了禁用外键,需要取消,如果要禁用需要注意以下几点:
- 数据库外键约束:数据库级别的外键约束是数据库管理系统(DBMS)用来保证数据完整性的一种机制。如果在外键约束被禁用的情况下,数据库不会自动检查或强制执行外键的引用完整性。
GORM
标签定义:在GORM
结构体中使用标签(例如gorm:"constraint:OnUpdate:CASCADE,OnDelete:SET NULL"
)定义外键行为,这些定义是给GORM
框架使用的,以便在 ORM 层面上理解和维护关系。这些标签定义了当主键更新或删除时,外键应如何响应。- 外键约束的生效:如果你在数据库级别禁用了外键约束,即使在
GORM
标签中定义了外键行为,数据库也不会自动执行这些行为。但是,GORM
可以使用这些标签来生成相应的 SQL 语句,以在应用层面上模拟这些行为。- 应用层面的模拟:当
GORM
执行创建、更新或删除操作时,它会根据结构体标签生成相应的 SQL 语句。例如,如果你定义了OnDelete: SET NULL
,GORM
会生成 SQL 语句来更新或删除相关记录。但是,这完全依赖于GORM
的操作,而不是数据库的自动约束。- 数据一致性:在没有数据库外键约束的情况下,维护数据一致性的负担落在了应用逻辑上。你需要确保应用逻辑正确处理了外键的级联更新或删除,否则可能会导致数据不一致。
- 性能和并发:在没有数据库外键约束的情况下,某些操作可能会获得更好的性能,因为数据库不需要检查外键约束。但是,这可能会牺牲数据的一致性,特别是在高并发的应用中。
- 事务管理:即使外键约束在数据库级别被禁用,你仍然可以使用
GORM
的事务管理功能来确保操作的原子性。
// User 创建结构体对应表
type User struct {
gorm.Model
Name string
Password string `gorm:"not null"`
}
// Tasks 用户任务表
type Tasks struct {
gorm.Model
Title string `gorm:"not null;comment: 任务标题"`
Content string `gorm:"not null;comment: 任务内容"`
Status int `gorm:"not null;comment: 任务状态"`
UserID string //外键,如果不指定默认就是和主键
User User `gorm:"constraint:OnUpdate:CASCADE,OnDelete:SET NULL;"` //表示属于的用户
}
测试一下
var task Tasks
db.Model(&Tasks{}).Preload("User").First(&task, 2)
fmt.Println("第一遍输出ID: ", task.UserID) //关联的是user的id为3的
db.Model(&User{}).Where("id = ?", 3).Update("id", "1")
db.Model(&Tasks{}).Preload("User").First(&task, 2)
fmt.Println("第二遍输出ID: ", task.UserID)
//输出
第一遍输出ID: 3
第二遍输出ID: 1
删除就不演示了,需要注意的是,由于 gorm
使用的是软删除,不会直接删除数据,导致外键无法及时更新,就需要使用到 unscoped
了
🌟 Has One
has one
与另一个模型建立一对一的关联,但它和 Belogns to
一对一关系有些许不同。 这种关联表明一个模型的每个实例都包含或拥有另一个模型的一个实例,在 belong to
中我设置的是一个任务它 属于
一个用户,而在 Has one
中它表示这个用户 拥有
一个任务
Has One
是在 父项
的视角出发的
// User 创建结构体对应表
type User struct {
gorm.Model
Name string `gorm:"unique;not null"`
Password string `gorm:"not null"`
Task Tasks //表示拥有一个Task
}
// Tasks 用户任务表
type Tasks struct {
gorm.Model
UserID uint //表示它属于谁
Title string `gorm:"not null;comment: 任务标题"`
Content string `gorm:"not null;comment: 任务内容"`
Status int `gorm:"not null;comment: 任务状态"`
}
使用 Has One
创建数据表的话,就不可以向 Belongs to
那样创建 子类
就可以将 父类
的表创建出来,反之也不行,所以在 has one
创建中最好老老实实直接创建两个数据表,且需要先创建 User
表在创建 Tasks
,因为在创建 task
表的时候时,由于关系需要,需要使用外键执行 user
表中的 id
func main() {
// 连接数据库
db, err := untils.Dbuntils()
if err != nil {
println("连接数据库失败", err.Error())
}
println(db.Name())
err = db.AutoMigrate(&User{}, &Tasks{})
}
查看这结构,此时 user
表内是没有任何 task
的数据的,但是 task
表内的 user_id
在 gorm
中是和 user
有关联的
MariaDB [gorm]> desc gormtasks;
+------------+---------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+---------------------+------+-----+---------+----------------+
| id | bigint(20) unsigned | NO | PRI | NULL | auto_increment |
| created_at | datetime(3) | YES | | NULL | |
| updated_at | datetime(3) | YES | | NULL | |
| deleted_at | datetime(3) | YES | MUL | NULL | |
| user_id | bigint(20) unsigned | YES | | NULL | |
| title | varchar(256) | NO | | NULL | |
| content | varchar(256) | NO | | NULL | |
| status | bigint(20) | NO | | NULL | |
+------------+---------------------+------+-----+---------+----------------+
8 rows in set (0.014 sec)
MariaDB [gorm]> desc gormuser;
+------------+---------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+---------------------+------+-----+---------+----------------+
| id | bigint(20) unsigned | NO | PRI | NULL | auto_increment |
| created_at | datetime(3) | YES | | NULL | |
| updated_at | datetime(3) | YES | | NULL | |
| deleted_at | datetime(3) | YES | MUL | NULL | |
| name | varchar(256) | NO | UNI | NULL | |
| password | varchar(256) | NO | | NULL | |
+------------+---------------------+------+-----+---------+----------------+
6 rows in set (0.013 sec)
MariaDB [gorm]>
1️⃣ 创建关系
在 belongs to
中如果创建 Task
就必须要有一个 user
对象在 Has one
也是一样的,或者直接通过 user
来创建(注意:User创建并不依赖于 Task
)
//Has one 创建关系
user := &User{
Name: "张三",
Password: "123456",
Task: Tasks{
Title: "学习ing...",
Content: "学习Go,Gin,Gore,Go-zero",
Status: 1,
//这里的UserID不需要指定它会自动写入当前创建用户的
},
}
db.Create(user)
同样的查找关系也是需要通过 Preload
或 Joins
来查询
//查询
var user User
db.Preload("Task").First(&user, 4)
fmt.Println(user)
//输出
{{4 2024-07-01 23:15:27.488 +0800 CST 2024-07-01 23:15:27.488 +0800 CST {0001-01-01 00:00:00 +0000 UTC false}
} 张三 123456 {{2 2024-07-01 23:15:27.512 +0800 CST 2024-07-01 23:15:27.512 +0800 CST {0001-01-01 00:00:00 +0
000 UTC false}} 4 学习ing... 学习Go,Gin,Gore,Go-zero 1}}
2️⃣ 重写外键和引用
同样也是重写外键的引用
// User 创建结构体对应表
type User struct {
ID string `gorm:"primaryKey"`
CreatedAt time.Time
UpdatedAt time.Time
DeletedAt gorm.DeletedAt `gorm:"index"`
Name string `gorm:"unique;not null"`
Password string `gorm:"not null"`
Task Tasks `gorm:"foreignKey: UserName;reference:name"` //表示拥有一个Task,且引用的是name字段
}
// Tasks 用户任务表
type Tasks struct {
gorm.Model
UserName string
Title string `gorm:"not null;comment: 任务标题"`
Content string `gorm:"not null;comment: 任务内容"`
Status int `gorm:"not null;comment: 任务状态"`
}
重写创建表,主要着重看tasks表,会发现多了一个 user_name
,但是 user_id
没有删除,在介绍 Belongs to
已经介绍过了
MariaDB [gorm]> DESC gormtasks;
+------------+---------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+---------------------+------+-----+---------+----------------+
| id | bigint(20) unsigned | NO | PRI | NULL | auto_increment |
| created_at | datetime(3) | YES | | NULL | |
| updated_at | datetime(3) | YES | | NULL | |
| deleted_at | datetime(3) | YES | MUL | NULL | |
| user_id | varchar(256) | YES | | NULL | |
| title | varchar(256) | NO | | NULL | |
| content | varchar(256) | NO | | NULL | |
| status | bigint(20) | NO | | NULL | |
| user_name | varchar(256) | YES | | NULL | |
+------------+---------------------+------+-----+---------+----------------+
9 rows in set (0.044 sec)
2️⃣ 自引用(套娃)
这里指定了一个经理,当然我觉得在创建一个经理 model
更加好一点
// User 创建结构体对应表
type User struct {
ID string `gorm:"primaryKey"`
CreatedAt time.Time
UpdatedAt time.Time
DeletedAt gorm.DeletedAt `gorm:"index"`
Name string `gorm:"unique;not null"`
Password string `gorm:"not null"`
Task Tasks `gorm:"foreignKey: UserName;reference:name"` //表示拥有一个Task,且引用的是name字段
ManagerID string
Manager *User //表示一个用户拥有一个上级
}
3️⃣ 外键约束
你可以通过为标签 constraint
配置 OnUpdate
、OnDelete
实现外键约束,在使用 GORM
进行迁移时它会被创建(Belongs to
也可以)
// User 创建结构体对应表
type User struct {
gorm.Model
Name string `gorm:"unique;not null"`
Password string `gorm:"not null"`
Task Tasks `gorm:"constraint:OnUpdate:CASCADE,OnDelete:SET NULL;"` //表示拥有一个Task,且引用的是name字段
}
// Tasks 用户任务表
type Tasks struct {
gorm.Model
UserID uint
Title string `gorm:"not null;comment: 任务标题"`
Content string `gorm:"not null;comment: 任务内容"`
Status int `gorm:"not null;comment: 任务状态"`
}
创建两个表的关联字段然后删除用户字段,查看 task
字段会不会改变
package main
import (
"GORM_STU/GORM_Connect/untils"
"fmt"
"gorm.io/gorm"
)
// User 创建结构体对应表
type User struct {
gorm.Model
Name string `gorm:"unique;not null"`
Password string `gorm:"not null"`
Task Tasks `gorm:"constraint:OnUpdate:CASCADE,OnDelete:SET NULL;"` //表示拥有一个Task,且引用的是name字段
}
// Tasks 用户任务表
type Tasks struct {
gorm.Model
UserID uint
Title string `gorm:"not null;comment: 任务标题"`
Content string `gorm:"not null;comment: 任务内容"`
Status int `gorm:"not null;comment: 任务状态"`
}
func main() {
// 连接数据库
db, err := untils.Dbuntils()
if err != nil {
println("连接数据库失败", err.Error())
}
println(db.Name())
err = db.AutoMigrate(&User{}, &Tasks{})
//Has one 创建关系
user := &User{
Name: "张三",
Password: "123456",
Task: Tasks{
Title: "学习ing...",
Content: "学习Go,Gin,Gore,Go-zero",
Status: 1,
},
}
db.Debug().Create(user)
//查询任务
var user2 User
db.Model(&User{}).Preload("Task").First(&user2, 1)
//删除用户
db.Debug().Unscoped().Delete(&user2)
//查询删除用户后的任务字段
var task Tasks
db.Debug().Model(&Tasks{}).First(&task)
fmt.Println(task)
}
🍅 一对多
🌟 Has Many
has many
与另一个模型建立了一对多的连接,拥有者可以拥有零个或者多个关联模型
比如用户和任务,一个用户可以拥有多个任务
// User 创建结构体对应表
type User struct {
gorm.Model
Name string `gorm:"unique;not null"`
Password string `gorm:"not null"`
Task []Tasks //拥有多个用户
}
// Tasks 用户任务表
type Tasks struct {
gorm.Model
UserID string //这里需要也是user_id,默认及是主键
Title string `gorm:"not null;comment: 任务标题"`
Content string `gorm:"not null;comment: 任务内容"`
Status int `gorm:"not null;comment: 任务状态"`
}
创建数据库,这里的创建方式和 has one
一样需要先创建 User
表在创建 Tasks
表
// 连接数据库
db, err := untils.Dbuntils()
if err != nil {
println("连接数据库失败", err.Error())
}
println(db.Name())
err = db.AutoMigrate(&User{}, &Tasks{})
在查看一下表结构,会发现和 has one
一模一样的,在表结构相同,但是在 gorm
中,user
类可以拥有多个 tasks
MariaDB [gorm]> desc gormuser;
+------------+---------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+---------------------+------+-----+---------+----------------+
| id | bigint(20) unsigned | NO | PRI | NULL | auto_increment |
| created_at | datetime(3) | YES | | NULL | |
| updated_at | datetime(3) | YES | | NULL | |
| deleted_at | datetime(3) | YES | MUL | NULL | |
| name | varchar(256) | NO | UNI | NULL | |
| password | varchar(256) | NO | | NULL | |
+------------+---------------------+------+-----+---------+----------------+
6 rows in set (0.016 sec)
MariaDB [gorm]> desc gormtasks;
+------------+---------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+---------------------+------+-----+---------+----------------+
| id | bigint(20) unsigned | NO | PRI | NULL | auto_increment |
| created_at | datetime(3) | YES | | NULL | |
| updated_at | datetime(3) | YES | | NULL | |
| deleted_at | datetime(3) | YES | MUL | NULL | |
| user_id | bigint(20) unsigned | YES | MUL | NULL | |
| title | varchar(256) | NO | | NULL | |
| content | varchar(256) | NO | | NULL | |
| status | bigint(20) | NO | | NULL | |
+------------+---------------------+------+-----+---------+----------------+
8 rows in set (0.013 sec)
1️⃣ 创建关联关系
这里的逻辑和 Has one
一样,只不过 Tasks
数量变多了
//Has Many创建关系
user := &User{
Name: "张三",
Password: "123456",
Task: []Tasks{{
Title: "学习ing...",
Content: "学习Go,Gin,Gore,Go-zero",
Status: 1,
},
{
Title: "运动ing...",
Content: "跑步、跳绳、羽毛球",
Status: 1,
}},
}
db.Debug().Create(user)
查询和列出和 user
有关联的数据
var user User
db.Preload("Task").First(&user, 1)
fmt.Println(user)
//输出
{{1 2024-07-02 23:03:51.133 +0800 CST 2024-07-02 23:03:51.133 +0800 CST {0001-01
-01 00:00:00 +0000 UTC false}} 张三 123456 [{{1 2024-07-02 23:03:51.154 +0800 CS
T 2024-07-02 23:03:51.154 +0800 CST {0001-01-01 00:00:00 +0000 UTC false}} 1 学
习ing... 学习Go,Gin,Gore,Go-zero 1} {{2 2024-07-02 23:03:51.154 +0800 CST 202
4-07-02 23:03:51.154 +0800 CST {0001-01-01 00:00:00 +0000 UTC false}} 1 运动ing.
.. 跑步、跳绳、羽毛球 1}]}
它的各种操作都和 Has one
差不多
🍅 多对多
🌟 Many to Many
Many to Many
不同于前面两种关系,它在两个模型之间建立一个关联表,拿文档举例吧,一个人可以编写多个文档,多个人也可以编写一个文档
// User 创建结构体对应表
type User struct {
gorm.Model
Name string `gorm:"unique;not null"`
Password string `gorm:"not null"`
Doc []Docs `gorm:"many2many:user_doc"` // 多对多关系,使用many2many:user_doc 表示中间表
}
// Docs 用户任务表
type Docs struct {
gorm.Model
Title string `gorm:"not null;comment: 文章Title"`
User []User `gorm:"many2many:user_doc"` // 多对多关系,使用many2many:user_doc 表示中间表
}
创建表
db, err := untils.Dbuntils()
if err != nil {
println("连接数据库失败", err.Error())
}
println(db.Name())
err = db.AutoMigrate(&User{}, &Docs{})
创建完成后会发现有三个表,其中一个为关联表
MariaDB [gorm]> show tables;
+----------------+
| Tables_in_gorm |
+----------------+
| gormdoc |
| gormuser |
| gormuser_doc |
+----------------+
3 rows in set (0.001 sec)
查看表结构
MariaDB [gorm]> desc gormuser;
+------------+---------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+---------------------+------+-----+---------+----------------+
| id | bigint(20) unsigned | NO | PRI | NULL | auto_increment |
| created_at | datetime(3) | YES | | NULL | |
| updated_at | datetime(3) | YES | | NULL | |
| deleted_at | datetime(3) | YES | MUL | NULL | |
| name | varchar(256) | NO | UNI | NULL | |
| password | varchar(256) | NO | | NULL | |
+------------+---------------------+------+-----+---------+----------------+
6 rows in set (0.040 sec)
MariaDB [gorm]> desc gormdoc;
+------------+---------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+---------------------+------+-----+---------+----------------+
| id | bigint(20) unsigned | NO | PRI | NULL | auto_increment |
| created_at | datetime(3) | YES | | NULL | |
| updated_at | datetime(3) | YES | | NULL | |
| deleted_at | datetime(3) | YES | MUL | NULL | |
| user_id | varchar(256) | YES | | NULL | |
| title | varchar(256) | NO | | NULL | |
+------------+---------------------+------+-----+---------+----------------+
6 rows in set (0.040 sec)
MariaDB [gorm]> desc gormuser_doc;
+---------+---------------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+---------+---------------------+------+-----+---------+-------+
| user_id | bigint(20) unsigned | NO | PRI | NULL | |
| doc_id | bigint(20) unsigned | NO | PRI | NULL | |
+---------+---------------------+------+-----+---------+-------+
2 rows in set (0.039 sec)
MariaDB [gorm]>
查看关联表的 sql
语句
CREATE TABLE `gormuser_doc` (
`user_id` bigint(20) unsigned NOT NULL,
`doc_id` bigint(20) unsigned NOT NULL,
PRIMARY KEY (`user_id`,`doc_id`),
KEY `fk_gormuser_doc_doc` (`doc_id`),
CONSTRAINT `fk_gormuser_doc_doc` FOREIGN KEY (`doc_id`) REFERENCES `gormdoc` (`id`),
CONSTRAINT `fk_gormuser_doc_user` FOREIGN KEY (`user_id`) REFERENCES `gormuser` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
1️⃣ 创建关联关系
package main
import (
"GORM_STU/GORM_Connect/untils"
"gorm.io/gorm"
)
// User 创建结构体对应表
type User struct {
gorm.Model
Name string `gorm:"unique;not null"`
Password string `gorm:"not null"`
Doc []Docs `gorm:"many2many:user_doc"` // 多对多关系,使用many2many:user_doc 表示中间表
}
// Docs 用户任务表
type Docs struct {
gorm.Model
Title string `gorm:"not null;comment: 文章Title"`
User []User `gorm:"many2many:user_doc"` // 多对多关系,使用many2many:user_doc 表示中间表
}
func main() {
// 连接数据库
db, err := untils.Dbuntils()
if err != nil {
println("连接数据库失败", err.Error())
}
println(db.Name())
err = db.AutoMigrate(&User{}, &Docs{})
//
////Many to Many创建关系
u1 := &User{
Name: "tanc",
Password: "1234567",
Doc: []Docs{
{Title: "Gorm学习笔记"},
{Title: "Go基础学习笔记"},
},
}
d1 := &Docs{
Title: "学习Gin",
User: []User{
*u1,
{Name: "zs", Password: "123456"},
},
}
//创建用户
db.Debug().Create(u1)
//创建Doc
db.Debug().Create(d1)
}
创建后查看关联表即可,这里表示为 1和2的doc
可以被 1号用户
操作,3号的doc
,可以被 1号和二号用户操作
MariaDB [gorm]> select * from gormuser_doc;
+---------+---------+
| docs_id | user_id |
+---------+---------+
| 1 | 1 |
| 2 | 1 |
| 3 | 1 |
| 3 | 2 |
+---------+---------+
4 rows in set (0.001 sec)
2️⃣ 自定义连接表
gorm自动创建的连接表,只有两个表的主键,没有其他信息,在有些情况下不能满足需求,就可以自定义连接表
自定义 连接表
可以是一个功能齐全的模型,比如支持 软删除
、钩子函数
功能,并且可以具有更多字段
注意这里有一个小小的坑那就是,在连接表中你的主键字段需要和关联的结构体名字相同,比如
User
结构体,在自定义连接表中就需要定义UserID
,而不是UsersID
// User 创建结构体对应表
type User struct {
gorm.Model
Name string `gorm:"unique;not null"`
Password string `gorm:"not null"`
Doc []Docs `gorm:"many2many:user_doc"` // 多对多关系,使用many2many:user_doc 表示中间表
}
// Docs 用户任务表
type Docs struct {
gorm.Model
Title string `gorm:"not null;comment: 文章Title"`
User []User `gorm:"many2many:user_doc"` // 多对多关系,使用many2many:user_doc 表示中间表
}
// UserDoc 自定义连接表
type UserDoc struct {
UserID uint `gorm:"primaryKey"`
DocsID uint `gorm:"primaryKey"`
CreatedAt time.Time
DeletedAt gorm.DeletedAt
}
func main() {
// 连接数据库
db, err := untils.Dbuntils()
if err != nil {
println("连接数据库失败", err.Error())
}
//创建关联
err = db.SetupJoinTable(&User{}, "Tasks", &UserDoc{})
if err != nil {
println("创建关联失败", err.Error())
}
err = db.SetupJoinTable(&Task{}, "Users", &UserDoc{})
if err != nil {
println("创建关联失败", err.Error())
}
//创建数据表
err = db.AutoMigrate(&User{}, &Task{}, &UserDoc{})
if err != nil {
println("创建数据表失败", err.Error())
}
}
创建后查看连接表结构
MariaDB [gorm]> desc gormuser_doc;
+------------+---------------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+------------+---------------------+------+-----+---------+-------+
| user_id | bigint(20) unsigned | NO | PRI | NULL | |
| docs_id | bigint(20) unsigned | NO | PRI | NULL | |
| created_at | datetime(3) | YES | | NULL | |
| deleted_at | datetime(3) | YES | | NULL | |
+------------+---------------------+------+-----+---------+-------+
4 rows in set (0.004 sec)
sql
语句
CREATE TABLE `gormuser_doc` (
`user_id` bigint(20) unsigned NOT NULL,
`docs_id` bigint(20) unsigned NOT NULL,
`created_at` datetime(3) DEFAULT NULL,
`deleted_at` datetime(3) DEFAULT NULL,
PRIMARY KEY (`user_id`,`docs_id`),
KEY `fk_gormuser_doc_docs` (`docs_id`),
CONSTRAINT `fk_gormuser_doc_docs` FOREIGN KEY (`docs_id`) REFERENCES `gormdocs` (`id`),
CONSTRAINT `fk_gormuser_doc_user` FOREIGN KEY (`user_id`) REFERENCES `gormuser` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
3️⃣ 自定义主键
有时候默认组件ID是无法满足需求的,那就可以自定义主键
- 使用
foreingKey
来定义结构体外键 - 使用
joinForeignKey
来定义外键连接字段 - 使用
References
来定义关联结构体的外键字段 - 使用
joinReferences
来定义关联表单中的外键字段
// User 创建结构体对应表
type User struct {
gorm.Model
Name string `gorm:"unique;not null;index:,unique"`
Password string `gorm:"not null"`
Doc []Docs `gorm:"many2many:user_doc;foreignKey: Name;joinForeignKey:UserName;References: Title;JoinReferences: DocsName;"` // 多对多关系,使用many2many:user_doc 表示中间表
}
// Docs 用户任务表
type Docs struct {
gorm.Model
Title string `gorm:"not null;comment: 文章Title;index:,unique"`
User []User `gorm:"many2many:user_doc;foreignKey: Title;joinForeignKey:DocsName;References: Name;JoinReferences:UserName;"` // 多对多关系,使用many2many:user_doc 表示中间表
}
// UserDoc 自定义连接表
type UserDoc struct {
UserName string `gorm:"primaryKey;"`
DocsName string `gorm:"primaryKey"`
CreatedAt time.Time
DeletedAt gorm.DeletedAt
}
创建表,主要是查看自定义连接表中的代码
CREATE TABLE `gormuser_doc` (
`user_name` varchar(256) NOT NULL,
`docs_name` varchar(256) NOT NULL,
`created_at` datetime(3) DEFAULT NULL,
`deleted_at` datetime(3) DEFAULT NULL,
PRIMARY KEY (`user_name`,`docs_name`),
KEY `fk_gormuser_doc_docs` (`docs_name`),
CONSTRAINT `fk_gormuser_doc_docs` FOREIGN KEY (`docs_name`) REFERENCES `gormdocs` (`title`),
CONSTRAINT `fk_gormuser_doc_user` FOREIGN KEY (`user_name`) REFERENCES `gormuser` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
🍅关联标签(Association Tags)
GORM中的关联标签通常用于指定如何处理模型之间的关联。 这些标签定义了一些关系细节,比如外键,引用和约束。 理解这些标签对于有效地建立和管理模型之间的关系而言至关重要。
标签 | 描述 |
---|---|
foreignKey |
指定用作连接表中外键的当前模型的列名。 |
references |
指示连接表的外键映射到的引用表中的列名。 |
polymorphic |
定义多态类型,通常是模型名称。 |
polymorphicValue |
设置多态值,通常是表名,如果没有另行指定。 |
many2many |
命名多对多关系中使用的连接表。 |
joinForeignKey |
标识连接表中映射回当前模型表的外键列。 |
joinReferences |
指向与参考模型表相链接的连接表中的外键列。 |
constraint |
为关联指定关系约束 OnUpdate ,如。OnDelete |