This repository has been archived on 2026-05-24. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
all-in-kingsoft/hhs/GORM/03-CRUD 操作.md
T
2026-04-28 20:02:42 +08:00

6.8 KiB
Raw Blame History

tags, create time
tags create time
GORM
Go
ORM
CRUD
增删改查
First
Find
Create
Update
Delete
2026-04-28 00:00

CRUD 操作

概述

CRUD 是最基础的数据库操作。GORM 提供了语义清晰的五个核心查询方法(First / Take / Find / Get / Last)和多种更新删除 API。掌握它们就能覆盖日常 80% 的数据交互需求。

五大查询方法

flowchart LR
    A[db.Model(&User{})] --> B{First / Take / Last}
    A --> C[Find - 批量查询]
    A --> D[Get - 封装版 First]
    B --> E["单条记录<br/>返回 *User"]
    C --> F["多条记录切片<br/>返回 []User"]
    D --> E
    
    style A fill:#4FC08D,color:#fff
    style B fill:#3B82F6,color:#fff
    style C fill:#8B5CF6,color:#fff
    style E fill:#10B981,color:#fff
    style F fill:#F59E0B,color:#fff
    style D fill:#EC4899,color:#fff

方法对比

方法 返回类型 行为 是否需要 Where
First 单条指针 按主键排序取第一条,必须有条件 ✅ 必须
Take 单条指针 随机取一条(ORDER BY 随机),必须有条件 ✅ 必须
Last 单条指针 按主键倒序取最后一条 ✅ 必须
Find 切片 查询所有匹配行 ❌ 可选
Get 单条指针 First 的封装,自动处理空结果 同 First

First vs Take 的区别

// First: 按主键升序取第一条(确定性)
db.First(&user) // SELECT * FROM users ORDER BY primary_key LIMIT 1; WHERE ?

// Take: 无序取任意一条(用于随机抽样)
db.Take(&user)  // SELECT * FROM users LIMIT 1;

实际用法示例

// 1. First - 根据主键查找
var user User
db.First(&user, 10)                    // WHERE id = 10
db.First(&user, "name = ?", "john")    // WHERE name = 'john'

// 2. Take - 随机取一条(抽奖场景常用)
var product Product
db.Take(&product)                       // 任意一条商品

// 3. Find - 批量查询
var users []User
db.Where("age > ?", 18).Find(&users)   // WHERE age > 18

// 4. Get - 安全版的 First
err := db.First(&user).Error
if errors.Is(err, gorm.ErrRecordNotFound) {
    // 记录不存在
}

[!danger] 常见误区 Find 不带 Where 会查询全表!数据量大时会导致 OOM。始终记得加过滤条件,或用 Limit 兜底。

创建(Create)

// 单条插入
user := User{Name: "Alice", Age: 30}
result := db.Create(&user)

// result.RowsAffected — 影响行数
// result.Error     — 错误

// 批量插入(至少两条才生效批量优化)
users := []User{
    {Name: "Bob", Age: 25},
    {Name: "Charlie", Age: 35},
}
db.Create(&users) // INSERT INTO users ... VALUES (...), (...), (...)

[!tip] 批量插入上限 GORM 内部会将批量 insert 拆分为每批约 256 条,避免单次 SQL 过大。如需调整,可通过 db.Session(&gorm.Session{FullSaveRecords: true}) 控制行为。

插入后获取自增 ID

db.Create(&user)
fmt.Println(user.ID) // 插入后 ID 自动回填到结构体

更新(Update)

GORM 提供多种更新粒度,从单个字段到全量替换:

方法速查表

方法 签名 行为
Update (col string, value any) 更新单个字段
Updates `(value Model map[string]any)`
Save (value Model) 全量更新所有字段
UpdateColumn (col string, value any) 同 Update 但不触发 Hooks
UpdateColumns (value Model) 同 Updates 但不触发 Hooks

详细示例

// 1. Update - 单字段
db.Model(&user).Update("name", "Bob")
// UPDATE users SET name='Bob', updated_at=... WHERE id=...

// 2. Updates - 多字段(map 方式,零值也会被更新)
db.Model(&user).Updates(map[string]any{
    "name": "Alice",
    "age":  0,         // 注意:0 也会被写入
    "role": "admin",
})

// 3. Updates - 多字段(struct 方式,只更新非零值字段!)
db.Model(&user).Updates(User{Name: "Alice", Role: "admin"})
// 只有 Name 和 Role 被更新,Age 保持不变

// 4. Save - 全量覆盖(全部字段都写回去)
db.Save(&user)
// UPDATE users SET name='Alice', age=30, role='admin', ... WHERE id=...

[!warning] Zero Values 陷阱 Updates 用 struct 传入时,零值字段("" 0 false)不会被更新。如果需要更新零值,改用 map[string]any 方式:

db.Model(&user).Updates(map[string]any{"age": 0}) // ✅ 能更新为 0

批量更新

// 批量更新满足条件的行
db.Model(&User{}).Where("status = ?", "active").Update("role", "vip")
// UPDATE users SET role='vip' WHERE status='active';

删除(Delete)

// 根据主键删除
db.Delete(&user, 10) // DELETE FROM users WHERE id = 10

// 批量删除
db.Where("age < ?", 18).Delete(&User{})

// 软删除(如果模型包含 DeletedAt)
db.Delete(&user) // 不是 DELETE,而是 UPDATE ... SET deleted_at=NOW()

[!info] 软删除 vs 物理删除 物理 DELETE FROM 不可逆且丢失审计痕迹。如果你启用了 SoftDelete(模型含 DeletedAt 字段),默认执行软删除。

想强制执行物理删除?使用 Unscoped:

db.Unscoped().Delete(&user, 10) // 真正的 DELETE

错误处理

result := db.First(&user, 100)
if errors.Is(result.Error, gorm.ErrRecordNotFound) {
    fmt.Println("用户不存在")
} else if result.Error != nil {
    fmt.Println("数据库错误:", result.Error)
}
错误常量 含义
gorm.ErrRecordNotFound 查询结果为空
gorm.ErrInvalidData 插入/更新的数据无效
gorm.ErrTxAlreadyCommitted 事务已提交
gorm.ErrSavedValueNotNull 保存了非空字段的零值

CRUD 决策流程图

flowchart TD
    A[收到数据操作请求] --> B{操作类型?}
    B -->|读取| C{要几条?}
    C -->|1 条| D{需要确定性顺序?}
    D -->|是| E[First - 按主键升序]
    D -->|否/随机| F[Take - 随机一条]
    C -->|N 条| G[Find + Where 条件]
    
    B -->|新增| H{单条还是批量?}
    H -->|单条| I[Create]
    H -->|批量| J[Create slice]
    
    B -->|修改| K{改多少字段?}
    K -->|1 个| L[Update col, val]
    K -->|部分| M{需要更新零值?}
    M -->|是| N[Updates map]
    M -->|否| O[Updates struct]
    K -->|全部| P[Save]
    
    B -->|删除| Q{启用 SoftDelete?}
    Q -->|是| R[Delete - 软删除]
    Q -->|否| S[Delete - 物理删除]
    
    style A fill:#4FC08D,color:#fff
    style E fill:#3B82F6,color:#fff
    style I fill:#8B5CF6,color:#fff
    style P fill:#EC4899,color:#fff
    style R fill:#F59E0B,color:#000

关联笔记