單條關聯查詢
先創建兩個關聯模型:
// 用戶模型
type User struct {
gorm.Model
Username string `gorm:"type:varchar(20);not null;unique"`
Email string `gorm:"type:varchar(64);not null;unique"`
Role string `gorm:"type:varchar(32);not null"`
Active uint8 `gorm:"type:tinyint unsigned;default:1"`
Profile Profile `gorm:"foreignkey:UserID;association_autoupdate:false"`
}
// 用戶信息模型
type Profile struct {
gorm.Model
UserID uint `gorm:"type:int unsigned;not null;unique"`
Username string `gorm:"type:varchar(20);not null;unique"`
Nickname string `gorm:"type:varchar(64);not null;unique"`
Phone string `gorm:"type:varchar(32)"`
Gender uint8 `gorm:"type:tinyint unsigned;default:0"`
Birthday time.Time `gorm:"type:datetime;default:null"`
Sign string `gorm:"type:varchar(255)"`
Avatar string `gorm:"type:text"`
}
直接查詢單條 User 記錄
var user User
DB.Debug().First(&user)
這里省略 JSON 序列化輸出的過程,直接看輸出,類似這樣:
{
"ID": 1,
"CreatedAt": "2020-03-11T18:26:13+08:00",
"UpdatedAt": "2020-03-11T18:28:41+08:00",
"DeletedAt": null,
"Username": "test",
"Email": "text@demo.dev",
"Role": "member",
"Active": 1,
"Profile": {
"ID": 0,
"CreatedAt": "001-01-01T00:00:00Z",
"UpdatedAt": "001-01-01T00:00:00Z",
"DeletedAt": null,
"UserID": 0,
"Username": "",
"Nickname": "",
"Phone": "",
"Gender": 0,
"Birthday": "0001-01-01T00:00:00Z",
"Sign": "",
"Avatar": ""
}
}
可以看到,雖然有輸出 Profile 字段,但是里面的字段值全為零值,也就是說直接查詢 User 並不會默認把關聯的 Profile 一同查詢出來。
可能有童鞋要問了,沒查詢 Profile 為什么還會輸出空值的 Profile 字段呢?這是因為 JSON 序列化是按照模型的定義自動處理,User 模型中定義了 Profile 字段,如進行關聯查詢且能查到結果,那么就賦值給 Profile 字段,否則 Profile 依然序列化輸出,只不過 Profile 里面的字段全都為空值。
接下來看下如何使用關聯查詢獲取完整的 User 數據:
// 方式一:手動查詢關聯數據
var user User
// 第一步,查詢用戶
DB.Debug().First(&user)
// 第二步,查詢關聯的用戶信息
// 注意,Related 方法第二個參數為 Profile 的外鍵
DB.Debug().Model(&user).Related(&user.Profile, "UserID")
// 方式二:也可以使用預加載方式查詢關聯數據
DB.Debug().Model(&user).Preload("Profile").First(&user)
可以看到,使用預加載方式語法更簡練,實際上底層還是2個查詢,只不過 gorm 幫我們封裝好了,現在可以獲取到完整的數據,類似這樣:
{
"ID": 1,
"CreatedAt": "2020-03-11T18:26:13+08:00",
"UpdatedAt": "2020-03-11T18:28:41+08:00",
"DeletedAt": null,
"Username": "test",
"Email": "text@demo.dev",
"Role": "member",
"Active": 1,
"Profile": {
"ID": 1,
"CreatedAt": "2020-03-11T18:26:13+08:00",
"UpdatedAt": "2020-03-11T18:26:13+08:00",
"DeletedAt": null,
"UserID": 1,
"Username": "test",
"Nickname": "test",
"Phone": "",
"Gender": 0,
"Birthday": "0001-01-01T00:00:00Z",
"Sign": "",
"Avatar": ""
}
}
列表關聯查詢
列表的關聯查詢和單條關聯查詢類似,不過手動進行列表的關聯查詢很繁瑣,得先查出 User 列表,然后再查詢一次 Profile 列表獲取對應的數據,最后整合兩部分數據。直接使用預加載就很簡單了,代碼如下:
var users []User
// 使用預加載查詢
DB.Debug().Model(&User{}).Preload("Profile").Find(&users)
輸出如下:
[
{
"ID": 1,
"CreatedAt": "2020-03-11T18:26:13+08:00",
"UpdatedAt": "2020-03-11T18:28:41+08:00",
"DeletedAt": null,
"Username": "test",
"Email": "text@demo.dev",
"Role": "member",
"Active": 1,
"Profile": {
"ID": 1,
"CreatedAt": "2020-03-11T18:26:13+08:00",
"UpdatedAt": "2020-03-11T18:26:13+08:00",
"DeletedAt": null,
"UserID": 1,
"Username": "test",
"Nickname": "test",
"Phone": "",
"Gender": 0,
"Birthday": "0001-01-01T00:00:00Z",
"Sign": "",
"Avatar": ""
}
},
...
]
gorm 底層使用這兩條查詢:
SELECT * FROM `user` WHERE `user`.`deleted_at` IS NULL
SELECT * FROM `profile` WHERE `profile`.`deleted_at` IS
NULL AND ((`user_id` IN (1,2,3,4,5,6)))
gorm 還在內部把兩條查詢的數據都整合好了,使用相當方便。
這只是一個簡單的預加載應用,更多應用可參考 Gorm官方文檔-預加載。
小結
預加載在單條關聯查詢中提供了更簡潔的語法,在列表關聯查詢中不僅解決了關聯查詢 N + 1 的問題,還自動整合了數據,方便快捷。
到這里,一個簡單的預加載查詢就完成了,但是可以發現查詢輸出還有很多瑕疵,如:只查詢 User 也會帶上空值 Profile,字段名和模型定義的一樣都是首字母大寫,並且時間格式不友好。
這就衍生出了幾個問題:
- 如何自定義輸出結構,只輸出指定字段?
- 如何自定義字段名,並去掉空值字段?
- 如何自定義時間格式?
下一篇將介紹如何處理查詢輸出,解決上述問題。
如發現任何問題,歡迎指正,謝謝觀看!
參考資料: Gorm官方文檔
本文出處:https://www.cnblogs.com/zhenfengxun/
本文鏈接:https://www.cnblogs.com/zhenfengxun/p/12486325.html