Gin 中使用 GORM 实现表关联查询
官方文档:https://gorm.io/zh_CN/docs/has_many.html (opens new window)
# 1、一对一
如上图所示,一个文章只有一个分类,article 和 article_cate 之间是 1 对 1 的关系。
文章表中的 cate_id 保存着文章分类的 id。
如果我们想查询文章的时候同时获取文章分类,就涉及到 1 对 1 的关联查询。
foreignkey 指定当前表的外键、references 指定关联表中和外键关联的字段
Article
package models
type Article struct {
Id int `json:"id"`
Title string `json:"title"`
Description int `json:"description"`
CateId string `json:"cate_id"`
State int `json:"state"`
ArticleCate ArticleCate `gorm:"foreignKey:CateId;references:Id"`
}
func (Article) TableName() string {
return "article"
}
ArticleCate
package models
// ArticleCate 的结构体
type ArticleCate struct {
Id int `json:"id"`
Title string `json:"title"`
State int `json:"state"`
}
func (ArticleCate) TableName() string {
return "article_cate"
}
1、查询所有文章以及文章对应的分类信息:
func (con ArticleController) Index(c *gin.Context) {
var articleList []models.Article
models.DB.Preload("ArticleCate").Limit(2).Find(&articleList)
c.JSON(200, gin.H{"result": articleList})
}
**注意:**Preload("ArticleCate")里面的 ArticleCate 为 Article struct 中定义的属性 ArticleCate
返回 JSON 数据:
[
{
"id": 1,
"title": "8 月份 CPI 同比上涨 2.8% 猪肉价格上涨 46.7%",
"description": 0,
"cate_id": "1",
"state": 1,
"ArticleCate": {
"id": 1,
"title": "国内",
"state": 1
}
},
{
"id": 2,
"title": "中国联通与中国电信共建共享 5G 网络 用户归属不变",
"description": 0,
"cate_id": "1",
"state": 1,
"ArticleCate": {
"id": 1,
"title": "国内",
"state": 1
}
}
]
2、查询所有文章以及文章对应的分类信息 指定条件:
func (con ArticleController) Index2(c *gin.Context) {
var articleList []models.Article
models.DB.Preload("ArticleCate").Where("id>=?", 4).Find(&articleList)
c.JSON(200, gin.H{ "result": articleList, })
}
返回数据:
[
{
"id": 4,
"title": "这些老师的口头禅,想起那些年“被支配的恐惧”了吗",
"description": 0,
"cate_id": "2",
"state": 1,
"ArticleCate": {
"id": 2,
"title": "国际",
"state": 1
}
},
{
"id": 5,
"title": "美国空军一号差点遭雷劈,特朗普惊呼:令人惊奇",
"description": 0,
"cate_id": "3",
"state": 1,
"ArticleCate": {
"id": 3,
"title": "娱乐",
"state": 1
}
}
]
# 2、一对多
1 对多在实际项目中用的非常多
- 比如一个点餐系统:有菜品分类、有菜品。 菜品分类和菜品之间就是一对多的关系
- 订单表和订单商品表:订单表和订单商品表之间也是一对多的关系
如上图所示,一个分类下面有很多个文章,article_cate 和 article 之间是 1 对多的关系。
文章表中的 cate_id 保存着文章分类的 id。
如果我们想查询文章分类的时候获取分类下面的文章,这个时候就涉及到一对多的关联查询。
ArticleCate
package models
// ArticleCate 的结构体
type ArticleCate struct {
Id int `json:"id"`
Title string `json:"title"`
State int `json:"state"`
Article []Article `gorm:"foreignKey:CateId"`
}
func (ArticleCate) TableName() string {
return "article_cate"
}
Article
package models
type Article struct {
Id int `json:"id"`
Title string `json:"title"`
Description int `json:"description"`
CateId string `json:"cate_id"`
State int `json:"state"`
}
func (Article) TableName() string {
return "article"
}
1、查找所有分类以及分类下面的文章信息
func (con ArticleController) Index3(c *gin.Context) {
var articleCateList []models.ArticleCate
models.DB.Preload("Article").Find(&articleCateList)
c.JSON(200, gin.H{"result": articleCateList})
}
返回数据:
[
{
"id": 1,
"title": "国内",
"state": 1,
"Article": [
{
"id": 1,
"title": "8 月份 CPI 同比上涨 2.8% 猪肉价格上涨 46.7%",
"description": 0,
"cate_id": "1",
"state": 1
},
{
"id": 2,
"title": "中国联通与中国电信共建共享 5G 网络 用户归属不变",
"description": 0,
"cate_id": "1",
"state": 1
}
]
},
{
"id": 2,
"title": "国际",
"state": 1,
"Article": [
{
"id": 3,
"title": "林郑月娥斥责暴徒破坏港铁:不能因为没生命就肆意破坏",
"description": 0,
"cate_id": "2",
"state": 1
},
{
"id": 4,
"title": "这些老师的口头禅,想起那些年“被支配的恐惧”了吗",
"description": 0,
"cate_id": "2",
"state": 1
}
]
},
...
]
2、查找所有分类以及分类下面的文章信息 指定条件
func (con ArticleController) Index4(c *gin.Context) {
var articleCateList []models.ArticleCate
models.DB.Preload("Article").Where("id>0").Offset(1).Limit(1).Find(&articleCateList)
c.JSON(200, gin.H{"result": articleCateList})
}
返回数据:
[
{
"id": 2,
"title": "国际",
"state": 1,
"Article": [
{
"id": 3,
"title": "林郑月娥斥责暴徒破坏港铁:不能因为没生命就肆意破坏",
"description": 0,
"cate_id": "2",
"state": 1
},
{
"id": 4,
"title": "这些老师的口头禅,想起那些年“被支配的恐惧”了吗",
"description": 0,
"cate_id": "2",
"state": 1
}
]
}
]
3、更多 1 对多的查询方法
地址:https://github.com/jouyouyun/examples/tree/master/gorm/related (opens new window)
4、如果我们的程序中有如下需求
- 查询文章获取文章分类信息
- 查询文章分类获取文章信息
这个时候可以这样定义 models
package models
type Article struct {
Id int `json:"id"`
Title string `json:"title"`
Description int `json:"description"`
CateId string `json:"cate_id"`
State int `json:"state"`
ArticleCate ArticleCate `gorm:"foreignKey:CateId;references:Id"`
}
func (Article) TableName() string {
return "article"
}
package models
// ArticleCate 的结构体
type ArticleCate struct {
Id int `json:"id"`
Title string `json:"title"`
State int `json:"state"`
Article []Article `gorm:"foreignKey:CateId"`
}
func (ArticleCate) TableName() string {
return "article_cate"
}
# 3、多对多
1、定义学生 课程 学生课程表 model
如果想根据课程获取选学本门课程的学生,这个时候就在 Lesson 里面关联 Student
Lesson
package models
type Lesson struct {
Id int `json:"id"`
Name string `json:"name"`
Student []*Student `gorm:"many2many:lesson_student"`
}
func (Lesson) TableName() string {
return "lesson"
}
Student
package models
type Student struct {
Id int
Number string
Password string
ClassId int
Name string
Lesson []*Lesson `gorm:"many2many:lesson_student"`
}
func (Student) TableName() string {
return "student"
}
LessonStudent
package models
type LessonStudent struct {
LessonId int
StudentId int
}
func (LessonStudent) TableName() string {
return "lesson_student"
}
2、获取学生信息 以及课程信息
func (con StudyController) test1(c *gin.Context) {
var studentList []models.Student
models.DB.Find(&studentList)
c.JSON(http.StatusOK, studentList)
}
func (con StudyController) test2(c *gin.Context) {
var lessonList []models.Lesson
models.DB.Find(&lessonList)
c.JSON(http.StatusOK, lessonList)
}
3、查询学生信息的时候获取学生的选课信息
func (con StudyController) test3(c *gin.Context) {
var studentList []models.Student
models.DB.Preload("Lesson").Find(&studentList)
c.JSON(http.StatusOK, studentList)
}
4、查询张三选修了哪些课程
func (con StudyController) test4(c *gin.Context) {
var studentList []models.Student
models.DB.Preload("Lesson").Where("id=1").Find(&studentList)
c.JSON(http.StatusOK, studentList)
}
5、课程被哪些学生选修了
func (con StudyController) test5(c *gin.Context) {
var lessonList []models.Lesson
models.DB.Preload("Student").Find(&lessonList)
c.JSON(http.StatusOK, lessonList)
}
6、计算机网络被那些学生选修了
func (con StudyController) test6(c *gin.Context) {
var lessonList []models.Lesson
models.DB.Preload("Student").Where("id=1").Find(&lessonList)
c.JSON(http.StatusOK, lessonList)
}
7、查询数据指定条件
func (con StudyController) test7(c *gin.Context) {
var lessonList []models.Lesson
models.DB.Preload("Student").Offset(1).Limit(2).Find(&lessonList)
c.JSON(http.StatusOK, lessonList)
}
8、关联查询指定子集的筛选条件
https://gorm.io/zh_CN/docs/preload.html (opens new window)
张三被开除了 查询课程被哪些学生选修的时候要去掉张三
用法:
func (con StudyController) test8(c *gin.Context) {
var access []models.Access
models.DB.Preload("AccessItem", "status=1").Order("sort desc").Where("module_id=?", 0).Find(&access)
var lessonList []models.Lesson
models.DB.Preload("Student", "id!=1").Find(&lessonList)
c.JSON(http.StatusOK, lessonList)
}
func (con StudyController) test9(c *gin.Context) {
var lessonList []models.Lesson
models.DB.Preload("Student", "id not in (1,2)").Find(&lessonList)
c.JSON(http.StatusOK, lessonList)
}
9、自定义预加载 SQL
查看课程被哪些学生选修 要求:学生 id 倒叙输出
https://gorm.io/zh_CN/docs/preload.html (opens new window)
**注意:**需要引入 gorm.io/gorm 这个包
func (con StudyController) test10(c *gin.Context) {
var lessonList []models.Lesson
models.DB.Preload("Student", func(db *gorm.DB) *gorm.DB {
return models.DB.Order("id DESC")
}).Find(&lessonList)
c.JSON(http.StatusOK, lessonList)
}
func (con StudyController) test11(c *gin.Context) {
var lessonList []models.Lesson
models.DB.Preload("Student", func(db *gorm.DB) *gorm.DB {
return models.DB.Where("id>3").Order("id DESC")
}).Find(&lessonList)
c.JSON(http.StatusOK, lessonList)
}