4. GORM CRUD 操作:基础用法与工程实践

CRUD(Create、Read、Update、Delete)是数据持久化的四大基石。在微服务架构中,GORM 的 CRUD 操作不仅要满足单表基础增删改查,还需应对批量写入、软删除策略、硬删除清理等复杂场景。本章从基础用法出发,逐步深入到批量操作与删除策略,帮助你在工程落地中做出正确选择。

工程提示: 本章所有代码示例均基于 GORM v2 版本。建议在项目中统一使用 *gorm.DB 实例,避免每次操作都创建新连接。

4.1 基础 CRUD 操作

以下以 User 模型为例,演示最常用的增、查、改、删操作。

type User struct {
    ID        uint      `gorm:"primaryKey"`
    Name      string    `gorm:"size:100;not null"`
    Email     string    `gorm:"uniqueIndex;size:150"`
    Age       int
    CreatedAt time.Time
    UpdatedAt time.Time
    DeletedAt gorm.DeletedAt `gorm:"index"` // 软删除字段
}

4.1.1 Create — 创建记录

GORM 提供多种创建方式,适用于单条插入与批量插入。

// 单条创建
user := User{Name: "张三", Email: "zhangsan@example.com", Age: 28}
result := db.Create(&user)
fmt.Println("新记录 ID:", user.ID, "影响行数:", result.RowsAffected)

// 指定字段创建
db.Select("Name", "Email").Create(&user)

// 忽略字段创建
db.Omit("Age").Create(&user)
关键点: Create 会回填主键、CreatedAt、UpdatedAt。使用 Select 或 Omit 可精细控制写入字段,避免零值覆盖。

4.1.2 Read — 查询记录

GORM 的查询 API 覆盖单条、多条、条件、排序、分页等场景。

// 查询单条
var user User
db.First(&user, 1)                 // 按主键查询
db.First(&user, "email = ?", "zhangsan@example.com")

// 查询多条
var users []User
db.Where("age > ?", 25).Find(&users)
db.Where(&User{Name: "张三"}).Find(&users)

// 条件组合
db.Where("age > ? AND name LIKE ?", 20, "%张%").Find(&users)

// 排序与分页
db.Order("age DESC").Limit(10).Offset(0).Find(&users)

// 指定查询字段
db.Select("name, email").Find(&users)
避坑指南: First 在未找到记录时返回 ErrRecordNotFound,建议使用 Find 配合 len(users) 判断是否为空,避免错误处理分支过多。

4.1.3 Update — 更新记录

更新操作分为单字段更新、多字段更新、条件更新。

// 单条更新
db.Model(&user).Update("age", 30)

// 多字段更新
db.Model(&user).Updates(User{Name: "李四", Age: 32})

// 条件更新
db.Model(&User{}).Where("age < ?", 18).Update("age", 18)

// 使用 map 更新(零值字段不会被忽略)
db.Model(&user).Updates(map[string]interface{}{"name": "", "age": 0})
注意: 使用 struct 更新时,零值字段(如 ""、0)不会被更新;若需更新零值,必须使用 map[string]interface{}。

4.1.4 Delete — 删除记录

GORM 默认启用软删除,即不真正删除数据,而是设置 DeletedAt 字段。硬删除则直接从数据库移除。

// 软删除(设置 DeletedAt 时间戳)
db.Delete(&user, 1)

// 条件软删除
db.Where("age < ?", 10).Delete(&User{})

// 硬删除(永久移除)
db.Unscoped().Delete(&user, 1)
工程建议: 微服务中推荐默认使用软删除,便于数据恢复与审计。仅在明确需要释放存储空间或清理敏感数据时使用硬删除。

4.2 批量操作

微服务场景下,批量写入与批量更新是高频需求。GORM 提供了高效的批量 API。

4.2.1 批量创建

var users = []User{
    {Name: "王五", Email: "wangwu@example.com", Age: 25},
    {Name: "赵六", Email: "zhaoliu@example.com", Age: 30},
    {Name: "孙七", Email: "sunqi@example.com", Age: 35},
}

// 批量插入(默认分批,每批 100 条)
db.Create(&users)

// 自定义批次大小
db.CreateInBatches(&users, 50)
性能对比: 批量创建相比逐条 Create 可减少 80% 以上的数据库往返次数。建议根据业务量调整批次大小,通常 100~500 条/批。

