GORM 指南 | GORM - The fantastic ORM library for Golang, aims to be developer friendly.
一 对多入门
比如要开发cmdb的系统,无论是硬件还是软件。硬件对应的就是对应的哪个开发在用。或者服务对应的是哪个业务模块在使用,或者应用谁在使用。那么这就是一对多的关系。
has many介绍
- has many 关联就是创建和另一个模型的一对多关系(数据库里面是一对多,然后struct里面也是一对多)
- 例如, 例如每一个用户都拥有多张信用卡,这样就是生活中一个简单的一对多关系
在设计表的时候不可能将所有的信息都放在一个表里面,那么表就会非常非常的宽。这样字段就会非常的多,性能就会受到影响。
在设计的时候这里其实就可以设置为两张表。一个是用户的详情表和信用卡的详情表,用户表里面加上卡的id和他做一个关联,那么这就是一对多的关系。
当一个数据库存储了很多数据的时候就需要分库分表,上面也类似,将一张表分为两张表。
creditcar的外键是userid。
package main
import (
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
type User struct {
gorm.Model //默认创建三个时间字段和一个id字段,create_at update_at delete_at这样可以知道创建时间/更新时间/删除时间
CreditCards []*CreditCard66
}
//用户有多张信用卡,UserID是外键
type CreditCard struct {
gorm.Model
Number string
UserId int //默认会在CreditCard表中生成User66Id字段作为与user表关联的外键id
//默认会在CreditCard表中生成UserID字段作为与User表关联的外键ID
}
func main() {
dsn := "root:7PXjAkY!&nlR@tcp(192.168.11.128:3306)/test_db?charset=utf8mb4&parseTime=True&loc=Local"
db, _ := gorm.Open(mysql.Open(dsn66), &gorm.Config{})
db.AutoMigrate(User{}, CreditCard{})
}
mysql> show tables;
+-------------------+
| Tables_in_test_db |
+-------------------+
| credit_cards |
| users |
+-------------------+
2 rows in set (0.00 sec)
mysql> desc users;
+------------+-----------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+-----------------+------+-----+---------+----------------+
| id | bigint 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 | |
+------------+-----------------+------+-----+---------+----------------+
mysql> desc credit_cards;
+------------+-----------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+-----------------+------+-----+---------+----------------+
| id | bigint 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 | |
| number | bigint | YES | | NULL | |
| user_id | bigint unsigned | YES | MUL | NULL | |
+------------+-----------------+------+-----+---------+----------------+
mysql> drop table users;
ERROR 3730 (HY000): Cannot drop table 'users' referenced by a foreign key constraint 'fk_users_credit_cards' on table 'credit_cards'.
二 外键
type User struct{
gorm.Model
CreditCards []CreditCard `gorm:"foreignKey:UserRefer"`
}
type CreditCard struct{
gorm.Model
Number string
UserRefer uint
}
`gorm:"foreignKey:UserRefer"` 可以使用这个字段来去改它的外键。
import (
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
type User struct {
gorm.Model
CreditCards []*CreditCard `gorm:"foreignKey:UserRefer"`
}
type CreditCard struct {
gorm.Model
Number int
UserRefer int
}
func main() {
dsn6 := "root:7PXjAkY!&nlR@tcp(192.168.11.128:3306)/test_db?charset=utf8mb4&parseTime=True&loc=Local"
db, _ := gorm.Open(mysql.Open(dsn6), &gorm.Config{})
db.AutoMigrate(&User{}, &CreditCard{})
}
mysql> desc users;
+------------+-----------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+-----------------+------+-----+---------+----------------+
| id | bigint 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 | |
+------------+-----------------+------+-----+---------+----------------+
mysql> desc credit_cards;
+------------+-----------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+-----------------+------+-----+---------+----------------+
| id | bigint 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 | |
| number | bigint | YES | | NULL | |
| user_refer | bigint unsigned | YES | MUL | NULL | |
+------------+-----------------+------+-----+---------+----------------+
mysql> drop tables users;
ERROR 3730 (HY000): Cannot drop table 'users' referenced by a foreign key constraint 'fk_users_credit_cards' on table 'credit_cards'.
一般不推荐使用这种方式,还是推荐使用Userid的方式。
三 外键关联
type User struct {
gorm.Model
MemberNumber string
// 默认CreditCard会使用User表的Id作为外键,association_foreignkey:MemberNumber
// 指定使用MemberNumber 作为外键关联
CreditCards []CreditCard
`gorm:"foreignkey:UserMemberNumber;association_foreignkey:MemberNumber"`
}
type CreditCard struct {
gorm.Model
Number string
UserMemberNumber string
}
其实就是creditcard外键UserMemberNumber直接去找user的 MemberNumber。
上面其实也就是改变了外键的默认值,默认外键是Userid,那么现在变为了MemberNumber。
一般使用默认id的外键就能够覆盖很多场景,上面这种只是举例。
package main
import (
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
type User struct {
gorm.Model
MemberNumber string
CreditCards []*CreditCard `gorm:"foreignKey:UserMemberNumber;association_foreignKey:MemberNumber"`
}
type CreditCard struct {
gorm.Model
Number int
UserMemberNumber string
}
func main() {
dsn6 := "root:7PXjAkY!&nlR@tcp(192.168.11.128:3306)/test_db?charset=utf8mb4&parseTime=True&loc=Local"
db, _ := gorm.Open(mysql.Open(dsn6), &gorm.Config{})
db.AutoMigrate(&User{}, &CreditCard{})
}
mysql> desc users;
+---------------+-----------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------------+-----------------+------+-----+---------+----------------+
| id | bigint 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 | |
| member_number | longtext | YES | | NULL | |
+---------------+-----------------+------+-----+---------+----------------+
mysql> desc credit_cards;
+--------------------+-----------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+--------------------+-----------------+------+-----+---------+----------------+
| id | bigint 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 | |
| number | bigint | YES | | NULL | |
| user_member_number | bigint unsigned | YES | MUL | NULL | |
+--------------------+-----------------+------+-----+---------+----------------+
四 创建一对多表
/*
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() {
// 0、连接数据库
dsn := "root:1@tcp(127.0.0.1:3306)/test_db?
charset=utf8mb4&parseTime=True&loc=Local"
db, _ := gorm.Open(mysql.Open(dsn), &gorm.Config{})
// 创建表结构
db.AutoMigrate(User{}, CreditCard{})
// 1、创建一对多
user := User{
Username: "zhangsan",
CreditCards: []CreditCard{
{Number: "0001"},
{Number: "0002"},
},
}
db.Create(&user)
// 2、为已存在用户添加信用卡
u := User{Username: "zhangsan"}
db.First(&u)
//fmt.Println(u.Username)
}
3. 一对多Association 查找关联 使用 Association 方法, 需要把把 User 查询好, 然后根据 User 定义中指定的 AssociationForeignKey 去查找 CreditCard
import (
"fmt"
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
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 int
}
func main() {
dsn6 := "root:7PXjAkY!&nlR@tcp(192.168.11.128:3306)/test_db?charset=utf8mb4&parseTime=True&loc=Local"
db, _ := gorm.Open(mysql.Open(dsn6), &gorm.Config{})
db.AutoMigrate(&User{}, &CreditCard{})
/*
u := &User{
Model: gorm.Model{},
UserName: "lucas",
CreditCards: []*CreditCard{
{Number: "0001", UserID: 1},
{Number: "0002", UserID: 2},
},
}
db.Create(u)
*/
// 1、查找 用户名为 lucas 的所有信用卡信息
u1 := &User{UserName: "lucas"}
// Association必须要先查出User才能关联查询对应的CreditCard
db.First(u1)
db.Model(u1).Association("CreditCards").Find(&u1.CreditCards)
fmt.Println(u1)
}
追加
package main
import (
"encoding/json"
"fmt"
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
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 int
}
func main() {
dsn6 := "root:7PXjAkY!&nlR@tcp(192.168.11.128:3306)/test_db?charset=utf8mb4&parseTime=True&loc=Local"
db, _ := gorm.Open(mysql.Open(dsn6), &gorm.Config{})
db.AutoMigrate(&User{}, &CreditCard{})
u := &User{
Model: gorm.Model{},
UserName: "lucas",
CreditCards: []CreditCard{
{Number: "0001", UserID: 0},
{Number: "0002", UserID: 1},
},
}
db.Create(u)
u1 := &User{UserName: "lucas"}
db.First(u1)
db.Model(u1).Association("CreditCards").Find(&u1.CreditCards)
fmt.Println(u1)
db.Model(u1).Association("CreditCards").Append([]CreditCard{
{Number: "0008", UserID: 1},
})
fmt.Println(u1)
strUser, _ := json.Marshal(u1)
fmt.Println(string(strUser))
}
&{{1 2023-04-16 10:48:54.423 +0800 CST 2023-04-16 14:12:45.825 +0800 CST {0001-01-01 00:00:00 +0000 UTC false}} lucas []}
&{{1 2023-04-16 10:48:54.423 +0800 CST 2023-04-16 14:15:51.44 +0800 CST {0001-01-01 00:00:00 +0000 UTC false}} lucas [{{17 2023-04-16 14:15:51.445
+0800 CST 2023-04-16 14:15:51.445 +0800 CST {0001-01-01 00:00:00 +0000 UTC false}} 0008 1}]}
{"ID":1,"CreatedAt":"2023-04-16T10:48:54.423+08:00","UpdatedAt":"2023-04-16T14:15:51.44+08:00","DeletedAt":null,"username":"lucas","CreditCards":[
{"ID":17,"CreatedAt":"2023-04-16T14:15:51.445+08:00","UpdatedAt":"2023-04-16T14:15:51.445+08:00","DeletedAt":null,"Number":"0008","UserID":1}]}
4. 一对多Preload 预加载
使用 Preload 方法, 在查询 User 时先去获取 CreditCard 的记录
和上面associate不一样,他们两个最大的区别是执行的顺序是不一样的,associate是先去获取user,再去获取creditcard。
preload是先去获取creditcard,再去获取user。
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() {
// 0、连接数据库
dsn := "root:1@tcp(127.0.0.1:3306)/test_db?
charset=utf8mb4&parseTime=True&loc=Local"
db, _ := gorm.Open(mysql.Open(dsn), &gorm.Config{})
// 1、预加载: 查找 user 时预加载相关 CreditCards
//users := User{Username: "zhangsan"} // 只查找张三用户的信用卡信息
users := []User{}
db.Preload("CreditCards").Find(&users)
查询结果
[
{
"ID":1,
"username":"zhangsan",
"CreditCards":[
{
"ID":1,
"Number":"0001",
"UserID":1
},
...
]
}
]