GORM是Go语言生态中最流行的ORM框架,提供了链式调用API简化数据库操作。在实际业务开发中,复杂查询场景(动态条件拼接、子查询、联表预加载、批量操作)对GORM的使用提出了更高要求。GORM的高级查询能力包括Scopes复用、动态条件构建、关联预加载、原生SQL混合使用和事务管理,掌握这些能力可以应对绝大多数业务场景的数据访问需求。
GORM模型定义与关联关系配置
GORM通过结构体标签定义模型与数据库表的映射关系,支持一对一、一对多、多对多和多态关联。合理的模型定义是高效查询的基础,索引声明和字段约束直接影响查询性能。
package model
import (
"time"
"gorm.io/gorm"
)
type User struct {
ID uint `gorm:"primaryKey"`
Username string `gorm:"uniqueIndex;size:50;not null"`
Email string `gorm:"uniqueIndex;size:100;not null"`
Phone string `gorm:"index;size:20"`
Status int8 `gorm:"default:1;index"`
Role string `gorm:"size:20;default:user"`
CreatedAt time.Time
UpdatedAt time.Time
DeletedAt gorm.DeletedAt `gorm:"index"`
Orders []Order `gorm:"foreignKey:UserID"`
Roles []Role `gorm:"many2many:user_roles;"`
}
type Order struct {
ID uint `gorm:"primaryKey"`
OrderNo string `gorm:"uniqueIndex;size:32;not null"`
UserID uint `gorm:"index;not null"`
TotalAmount float64 `gorm:"type:decimal(12,2);not null"`
Status string `gorm:"size:20;default:pending;index"`
PaymentTime *time.Time `gorm:"index"`
CreatedAt time.Time
UpdatedAt time.Time
User User `gorm:"foreignKey:UserID"`
Items []OrderItem `gorm:"foreignKey:OrderID"`
}
type OrderItem struct {
ID uint `gorm:"primaryKey"`
OrderID uint `gorm:"index;not null"`
ProductID uint `gorm:"index;not null"`
Quantity int `gorm:"not null;default:1"`
Price float64 `gorm:"type:decimal(10,2);not null"`
Product Product `gorm:"foreignKey:ProductID"`
}
type Product struct {
ID uint `gorm:"primaryKey"`
Name string `gorm:"size:200;not null;index"`
Price float64 `gorm:"type:decimal(10,2);not null"`
Stock int `gorm:"default:0"`
Category string `gorm:"size:50;index"`
}
type Role struct {
ID uint `gorm:"primaryKey"`
Name string `gorm:"size:50;uniqueIndex;not null"`
Code string `gorm:"size:50;uniqueIndex;not null"`
Users []User `gorm:"many2many:user_roles;"`
}
Scopes复用查询条件与分页封装
GORM的Scopes机制允许将常用查询条件封装为可复用函数,通过链式调用组合多个条件。分页查询、软删除过滤、状态筛选等通用逻辑通过Scopes实现,避免在业务代码中重复编写查询条件。
package repository
import "gorm.io/gorm"
// 分页Scope
func Paginate(page, pageSize int) func(db *gorm.DB) *gorm.DB {
return func(db *gorm.DB) *gorm.DB {
if page <= 0 {
page = 1
}
if pageSize <= 0 || pageSize > 100 {
pageSize = 10
}
offset := (page - 1) * pageSize
return db.Offset(offset).Limit(pageSize)
}
}
// 活跃用户Scope
func ActiveUser(db *gorm.DB) *gorm.DB {
return db.Where("status = ?", 1)
}
// 按角色筛选Scope
func WithRole(role string) func(db *gorm.DB) *gorm.DB {
return func(db *gorm.DB) *gorm.DB {
if role != "" {
return db.Where("role = ?", role)
}
return db
}
}
// 时间范围Scope
func CreatedBetween(start, end string) func(db *gorm.DB) *gorm.DB {
return func(db *gorm.DB) *gorm.DB {
if start != "" && end != "" {
return db.Where("created_at BETWEEN ? AND ?", start, end)
}
if start != "" {
return db.Where("created_at >= ?", start)
}
if end != "" {
return db.Where("created_at <= ?", end)
}
return db
}
}
// 搜索Scope(多字段模糊匹配)
func SearchUser(keyword string) func(db *gorm.DB) *gorm.DB {
return func(db *gorm.DB) *gorm.DB {
if keyword == "" {
return db
}
like := "%" + keyword + "%"
return db.Where(
"username LIKE ? OR email LIKE ? OR phone LIKE ?",
like, like, like,
)
}
}
// 排序Scope(白名单防注入)
func OrderBy(field, direction string) func(db *gorm.DB) *gorm.DB {
return func(db *gorm.DB) *gorm.DB {
if field == "" {
return db.Order("created_at DESC")
}
dir := "ASC"
if direction == "desc" || direction == "DESC" {
dir = "DESC"
}
allowedFields := map[string]bool{
"id": true, "username": true, "created_at": true, "updated_at": true,
}
if !allowedFields[field] {
return db
}
return db.Order(field + " " + dir)
}
}
// 使用示例
func GetUserList(db *gorm.DB, params QueryParams) ([]User, int64, error) {
var users []User
var total int64
query := db.Model(&User{}).
Scopes(
ActiveUser,
WithRole(params.Role),
SearchUser(params.Keyword),
CreatedBetween(params.StartDate, params.EndDate),
OrderBy(params.SortBy, params.SortDir),
)
query.Count(&total)
err := query.Scopes(Paginate(params.Page, params.PageSize)).
Find(&users).Error
return users, total, err
}
动态条件构建与查询构造器模式
业务场景中查询条件往往不固定,需要根据入参动态拼接查询条件。GORM的链式调用天然支持条件叠加,配合条件判断实现动态查询构建。
package repository
import (
"strings"
"gorm.io/gorm"
)
type OrderQueryParams struct {
OrderNo string
UserID *uint
Status string
MinAmount *float64
MaxAmount *float64
StartDate string
EndDate string
SortBy string
SortDir string
Page int
PageSize int
}
type OrderQueryBuilder struct {
db *gorm.DB
params OrderQueryParams
}
func NewOrderQueryBuilder(db *gorm.DB, params OrderQueryParams) *OrderQueryBuilder {
return &OrderQueryBuilder{db: db, params: params}
}
func (b *OrderQueryBuilder) Build() *gorm.DB {
query := b.db.Model(&Order{})
if b.params.OrderNo != "" {
query = query.Where("order_no = ?", b.params.OrderNo)
}
if b.params.UserID != nil {
query = query.Where("user_id = ?", *b.params.UserID)
}
if b.params.Status != "" {
statuses := strings.Split(b.params.Status, ",")
if len(statuses) == 1 {
query = query.Where("status = ?", statuses[0])
} else {
query = query.Where("status IN ?", statuses)
}
}
if b.params.MinAmount != nil {
query = query.Where("total_amount >= ?", *b.params.MinAmount)
}
if b.params.MaxAmount != nil {
query = query.Where("total_amount <= ?", *b.params.MaxAmount)
}
if b.params.StartDate != "" {
query = query.Where("created_at >= ?", b.params.StartDate)
}
if b.params.EndDate != "" {
query = query.Where("created_at <= ?", b.params.EndDate+" 23:59:59")
}
if b.params.SortBy != "" {
dir := "ASC"
if b.params.SortDir == "desc" {
dir = "DESC"
}
query = query.Order(b.params.SortBy + " " + dir)
} else {
query = query.Order("created_at DESC")
}
return query
}
func (b *OrderQueryBuilder) Count() (int64, error) {
var total int64
err := b.Build().Count(&total).Error
return total, err
}
func (b *OrderQueryBuilder) Find() ([]Order, error) {
var orders []Order
query := b.Build()
page := b.params.Page
pageSize := b.params.PageSize
if page <= 0 {
page = 1
}
if pageSize <= 0 || pageSize > 100 {
pageSize = 20
}
query = query.Offset((page - 1) * pageSize).Limit(pageSize)
query = query.Preload("User").Preload("Items").Preload("Items.Product")
err := query.Find(&orders).Error
return orders, err
}
联表查询与子查询实战
GORM的Joins和子查询能力处理复杂关联查询场景。Preload使用独立查询加载关联数据(避免JOIN导致的笛卡尔积),Joins使用JOIN语法联表查询。根据场景选择合适的关联查询方式。
// Preload方式:独立查询,避免JOIN膨胀
func GetUserWithOrders(db *gorm.DB, userID uint) (*User, error) {
var user User
err := db.Preload("Orders", "status = ?", "paid").
Preload("Orders.Items").
First(&user, userID).Error
return &user, err
}
// Joins方式:JOIN联表查询
func GetUsersWithPaidOrders(db *gorm.DB) ([]User, error) {
var users []User
err := db.Joins("JOIN orders ON orders.user_id = users.id").
Where("orders.status = ?", "paid").
Distinct("users.id").
Find(&users).Error
return users, err
}
// 子查询:查询消费金额TOP10用户
func GetTopSpenders(db *gorm.DB, limit int) ([]map[string]interface{}, error) {
var results []map[string]interface{}
err := db.Table("users").
Select("users.id, users.username, SUM(orders.total_amount) as total_spent").
Joins("JOIN orders ON orders.user_id = users.id").
Where("orders.status = ?", "paid").
Group("users.id").
Order("total_spent DESC").
Limit(limit).
Find(&results).Error
return results, err
}
// 子查询作为条件
func GetUsersWithPendingOrders(db *gorm.DB) ([]User, error) {
var users []User
subQuery := db.Table("orders").
Select("user_id").
Where("status = ?", "pending")
err := db.Where("id IN (?)", subQuery).Find(&users).Error
return users, err
}
// 多级关联预加载条件控制
func GetOrderDetail(db *gorm.DB, orderID uint) (*Order, error) {
var order Order
err := db.
Preload("User").
Preload("Items", func(db *gorm.DB) *gorm.DB {
return db.Order("quantity DESC")
}).
Preload("Items.Product", func(db *gorm.DB) *gorm.DB {
return db.Select("id, name, price, stock")
}).
First(&order, orderID).Error
return &order, err
}
批量操作与事务管理
批量插入、批量更新和批量删除是高频数据库操作。GORM的CreateInBatches支持分批插入大数量数据,事务管理确保多表操作的原子性。
// 批量插入(分批,每批1000条)
func BatchCreateProducts(db *gorm.DB, products []Product) error {
return db.CreateInBatches(products, 1000).Error
}
// 批量更新(使用Map)
func BatchUpdateOrderStatus(db *gorm.DB, orderIDs []uint, status string) error {
return db.Model(&Order{}).
Where("id IN ?", orderIDs).
Updates(map[string]interface{}{
"status": status,
"updated_at": time.Now(),
}).Error
}
// 事务管理:订单创建
func CreateOrderWithItems(db *gorm.DB, req CreateOrderRequest) (*Order, error) {
var order Order
err := db.Transaction(func(tx *gorm.DB) error {
order = Order{
OrderNo: generateOrderNo(),
UserID: req.UserID,
TotalAmount: req.TotalAmount,
Status: "pending",
}
if err := tx.Create(&order).Error; err != nil {
return err
}
var items []OrderItem
for _, item := range req.Items {
var product Product
if err := tx.Where("id = ? AND stock >= ?",
item.ProductID, item.Quantity).
First(&product).Error; err != nil {
return fmt.Errorf("商品 %d 库存不足", item.ProductID)
}
items = append(items, OrderItem{
OrderID: order.ID,
ProductID: item.ProductID,
Quantity: item.Quantity,
Price: product.Price,
})
if err := tx.Model(&Product{}).
Where("id = ? AND stock >= ?",
item.ProductID, item.Quantity).
Update("stock", gorm.Expr("stock - ?", item.Quantity)).
Error; err != nil {
return err
}
}
if err := tx.CreateInBatches(items, 100).Error; err != nil {
return err
}
return nil
})
return &order, err
}
GORM的高级查询能力覆盖了从模型定义到复杂关联查询的完整链路。Scopes机制是代码复用的关键,将通用查询条件抽象为可组合的函数。动态条件构建器模式将查询逻辑与业务逻辑解耦,便于维护和扩展。联表查询时优先使用Preload避免JOIN导致的笛卡尔积膨胀,仅在需要关联表字段作为筛选条件时使用Joins。事务管理中注意错误传播,内层事务的错误会自动触发外层事务回滚。批量操作使用CreateInBatches和Case When批量更新,避免循环单条操作产生大量SQL执行。
原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/go-yu-yan-gorm-kuang-jia-gao-ji-cha-xun-yu-dong-tai-sql-gou/