关联之一对一
一对一的场景下,有两种模式,一种是Belongs To(属于)
,一种是Has One(拥有一个)
Belongs To
belongs to
可以理解为A
身上打了B
的标签
,A
知道B
的所有信息,但是B
对A
一无所知,可以理解为单相思自作多情,一方会在自己这边记住对方,而对方却毫不知情
Go
type Dog struct {
ID uint
Name string
GirlGod GirlGod
GirlGodID uint // 外键
}
type GirlGod struct {
ID uint
Name string
}
TIP
Dog
结构体中声明了GirlGod
和GirlGodID
,但是GirlGod
结构体可以不声明关于Dog
的任何信息使用
DB.AutoMigrate(&Dog{})
创建表时,会自动创建dogs
表和girl_gods
表
- 执行
DB.AutoMigrate(&Dog{})
会执行以下语sql
SQL
CREATE TABLE dogs
(
id bigint UNSIGNED AUTO_INCREMENT
PRIMARY KEY,
name longtext NULL,
girl_god_id bigint UNSIGNED NULL,
CONSTRAINT fk_dogs_girl_god
FOREIGN KEY (girl_god_id) REFERENCES girl_gods (id)
);
CREATE TABLE girl_gods
(
id bigint UNSIGNED AUTO_INCREMENT
PRIMARY KEY,
name longtext NULL
);
创建关联
- 在创建
Dog
的时候就指定其所属的GirlGod
Go
func dogCreate() {
g1 := GirlGod{
Name: "女神1号",
}
d1 := Dog{
Name: "狗哥1号",
GirlGod: g1,
}
DB.Create(&d1)
}
// 下面同理
func dogCreate() {
g2 := GirlGod{
Name: "女神2号",
}
d2 := Dog{
Name: "狗哥2号",
GirlGod: g2,
}
DB.Create(&d2)
}
执行过程如下:
先创建新的
Dog
和GirlGod
将新创建的
Dog
的girl_god_id
设置为新创建的GirlGod
的id
SQL
INSERT INTO `girl_gods` (`name`) VALUES ('女神1号') ON DUPLICATE KEY UPDATE `id`=`id`
INSERT INTO `dogs` (`name`,`girl_god_id`) VALUES ('狗哥1号',1)
Append
替换关联
TIP
对于一对一(Belongs To
、Has One
)的场景,Append
的作用就是替换关联
- 先准备一些新的数据
Go
func prepareData() {
// 先创建一些GirlGod数据
girlGods := []GirlGod{
{Name: "女神1号"},
{Name: "女神2号"},
{Name: "女神3号"},
{Name: "女神4号"},
{Name: "女神5号"},
{Name: "女神6号"},
}
DB.Model(&GirlGod{}).Create(&girlGods)
var girlGod1 GirlGod
var girlGod2 GirlGod
DB.Model(&GirlGod{}).Find(&girlGod1, 1)
DB.Model(&GirlGod{}).Find(&girlGod2, 2)
// 创建Dog数据 并关联GirlGod
dog1 := Dog{Name: "狗哥1号", GirlGod: girlGod1}
dog2 := Dog{Name: "狗哥2号", GirlGod: girlGod2}
dogs := []Dog{dog1, dog2}
DB.Model(&Dog{}).Create(&dogs)
}
得到的dog
数据为
json
[
{
"id": 1,
"name": "狗哥1号",
"girl_god_id": 1
},
{
"id": 2,
"name": "狗哥2号",
"girl_god_id": 2
}
]
使用Append
替换关联
- 将
狗哥1号
关联的女神1号
替换为女神6号
Go
func operateAssociations() {
var dog1 Dog
DB.Model(&Dog{}).Find(&dog1, 1)
var girlGod6 GirlGod
DB.Model(&GirlGod{}).Find(&girlGod6, 6)
// 将狗哥1号关联的女神1号替换为女神6号
err := DB.Model(&dog1).Association("GirlGod").Append(&girlGod6)
if err != nil {
return
}
}
此时得到的dog
数据为
json
[
{
"id": 1,
"name": "狗哥1号",
"girl_god_id": 6
},
{
"id": 2,
"name": "狗哥2号",
"girl_god_id": 2
}
]
Replace
替换关联
不指定替换项和被替换项
- 将
狗哥2号
关联的女神2号
替换为女神5号
Go
func operateAssociations() {
var dog2 Dog
DB.Model(&Dog{}).Find(&dog2, 2)
var girlGod5 GirlGod
DB.Model(&GirlGod{}).Find(&girlGod5, 5)
// 将狗哥2号关联的女神2号替换为女神5号
err := DB.Model(&dog2).Association("GirlGod").Replace(&girlGod5)
if err != nil {
return
}
}
此时得到的dog
数据为
json
[
{
"id": 1,
"name": "狗哥1号",
"girl_god_id": 6
},
{
"id": 2,
"name": "狗哥2号",
"girl_god_id": 5
}
]
指定替换项和被替换项
- 将狗哥2号关联的女神5号替换为女神2号
Go
func operateAssociations() {
var dog2 Dog
DB.Model(&Dog{}).Find(&dog2, 2)
var girlGod5 GirlGod
DB.Model(&GirlGod{}).Find(&girlGod5, 5)
var girlGod2 GirlGod
DB.Model(&GirlGod{}).Find(&girlGod2, 2)
// 将狗哥2号关联的女神5号替换为女神2号
err := DB.Model(&dog2).Association("GirlGod").Replace(&girlGod5, &girlGod2)
if err != nil {
return
}
}
此时得到的dog
数据为
json
[
{
"id": 1,
"name": "狗哥1号",
"girl_god_id": 6
},
{
"id": 2,
"name": "狗哥2号",
"girl_god_id": 2
}
]
Delete
删除关联
- 删除
狗哥1号
关联的女神6号
Go
func operateAssociations() {
var dog1 Dog
DB.Model(&Dog{}).Find(&dog1, 1)
var girlGod6 GirlGod
DB.Model(&GirlGod{}).Find(&girlGod6, 6)
// 删除狗哥1号关联的女神6号
err := DB.Model(&dog1).Association("GirlGod").Delete(&girlGod6)
if err != nil {
return
}
}
此时得到的dog
数据为
json
[
{
"id": 1,
"name": "狗哥1号",
"girl_god_id": null
},
{
"id": 2,
"name": "狗哥2号",
"girl_god_id": 2
}
]
Clear
清除所有关联
- 将
狗哥2号
关联的女神清除
Go
func operateAssociations() {
var dog2 Dog
DB.Model(&Dog{}).Find(&dog2, 2)
// 将狗哥2号关联的女神清除
err := DB.Model(&dog2).Association("GirlGod").Clear()
if err != nil {
return
}
}
此时得到的dog
数据为
json
[
{
"id": 1,
"name": "狗哥1号",
"girl_god_id": null
},
{
"id": 2,
"name": "狗哥2号",
"girl_god_id": null
}
]
查询关联数据
TIP
- 查询关联数据之前需要使用
Preload
加载相关数据
- 先回归一下数据
Go
func setAssociation() {
var dog1 Dog
DB.Model(&Dog{}).Find(&dog1, 1)
var dog2 Dog
DB.Model(&Dog{}).Find(&dog2, 2)
var girlGod6 GirlGod
DB.Model(&GirlGod{}).Find(&girlGod6, 6)
var girlGod5 GirlGod
DB.Model(&GirlGod{}).Find(&girlGod5, 5)
DB.Model(&dog1).Association("GirlGod").Append(&girlGod6)
DB.Model(&dog2).Association("GirlGod").Append(&girlGod5)
}
此时得到的dog
数据为
json
[
{
"id": 1,
"name": "狗哥1号",
"girl_god_id": 6
},
{
"id": 2,
"name": "狗哥2号",
"girl_god_id": 5
}
]
- 查询关联
Go
// 查询关联
func queryAssociation() {
var dog1 Dog
DB.Preload("GirlGod").Find(&dog1, 1)
fmt.Printf("dog1 is %v\n", dog1)
// dog1 is {1 狗哥1号 {6 女神6号} 6}
}
Has One
Has One
的结构和Belongs To
的略有不同,如下
Go
type HDog struct {
ID uint
Name string
HGirlGodID uint // 外键,就是HGirlGod.ID
}
type HGirlGod struct {
ID uint
Name string
HDog HDog
}
TIP
- 此时执行
DB.AutoMigrate(&HGirlGod{})
,会自动创建h_girl_gods
表,并不会自动创建h_dogs
表,sql
如下
sql
CREATE TABLE h_girl_gods
(
id bigint UNSIGNED AUTO_INCREMENT
PRIMARY KEY,
name longtext NULL
);
创建数据
和
Belongs To
的方式一样创建
女神1号
和女神2号
、狗哥1号
,并将狗哥1号
和女神2号
创建关联
Go
// 创建女神1号和女神2号、狗哥1号,并将狗哥1号和女神2号创建关联
func createData() {
hGirlGod1 := HGirlGod{Name: "女神1号"}
DB.Model(&HGirlGod{}).Create(&hGirlGod1)
hDog1 := HDog{Name: "狗哥1号"}
hGirlGod2 := HGirlGod{Name: "女神2号", HDog: hDog1}
DB.Model(&HGirlGod{}).Create(&hGirlGod2)
}
Append
替换数据
- 将
狗哥1号
关联女神2号
替换为狗哥1号
关联女神1号
Go
func operateAssociation() {
var hDog1 HDog
DB.Model(&HDog{}).Find(&hDog1, 1)
var hGirlGod1 HGirlGod
DB.Model(&HGirlGod{}).Find(&hGirlGod1, 1)
// 将女神1号关联的HDog替换为hDog1(狗哥1号)
DB.Model(&hGirlGod1).Association("HDog").Append(&hDog1)
}
sql
如下:
sql
# 设置狗哥1号的关联女神ID为女神1号的ID
INSERT INTO `h_dogs` (`name`,`h_girl_god_id`,`id`) VALUES ('狗哥1号',1,1) ON DUPLICATE KEY UPDATE `h_girl_god_id`=VALUES(`h_girl_god_id`)
# 设置其他所有ID不为1的狗哥的女神ID为null
UPDATE `h_dogs` SET `h_girl_god_id`=NULL WHERE `h_dogs`.`id` <> 1 AND `h_dogs`.`h_girl_god_id` = 1
Replace
替换数据
- 将
狗哥1号
和女神1号
关联的关联替换为女神2号
Go
func operateAssociation() {
var hDog1 HDog
DB.Model(&HDog{}).Find(&hDog1, 1)
var hGirlGod2 HGirlGod
DB.Model(&HGirlGod{}).Find(&hGirlGod2, 2)
// 将女神2号关联的HDog替换为hDog1(狗哥1号)
DB.Model(&hGirlGod2).Association("HDog").Replace(&hDog1)
}
Delete
、Clear
Delete
和Clear
就和上面的相同,只是方法替换了一下