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