在 Go 语言的生态中,GORM 无疑是最受欢迎的 ORM (Object-Relational Mapping) 框架之一。它提供了一种流畅的方式来处理数据库的交互,使得 Go 语言开发者可以更加专注于业务逻辑的实现。在本篇博客中,我们将一起探索 GORM 的基本查询功能,包括如何使用 GORM 来创建查询、执行查询以及处理查询结果。
一、 基础概念:模型和关联
在开始编写查询代码之前,我们首先需要定义数据模型。GORM 的模型是基于结构体的,每个字段代表了数据库中的一个列。通过在字段上使用标签(Tag),我们可以为每个字段指定数据库的属性,如数据类型、主键、外键等。此外,模型还可以定义关联关系,如一对一、一对多和多对多等。
示例代码:
假设我们有一个简单的用户模型(User)和一个订单模型(Order),它们之间存在一对多的关系。
package main
import (
"gorm.io/driver/sqlite"
"gorm.io/gorm"
)
type User struct {
gorm.Model
Name string
Orders []Order
}
type Order struct {
gorm.Model
UserID uint
Number string
Price float64
}
func main() {
// 连接数据库
db, err := gorm.Open(sqlite.Open("test.db"), &gorm.Config{})
if err != nil {
panic("failed to connect database")
}
// 自动迁移模式
db.AutoMigrate(&User{}, &Order{})
// 创建一个用户
user := User{Name: "John Doe"}
db.Create(&user)
// 创建一个订单
order := Order{UserID: user.ID, Number: "123", Price: 999.99}
db.Create(&order)
// 查找用户及其订单
var johnDoe User
db.Preload("Orders").First(&johnDoe, 1)
// 打印查询结果
fmt.Println("User:", johnDoe.Name)
for _, order := range johnDoe.Orders {
fmt.Println("Order #:", order.ID, "Number:", order.Number, "Price:", order.Price)
}
}
二、 基本查询操作
查询单个记录: 使用 First 或 Last 方法可以根据主键或某个字段的值来查询记录。如果没有找到记录,First 方法会返回一个错误,而 Last 方法会返回一个空的模型实例。
示例代码:
var user User
db.First(&user, "id = ?", 1).Scan(&user)
if user.ID == 0 {
log.Fatalf("User not found")
}
fmt.Printf("Found user: %+v\n", user)
查询多个记录: 使用 Find 方法可以根据条件查询多个记录,并将它们填充到一个模型的切片中。如果没有找到记录,返回的切片将是空的。
示例代码:
var users []User
db.Where("age > ?", 18).Order("age DESC").Find(&users)
if len(users) == 0 {
log.Printf("No users found")
}
fmt.Printf("Found %d users.\n", len(users))
查询第一条记录: 使用 First 方法可以根据条件查询并返回第一条符合条件的记录。
示例代码:
var firstUser User
if err := db.First(&firstUser, "age = ?", 25).Error; err != nil {
log.Fatal(err)
}
fmt.Printf("Found first user: %+v\n", firstUser)
查询最后一条记录: 使用 Last 方法可以根据条件查询并返回最后一条符合条件的记录。
示例代码:
var lastUser User
if err := db.Last(&lastUser, "age = ?", 25).Error; err != nil {
log.Fatal(err)
}
fmt.Printf("Found last user: %+v\n", lastUser)
三、 高级查询操作
分页查询
使用 Limit 和 Offset 方法可以实现分页查询,其中 Limit 方法用于指定每页的记录数,Offset 方法用于指定从哪条记录开始查询。
示例代码:
pageSize := 10
offset := 0
var users []User
if err := db.Model(&User{}).Select("id, name, age").Limit(pageSize).Offset(offset).Find(&users).Error; err != nil {
log.Fatal(err)
}
fmt.Printf("Page %d, Found %d users.\n", (offset/pageSize)+1, len(users))
for _, user := range users {
fmt.Printf("User: %+v\n", user)
}
关联查询
使用 Joins, Preload 和 Associations 方法可以实现复杂的关联查询,例如内连接、左外连接、预加载关联数据等。
示例代码:
var books []Book
var authors []Author
if err := db.Model(&Book{}).Select("books.*, authors.name").Joins("LEFT JOIN authors ON authors.id = books.author_id").Find(&books).Error; err != nil {
log.Fatal(err)
}
for _, book := range books {
fmt.Printf("Book: %+v, Author: %s\n", book, book.Author.Name)
}
计数查询
使用 Count 方法可以对表中的记录进行计数查询。
示例代码:
var count int64
if err := db.Model(&User{}).Where("age > ?", 18).Count(&count).Error; err != nil {
log.Fatal(err)
}
fmt.Printf("Total number of users older than 18: %d\n", count)
条件查询
使用 Where 方法可以根据指定的条件对记录进行筛选。
示例代码:
var users []User
if err := db.Model(&User{}).Where("age > ?", 18).Order("age DESC").Find(&users).Error; err != nil {
log.Fatal(err)
}
fmt.Printf("Users older than 18: %d\n", len(users))
for _, user := range users {
fmt.Printf("User: %+v\n", user)
}
更新记录: 使用 Update 方法可以根据指定的条件更新记录。
示例代码:
var updateCount int64
if err := db.Model(&User{}).Where("age > ?", 18).Update("age", 19).Error; err != nil {
log.Fatal(err)
}
fmt.Printf("Updated users older than 18: %d\n", updateCount)
删除记录: 使用 Delete 方法可以根据指定的条件删除记录。
示例代码:
var deleteCount int64
if err := db.Model(&User{}).Where("age > ?", 18).Delete(&User{}, nil).Error; err != nil {
log.Fatal(err)
}
fmt.Printf("Deleted users older than 18: %d\n", deleteCount)
通过这些基本和高级的查询操作,我们可以构建出几乎任何复杂性的数据库查询,从而满足应用程序的需求。在实际开发中,根据具体的业务逻辑和数据模型,我们可能会组合使用这些方法,以编写出既高效又易于维护的数据库代码。