关联之一对多
一个女神可以有多个狗哥,一个狗哥只属于一个女神
结构体如下:
type HMDog struct {
ID uint
Name string
HMGirlGodID uint // 外键,就是HMGirlGod.ID
}
type HMGirlGod struct {
ID uint
Name string
HMDogs []HMDog
}
- 迁移表
func migrateTable() {
err := DB.AutoMigrate(&HMGirlGod{}, &HMDog{})
if err != nil {
return
}
}
- 创建一些数据
func createDGData() {
hmDog1 := HMDog{Name: "狗哥1号"}
hmDog2 := HMDog{Name: "狗哥2号"}
hmDog3 := HMDog{Name: "狗哥3号"}
hmDog4 := HMDog{Name: "狗哥4号"}
hmDog5 := HMDog{Name: "狗哥5号"}
hmDog6 := HMDog{Name: "狗哥6号"}
hmGirlGod1 := HMGirlGod{
Name: "女神1号",
HMDogs: []HMDog{hmDog1, hmDog2},
}
hmGirlGod2 := HMGirlGod{
Name: "女神2号",
HMDogs: []HMDog{hmDog3, hmDog4},
}
hmGirlGod3 := HMGirlGod{
Name: "女神3号",
HMDogs: []HMDog{hmDog5, hmDog6},
}
hmGirlGods := []HMGirlGod{hmGirlGod1, hmGirlGod2, hmGirlGod3}
DB.Model(&HMGirlGod{}).Create(&hmGirlGods)
}
HMDog
表数据如下
[
{
"id": 1,
"name": "狗哥1号",
"hm_girl_god_id": 1
},
{
"id": 2,
"name": "狗哥2号",
"hm_girl_god_id": 1
},
{
"id": 3,
"name": "狗哥3号",
"hm_girl_god_id": 2
},
{
"id": 4,
"name": "狗哥4号",
"hm_girl_god_id": 2
},
{
"id": 5,
"name": "狗哥5号",
"hm_girl_god_id": 3
},
{
"id": 6,
"name": "狗哥6号",
"hm_girl_god_id": 3
}
]
操作数据
Append
先清除一波数据,将
女神1号
关联的狗哥都清掉
func clearAssociation() {
// 找出女神1号
var g1 HMGirlGod
DB.Model(&HMGirlGod{}).Find(&g1, 1)
// 清除其关联的所有狗哥
err := DB.Model(&g1).Association("HMDogs").Clear()
if err != nil {
return
}
}
以上操作后HMDog
表数据如下:
[
{
"id": 1,
"name": "狗哥1号",
"hm_girl_god_id": null
},
{
"id": 2,
"name": "狗哥2号",
"hm_girl_god_id": null
},
......
]
Append
单个
func appendAssociation() {
// 找出女神1号
var g1 HMGirlGod
DB.Model(&HMGirlGod{}).Find(&g1, 1)
// 找出狗哥1号
var d1 HMDog
DB.Model(&HMDog{}).Find(&d1, 1)
// 将狗哥1号和女神1号关联
DB.Model(&g1).Association("HMDogs").Append(&d1)
}
以上操作后HMDog
表数据如下:
[
{
"id": 1,
"name": "狗哥1号",
"hm_girl_god_id": 1
},
{
"id": 2,
"name": "狗哥2号",
"hm_girl_god_id": null
},
......
]
Append
多个
func appendAssociation() {
// 找出女神1号
var g1 HMGirlGod
DB.Model(&HMGirlGod{}).Find(&g1, 1)
// 找出狗哥1号和狗哥2号
var d1 HMDog
var d2 HMDog
DB.Model(&HMDog{}).Find(&d1, 1)
DB.Model(&HMDog{}).Find(&d2, 2)
ds := []HMDog{d1, d2}
// 将狗哥1号、狗哥2号和女神1号关联
DB.Model(&g1).Association("HMDogs").Append(&ds)
}
以上操作后HMDog
表数据如下:
[
{
"id": 1,
"name": "狗哥1号",
"hm_girl_god_id": 1
},
{
"id": 2,
"name": "狗哥2号",
"hm_girl_god_id": 1
},
......
]
Append
新的数据
TIP
上面在使用
Append
时,操作数据库中的已有数据,也可以Append
新创建的数据当使用
Append
创建新数据时,不用手动去Create
数据,gorm
会自动创建新的数据
// 添加新成员
func appendNewRowAssociation() {
// 找出女神1号
var g1 HMGirlGod
DB.Model(&HMGirlGod{}).Find(&g1, 1)
d7 := HMDog{Name: "狗哥7号"}
d8 := HMDog{Name: "狗哥8号"}
// d7和d8是数据库中没有的数据,是新的数据,使用Append后,会自动在HMDog表中创建d7、d8,不用手动Create
dSlice := []HMDog{d7, d8}
DB.Model(&g1).Association("HMDogs").Append(&dSlice)
}
以上操作后HMDog
表数据如下:
[
{
"id": 1,
"name": "狗哥1号",
"hm_girl_god_id": 1
},
{
"id": 2,
"name": "狗哥2号",
"hm_girl_god_id": 1
},
{
"id": 3,
"name": "狗哥3号",
"hm_girl_god_id": 2
},
{
"id": 4,
"name": "狗哥4号",
"hm_girl_god_id": 2
},
{
"id": 5,
"name": "狗哥5号",
"hm_girl_god_id": 3
},
{
"id": 6,
"name": "狗哥6号",
"hm_girl_god_id": 3
},
{
"id": 7,
"name": "狗哥7号",
"hm_girl_god_id": 1
},
{
"id": 8,
"name": "狗哥8号",
"hm_girl_god_id": 1
}
]
Replace
WARNING
在
一对多
的关系中,Replace(value)
方法会将之前所有的关联都替换为value
,所以使用的时候要注意下如下所示,目前
女神1号
关联的有狗哥1号
、狗哥2号
、狗哥7号
、狗哥8号
,如果直接执行Replace(&d9)
,会清除之前女神1号
关联的所有狗哥,然后将女神1号
关联到狗哥7号
所以使用
Replace
的时候要注意,是否要消除原先所有的关联
目前HMDog
表数据如下:
[
{
"id": 1,
"name": "狗哥1号",
"hm_girl_god_id": 1
},
{
"id": 2,
"name": "狗哥2号",
"hm_girl_god_id": 1
},
......
{
"id": 7,
"name": "狗哥7号",
"hm_girl_god_id": 1
},
{
"id": 8,
"name": "狗哥8号",
"hm_girl_god_id": 1
}
]
新建一个
狗哥9号
,使用Replace
将其和女神1号
关联
func replaceAssociation() {
// 新创建狗哥9号
d9 := HMDog{Name: "狗哥9号"}
// 找出女神1号
var g1 HMGirlGod
DB.Model(&HMGirlGod{}).Find(&g1, 1)
DB.Model(&g1).Association("HMDogs").Replace(&d9)
}
以上操作后HMDog
表数据如下:
[
{
"id": 1,
"name": "狗哥1号",
"hm_girl_god_id": null
},
{
"id": 2,
"name": "狗哥2号",
"hm_girl_god_id": null
},
......
{
"id": 7,
"name": "狗哥7号",
"hm_girl_god_id": null
},
{
"id": 8,
"name": "狗哥8号",
"hm_girl_god_id": null
},
{
"id": 9,
"name": "狗哥9号",
"hm_girl_god_id": 1
}
]
替换多个方式如下
func replaceMulti() {
// 找出狗哥3号
var d3 HMDog
DB.Model(&HMDog{}).Find(&d3, 3)
// 找出狗哥4号
var d4 HMDog
DB.Model(&HMDog{}).Find(&d4, 4)
// 找出狗哥9号
var d9 HMDog
DB.Model(&HMDog{}).Find(&d9, 9)
// 将女神2号关联的狗哥替换为3号和9号
// 找出女神2号
var g2 HMGirlGod
DB.Model(&HMGirlGod{}).Find(&g2, 2)
g2Dogs := []HMDog{d3, d4}
// 下面两行中的替换方式都可以
//err := DB.Model(&g2).Association("HMDogs").Replace(&d3, &d4, &d9)
err := DB.Model(&g2).Association("HMDogs").Replace(&g2Dogs)
if err != nil {
return
}
}
Delete
Clear
和之前的用法一致
查询数据
查询
女神2号
关联的所有狗哥
func queryData() {
// 查询女神2号的所有狗哥
var g2 HMGirlGod
DB.Preload("HMDogs").Find(&g2, 2)
// g2 is {2 女神2号 [{3 狗哥3号 2} {4 狗哥4号 2}]}
fmt.Printf("g2 is %v\n", g2)
}
使用自定义预加载SQL
方式查询
查询
女神2号
下ID为3的狗哥
func queryData() {
// 查询女神2号下ID为3的狗哥
var g2 HMGirlGod
DB.Preload("HMDogs", func(db *gorm.DB) *gorm.DB {
return db.Where("id = ?", 3)
}).Find(&g2, 2)
// g2 is {2 女神2号 [{3 狗哥3号 2}]}
fmt.Printf("g2 is %v\n", g2)
}
使用预加载条件查询
方式
查询
女神2号
下ID为3的狗哥
func queryByPreloadCondition() {
// 查询女神2号下ID为3的狗哥
var g2 HMGirlGod
DB.Preload("HMDogs", "id = ?", 3).Find(&g2, 2)
// g2 is {2 女神2号 [{3 狗哥3号 2}]}
fmt.Printf("g2 is %v\n", g2)
}
链式预加载
方式查询
- 为狗哥增加一个钱包,每个人都有自己对应的小金库
修改结构体,为
HMDog
添加Wallet
type Wallet struct {
ID uint
Amount float64
HMDogID uint // 外键,就是HMDog.ID
}
type HMDog struct {
ID uint
Name string
HMGirlGodID uint // 外键,就是HMGirlGod.ID
Wallet Wallet
}
type HMGirlGod struct {
ID uint
Name string
HMDogs []HMDog
}
准备下数据,将
1-3号
,8-9号
狗哥和女神一号
创建关联
[
{
"id": 1,
"name": "狗哥1号",
"hm_girl_god_id": 1
},
{
"id": 2,
"name": "狗哥2号",
"hm_girl_god_id": 1
},
{
"id": 3,
"name": "狗哥3号",
"hm_girl_god_id": 1
},
......
{
"id": 7,
"name": "狗哥7号",
"hm_girl_god_id": 1
},
{
"id": 8,
"name": "狗哥8号",
"hm_girl_god_id": 1
},
{
"id": 9,
"name": "狗哥9号",
"hm_girl_god_id": 1
}
]
钱包数据如下
狗哥ID | 狗哥编号 | 金额 |
---|---|---|
1 | 1 | 200000 |
2 | 2 | 8000 |
3 | 3 | 300000 |
4 | 7 | 800 |
5 | 8 | 60000 |
6 | 9 | 1000000 |
找出
女神1号
下面的所有关联的狗哥
func linkQuery() {
// 找出女神1号下面的所有关联的狗哥
var g1 HMGirlGod
DB.Preload("HMDogs").Find(&g1, 1)
// g1 is {1 女神1号 [{1 狗哥1号 1 {0 0 0}} {2 狗哥2号 1 {0 0 0}} {3 狗哥3号 1 {0 0 0}} {7 狗哥7号 1 {0 0 0}} {8 狗哥8号 1 {0 0 0}} {9 狗哥9号 1 {0 0 0}}]}
fmt.Printf("g1 is %v\n", g1)
}
上面的结果查出来了
女神1号
关联的所有狗哥,但是狗哥的钱包余额却没有查到,如果要查询余额,可以使用链式预加载查询
,如下所示
func linkQuery() {
// 找出女神1号下面的所有关联的狗哥
var g1 HMGirlGod
//DB.Preload("HMDogs").Find(&g1, 1)
// 使用链式查询
DB.Preload("HMDogs.Wallet").Find(&g1, 1)
// g1 is {1 女神1号 [{1 狗哥1号 1 {1 200000 1}} {2 狗哥2号 1 {2 8000 2}} {3 狗哥3号 1 {3 300000 3}} {7 狗哥7号 1 {4 800 7}} {8 狗哥8号 1 {5 60000 8}} {9 狗哥9号 1 {6 1e+06 9}}]}
fmt.Printf("g1 is %v\n", g1)
}
链式预加载也可以
多级预加载链式调用
,比如找出女神1号下面的所有关联的ID > 3
的狗哥
func multiLinkQuery() {
// 找出女神1号下面的所有关联的`ID > 3`的狗哥
var g1 HMGirlGod
//DB.Preload("HMDogs").Find(&g1, 1)
// 使用多级预加载链式查询
DB.Preload("HMDogs.Wallet").Preload("HMDogs", "id > ?", 3).Find(&g1, 1)
fmt.Printf("g1 is %v\n", g1)
}
Joins查询
WARNING
Joins
只支持一对一
的场景,比如Belongs To
或者Has One
如果我们只想找到女神1号关联的狗哥中
余额大于10000
的,余额小于10000的就不要出现,上面的方法就不满足了,如下可以看到,把余额不足10000的狗哥也带出来了
func joinsQuery() {
// 找出女神1号下面的所有关联的狗哥
var g1 HMGirlGod
// 查询女神1号关联的狗哥中 金额大于10000的
DB.Preload("HMDogs.Wallet", "amount > ?", 10000).Find(&g1, 1)
// g1 is {1 女神1号 [{1 狗哥1号 1 {1 200000 1}} {2 狗哥2号 1 {0 0 0}} {3 狗哥3号 1 {3 300000 3}} {7 狗哥7号 1 {0 0 0}} {8 狗哥8号 1 {5 60000 8}} {9 狗哥9号 1 {6 1e+06 9}}]}
fmt.Printf("g1 is %v\n", g1)
}
使用
Joins
func joinsQuery() {
// 找出女神1号下面的所有关联的狗哥
var g1 HMGirlGod
// 查询女神1号关联的狗哥中 金额大于10000的
DB.Preload("HMDogs", func(db *gorm.DB) *gorm.DB {
return db.Joins("Wallet").Where("amount > ?", 10000)
}).Find(&g1, 1)
// g1 is {1 女神1号 [{1 狗哥1号 1 {1 200000 1}} {3 狗哥3号 1 {3 300000 3}} {8 狗哥8号 1 {5 60000 8}} {9 狗哥9号 1 {6 1e+06 9}}]}
fmt.Printf("g1 is %v\n", g1)
}
上述代码实际执行的sql如下
SELECT * FROM hm_dogs d LEFT JOIN wallets w ON d.id = w.hm_dog_id WHERE w.amount > '10000' AND d.hm_girl_god_id = '1';
查询女神1号关联的狗哥中
金额大于10000
并且ID大于3
的
func joinsQuery() {
var g1 HMGirlGod
// 查询女神1号关联的狗哥中金额大于10000并且ID大于3的
DB.Preload("HMDogs", func(db *gorm.DB) *gorm.DB {
return db.Joins("Wallet").Where("amount > ? AND hm_dogs.id > ?", 10000, 3)
}).Find(&g1, 1)
fmt.Printf("g1 is %v\n", g1)
}