导读
本套笔记是学习完Gin框架之后的下一个阶段,通过Gin框架我们可以很方便的和前端进行交互,下一步就是和数据库进行交互,这就需要使用GORM。本套笔记就是带你快速上手GORM,学习完这个之后就可以完成大项目的开发了,后续还会进行大项目的讲解开发,制作不易,喜欢的就点个关注吧。
本套课程基于GORM 指南 | GORM - The fantastic ORM library for Golang, aims to be developer friendly.进行讲解,是对这个的补充解释说明。
注意
代码详解大部分是注释的形式给出,请留意代码注释。
GORM介绍
GORM(Go Object Relational Mapping)是一个在 Go 语言中使用的优秀的 ORM(对象关系映射)库。它提供了简单且强大的 API,使开发人员能够轻松地与数据库进行交互
预加载
预加载(Preloading)是指在执行数据库查询时,同时加载关联的关联数据,以减少后续访问数据库的次数。它可以提高查询性能,并避免 N+1 查询问题。
预加载示例
GORM允许使用 Preload通过多个SQL中来直接加载关系, 例如:
package main
import (
"fmt"
"gorm.io/driver/mysql"
"gorm.io/gorm"
"log"
)
type User struct {
gorm.Model
Name string
Articles []Article
}
type Article struct {
gorm.Model
Title string
Content string
UserID uint
User User
}
func main() {
dsn := "user:password@tcp(127.0.0.1:3306)/database?charset=utf8mb4&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
log.Fatal(err)
}
// 自动迁移数据库结构
err = db.AutoMigrate(&User{}, &Article{})
if err != nil {
log.Fatal(err)
}
// 创建用户
user := User{Name: "John"}
result := db.Create(&user)
if result.Error != nil {
log.Fatal(result.Error)
}
// 创建文章,并关联用户
article := Article{Title: "Hello", Content: "World", UserID: user.ID}
result = db.Create(&article)
if result.Error != nil {
log.Fatal(result.Error)
}
// 查询用户及其关联的文章
var fetchedUser User
result = db.Preload("Articles").First(&fetchedUser, user.ID)
if result.Error != nil {
log.Fatal(result.Error)
}
fmt.Println("User:", fetchedUser)
fmt.Println("Articles:")
for _, article := range fetchedUser.Articles {
fmt.Println(article)
}
}
在上述示例中,我们使用 db.Preload("Articles").First(&fetchedUser, user.ID) 查询用户并预加载关联的文章。这样,在访问 fetchedUser.Articles 时,相关的文章数据已经被提前加载,避免了额外的数据库查询。
预加载有助于优化查询性能和减少数据库查询次数,特别适用于涉及到大量关联数据的情况。
JOIN预加载
package main
import (
"fmt"
"gorm.io/driver/mysql"
"gorm.io/gorm"
"log"
)
type User struct {
gorm.Model
Name string
Articles []Article
}
type Article struct {
gorm.Model
Title string
Content string
UserID uint
User User
}
func main() {
dsn := "user:password@tcp(127.0.0.1:3306)/database?charset=utf8mb4&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
log.Fatal(err)
}
// 自动迁移数据库结构
err = db.AutoMigrate(&User{}, &Article{})
if err != nil {
log.Fatal(err)
}
// 创建用户
user := User{Name: "John"}
result := db.Create(&user)
if result.Error != nil {
log.Fatal(result.Error)
}
// 创建文章,并关联用户
article := Article{Title: "Hello", Content: "World", UserID: user.ID}
result = db.Create(&article)
if result.Error != nil {
log.Fatal(result.Error)
}
// 使用 Joins 预加载查询用户及其关联的文章
var users []User
result = db.Joins("JOIN articles ON users.id = articles.user_id").
Distinct().
Preload("Articles").
Find(&users)
if result.Error != nil {
log.Fatal(result.Error)
}
for _, user := range users {
fmt.Println("User:", user)
fmt.Println("Articles:")
for _, article := range user.Articles {
fmt.Println(article)
}
fmt.Println("------")
}
}
在上述示例中,我们使用 db.Joins("JOIN articles ON users.id = articles.user_id") 执行 JOIN 操作,关联用户和文章表。然后,使用 Preload("Articles") 预加载关联的文章数据。
预加载全部
如果你想要一次性预加载所有的关联数据,而不仅仅是单个关联字段,可以使用 Preload 方法并传递一个空字符串作为参数。这将会预加载模型定义中的所有关联。
package main
import (
"fmt"
"gorm.io/driver/mysql"
"gorm.io/gorm"
"log"
)
type User struct {
gorm.Model
Name string
Articles []Article
}
type Article struct {
gorm.Model
Title string
Content string
UserID uint
User User
}
func main() {
dsn := "user:password@tcp(127.0.0.1:3306)/database?charset=utf8mb4&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
log.Fatal(err)
}
// 自动迁移数据库结构
err = db.AutoMigrate(&User{}, &Article{})
if err != nil {
log.Fatal(err)
}
// 创建用户
user := User{Name: "John"}
result := db.Create(&user)
if result.Error != nil {
log.Fatal(result.Error)
}
// 创建文章,并关联用户
article := Article{Title: "Hello", Content: "World", UserID: user.ID}
result = db.Create(&article)
if result.Error != nil {
log.Fatal(result.Error)
}
// 查询用户及其关联的所有数据
var fetchedUser User
result = db.Preload("").First(&fetchedUser, user.ID)
if result.Error != nil {
log.Fatal(result.Error)
}
fmt.Println("User:", fetchedUser)
fmt.Println("Articles:")
for _, article := range fetchedUser.Articles {
fmt.Println(article)
}
}
在上述示例中,我们使用 db.Preload("") 查询用户并预加载所有的关联数据。这样,当访问 fetchedUser.Articles 时,所有相关的文章数据都已经被提前加载。
通过使用空字符串作为参数,可以一次性预加载模型定义中的所有关联字段。
条件预加载
GORM允许预加载与条件关联,它的工作原理类似于内联条件
// 使用条件预加载订单数据 db.Preload("Orders", "state NOT IN (?)", "cancelled").Find(&users)
// 查询用户数据:
SELECT * FROM users;
// 查询订单数据:
SELECT * FROM orders WHERE user_id IN (1,2,3,4) AND state NOT IN ('cancelled');
db.Where("state = ?", "active").Preload("Orders", "state NOT IN (?)", "cancelled").Find(&users)
// 查询状态为 "active" 的用户数据:SELECT * FROM users WHERE state = 'active';
// 查询订单数据:SELECT * FROM orders WHERE user_id IN (1,2) AND state NOT IN ('cancelled');