4.2.2 批量更新

// 批量更新同一字段
db.Model(&User{}).Where("age < ?", 20).Update("age", 20)

// 批量更新不同值(使用 Case When)
db.Model(&User{}).Where("id IN ?", []uint{1,2,3}).Updates(map[string]interface{}{
    "age": gorm.Expr("CASE id WHEN 1 THEN 25 WHEN 2 THEN 30 WHEN 3 THEN 35 END"),
})
注意: 批量更新不同值时,GORM 不直接支持 Updates 传入多行不同数据。推荐使用 gorm.Expr 构造原生 SQL 或使用第三方库 gorm-bulk-upsert。

4.2.3 批量查询与分页

// 游标分页(推荐用于大数据量)
var users []User
db.Where("id > ?", lastID).Order("id ASC").Limit(100).Find(&users)

// 传统 offset 分页
db.Offset(0).Limit(20).Find(&users)
工程提示: 微服务中避免使用大偏移量分页(如 OFFSET 100000),性能会急剧下降。推荐使用游标分页(基于主键或唯一索引)或键集分页。

4.3 软删除与硬删除

删除策略直接影响数据恢复能力、查询性能与存储成本。下表对比了两种策略的核心差异:

特性 软删除 硬删除
数据恢复 支持(恢复 DeletedAt 为 NULL) 不支持
查询性能 需额外过滤 deleted_at IS NULL 无额外过滤
存储占用 保留数据,占用持续增长 释放空间
审计追溯 可追溯删除时间与操作 无法追溯
GORM 默认 是(模型包含 gorm.DeletedAt) 需显式调用 Unscoped()

4.3.1 软删除原理

当模型包含 gorm.DeletedAt 字段时,GORM 自动启用软删除。删除操作实际执行 UPDATE 而非 DELETE:

// 实际执行的 SQL
UPDATE users SET deleted_at = NOW() WHERE id = 1 AND deleted_at IS NULL;

查询时,GORM 自动追加 WHERE deleted_at IS NULL 条件,确保软删除记录不可见。

4.3.2 软删除的查询与恢复

// 查询包含软删除的记录
var users []User
db.Unscoped().Where("name = ?", "张三").Find(&users)

// 恢复软删除记录
db.Unscoped().Model(&user).Update("deleted_at", nil)

// 永久删除(硬删除)
db.Unscoped().Delete(&user, 1)
关键点: Unscoped() 会移除所有软删除过滤条件,包括查询和删除操作。使用 Unscoped().Delete() 即为硬删除。

4.3.3 硬删除场景

以下场景建议使用硬删除:

  • 临时数据: 如日志、缓存表、中间结果表。
  • 敏感数据: 如用户注销后需彻底清除个人隐私信息。
  • 存储受限: 数据量极大且无恢复需求时,硬删除可释放存储。
  • 合规要求: 某些法规要求数据不可保留超过指定期限。

4.4 CRUD 操作流程总览

下图展示了 GORM CRUD 操作的核心流程与删除策略分支:

GORM CRUD 操作流程 CRUD 操作入口 Create Read Update Delete 软删除? UPDATE deleted_at DELETE 物理行 软删除:数据保留,查询自动过滤 deleted_at IS NULL 硬删除:数据永久移除,需显式调用 Unscoped().Delete()

4.5 工程最佳实践

在微服务架构中落地 GORM CRUD,建议遵循以下原则:

  • 统一模型基类: 定义包含 ID、CreatedAt、UpdatedAt、DeletedAt 的 BaseModel,所有业务模型嵌入该基类。
  • 批量操作使用事务: 批量创建或更新时,使用 db.Transaction 包裹,保证原子性。
  • 软删除字段加索引: deleted_at 字段应建立索引,避免全表扫描。
  • 避免 N+1 查询: 使用 Preload 或 Joins 预加载关联数据,减少数据库查询次数。
  • 日志与监控: 开启 GORM 的慢查询日志(SlowThreshold),并集成链路追踪,便于排查性能问题。
总结: 基础 CRUD 是 GORM 的入门操作,但批量处理与删除策略的选择直接影响微服务的性能与数据安全。掌握软删除与硬删除的适用场景,结合事务与索引优化,才能构建健壮的数据持久化层。