查询构建模块原理说明
基于1.31 本文档深入解析查询构建模块的设计原理和核心原理,涵盖链式 API、条件构建、执行流程等核心内容。
一、设计原理 1.1 模块定位 在整体架构中的位置
查询构建模块是 GORM 对外提供的主要 API 层,是用户交互的主要界面,位于回调系统之上,协调子句系统和 Schema 模块完成查询构建和执行。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 ┌─────────────────────────────────────────────────────────────┐ │ 用户代码 │ │ db.Model(&User{}).Where("age > ?", 18).Find(&users) │ └────────────────────────┬────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ 链式 API (Chainable API) │ │ Where() → Select() → Order() → Limit() → ... │ └────────────────────────┬────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ 终结 API (Finisher API) │ │ Find() / First() / Create() / ... │ └────────────────────────┬────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ 回调系统执行 │ │ Before Query → Build → Execute → After Query │ └────────────────────────┬────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ 子句系统构建 SQL │ │ (调用 Clause.Build() 生成 SQL 和参数) │ └─────────────────────────────────────────────────────────────┘
核心价值
查询构建模块的核心价值在于:
流畅性 : 提供链式调用,代码读起来像自然语言
类型安全 : 通过方法重载和泛型提供类型检查
延迟执行 : 只在调用 Finisher 时才真正执行查询
组合性 : 可以任意组合查询条件
1.2 设计目的 问题 1: 如何提供流畅的链式调用体验?
挑战 : 每个方法需要返回合适的类型以支持继续链式调用
挑战 : 需要在链式调用中维护查询状态
解决方案 : 每个方法返回新的 DB 实例,共享或克隆 Statement
问题 2: 如何支持多种形式的条件输入?
挑战 : 开发者可能用字符串、map、结构体等方式表示条件
挑战 : 需要将不同形式转换为统一的内部表示
解决方案 : BuildCondition 方法统一处理各种输入
问题 3: 如何保证执行的时机正确?
挑战 : 需要区分配置阶段和执行阶段
挑战 : 需要在正确的时机构建 SQL
解决方案 : 链式 API 只配置,Finisher API 触发执行
1.3 结构安排依据 4 天学习时间的分配逻辑
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 Day 1: 链式 API 设计 目标: 理解流畅调用的实现原理 重点: getInstance()、clone、Statement 共享 Day 2: 条件构建系统 目标: 理解各种条件形式的处理 重点: BuildCondition、Expression 构建 Day 3: Finisher API 目标: 理解查询执行的完整流程 重点: 回调链、SQL 构建、结果扫描 Day 4: 高级特性 目标: 掌握 Preload、Joins、Scopes 重点: 预加载机制、连接查询、作用域复用
由外而内的学习路径
1 2 3 4 5 6 7 8 9 10 11 第 1 层: API 层 (用户看到的) db.Where().Order().Limit().Find() └─ 理解方法签名和返回值 第 2 层: 机制层 (如何工作的) getInstance() → clone → Statement └─ 理解实例创建和状态管理 第 3 层: 原理层 (为什么这样设计) 不变性、延迟执行、组合模式 └─ 理解设计决策的权衡
1.4 与其他模块的逻辑关系 依赖关系
依赖 Schema : 获取表名、列名、字段类型
依赖子句系统 : 添加和构建子句
依赖回调系统 : 执行查询前后的钩子
被依赖关系
被关联查询使用 : Preload 通过查询构建实现
**被事务处理使用: Tx 也是一个 DB 实例
二、核心原理 2.1 关键概念 概念 1: 链式调用的实现机制 定义 : 链式调用是一种编程模式,每个方法返回对象本身或相关对象,允许连续调用多个方法。
GORM 的实现方式 :
1 2 3 4 5 6 func (db *DB) Where(query interface {}, args ...interface {}) *DB { tx := db.getInstance() return tx }
getInstance() 的智能克隆 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 func (db *DB) getInstance() *DB { if db.clone > 0 { tx := &DB{Config: db.Config, Error: db.Error} if db.clone == 1 { tx.Statement = &Statement{ DB: tx, ConnPool: db.Statement.ConnPool, Context: db.Statement.Context, Clauses: map [string ]clause.Clause{}, Vars: make ([]interface {}, 0 , 8 ), SkipHooks: db.Statement.SkipHooks, } } else { tx.Statement = db.Statement.clone() tx.Statement.DB = tx } return tx } return db }
克隆策略对比 :
操作
clone 值
Statement
Config
用途
原始 DB
0
共享
共享
基础连接
首次克隆
1
新空 Statement
共享
链式调用
后续克隆
2
克隆 Statement
共享
继续链式调用
Session
1
独立
独立副本
会话隔离
NewDB
1
独立
独立
独立连接
学习要点 :
链式调用时 clone == 1,创建新的空 Statement(不克隆原 Statement)
继续链式调用时 clone > 1,才真正克隆 Statement
clone 值不会自动递增,而是根据创建方式决定(Session 设为 1,链式调用保持 1 或 2)
Config 在链式调用时共享,Session 时创建独立副本
概念 2: Statement 的状态管理 定义 : Statement 是查询的执行上下文,包含了构建查询所需的所有状态。
核心状态字段 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 type Statement struct { *DB Model interface {} Table string Schema *schema.Schema Dest interface {} ReflectValue reflect.Value Clauses map [string ]clause.Clause BuildClauses []string Selects []string Omits []string Joins []join Preloads map [string ][]interface {} SQL strings.Builder Vars []interface {} }
状态转换流程 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 初始状态 │ │ Model(&User{}) ▼ 设置 Model、Table、Schema │ │ Where("age > ?", 18) ▼ 添加 WHERE 子句到 Clauses │ │ Order("name") ▼ 添加 ORDER BY 子句到 Clauses │ │ Limit(10) ▼ 添加 LIMIT 子句到 Clauses │ │ Find(&users) ▼ 执行构建和查询
clone() 方法 :
1 2 3 4 5 6 7 8 9 10 11 12 func (stmt *Statement) clone() *Statement { return &Statement{ DB: stmt.DB, Table: stmt.Table, Model: stmt.Model, Clauses: make (map [string ]clause.Clause), BuildClauses: stmt.BuildClauses, Schema: stmt.Schema, } }
学习要点 :
Statement 是查询状态的容器
clone() 创建浅拷贝,共享 Schema
Clauses 是独立的,每个 DB 实例有自己的
概念 3: BuildCondition 条件构建 定义 : BuildCondition 是统一处理各种条件形式的入口方法。
支持的输入形式 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 db.Where("age > ?" , 18 ) db.Where(map [string ]interface {}{ "age > ?" : 18 , "name" : "John" , }) db.Where(User{Name: "John" , Age: 18 }) db.Where(clause.Expr{ SQL: "age BETWEEN ? AND ?" , Vars: []interface {}{18 , 65 }, })
构建流程 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 func (stmt *Statement) BuildCondition(query interface {}, args ...interface {}) []clause.Expression { switch v := query.(type ) { case clause.Expression: return []clause.Expression{v} case map [string ]interface {}: var exprs []clause.Expression for key, val := range v { exprs = append (exprs, stmt.buildExpression(key, val)) } return exprs case string : if len (args) > 0 { return []clause.Expression{ clause.Expr{SQL: v, Vars: args}, } } return []clause.Expression{ clause.NamedExpr{SQL: "? = ?" , Vars: []interface {}{clause.Column{Name: v}, v}}, } default : return stmt.buildStructCondition(v) } }
学习要点 :
统一入口处理多种形式
不同形式有不同的处理逻辑
最终都转换为 Expression
概念 4: 链式 API 完整实现 定义 : 链式 API 是 GORM 对外提供的主要查询接口,包括 Model、Where、Select、Order、Limit 等方法。
源码位置 : chainable_api.go
核心方法完整解析 :
1. Model() - 设置模型 1 2 3 4 5 6 func (db *DB) Model(value interface {}) (tx *DB) { tx = db.getInstance() tx.Statement.Model = value return }
功能 : 指定要操作的模型结构体使用场景 :
1 2 3 4 5 db.Model(&User{}).Update("name" , "hello" ) db.Model(&user).Update("name" , "hello" )
2. Where() - 添加 WHERE 条件 1 2 3 4 5 6 7 8 9 func (db *DB) Where(query interface {}, args ...interface {}) (tx *DB) { tx = db.getInstance() if conds := tx.Statement.BuildCondition(query, args...); len (conds) > 0 { tx.Statement.AddClause(clause.Where{Exprs: conds}) } return }
功能 : 添加 SQL WHERE 条件支持的形式 :
Where("age > ?", 18) - 字符串 + 参数
Where(map[string]interface{}{"age": 18}) - map
Where(User{Age: 18}) - 结构体
3. Select() - 选择字段 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 func (db *DB) Select(query interface {}, args ...interface {}) (tx *DB) { tx = db.getInstance() switch v := query.(type ) { case []string : tx.Statement.Selects = v case string : if len (args) > 0 { tx.Statement.AddClause(clause.Select{ Distinct: false , Columns: []clause.Column{{Name: v, Raw: true }}, }) } else if strings.Contains(v, "??" ) { tx.Statement.AddClause(clause.Select{ Distinct: false , Columns: []clause.Column{{Raw: v}}, }) } else { fields := strings.Fields(v) for _, field := range fields { tx.Statement.Selects = append (tx.Statement.Selects, field) } } } return }
4. Order() - 排序 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 func (db *DB) Order(value interface {}) (tx *DB) { tx = db.getInstance() switch v := value.(type ) { case clause.OrderBy: tx.Statement.AddClause(v) case clause.OrderByColumn: tx.Statement.AddClause(clause.OrderBy{ Columns: []clause.OrderByColumn{v}, }) case string : if v != "" { tx.Statement.AddClause(clause.OrderBy{ Columns: []clause.OrderByColumn{ {Column: clause.Column{Name: v}, Raw: true }, }, }) } } return }
5. Limit() / Offset() - 分页 1 2 3 4 5 6 7 8 9 10 11 12 13 func (db *DB) Limit(limit int ) (tx *DB) { tx = db.getInstance() tx.Statement.AddClause(clause.Limit{Limit: &limit}) return } func (db *DB) Offset(offset int ) (tx *DB) { tx = db.getInstance() tx.Statement.AddClause(clause.Limit{Offset: offset}) return }
链式 API 调用流程图 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 db.Model(&User{}).Where("age > ?", 18).Order("name").Limit(10).Find(&users) │ ▼ ┌─────────────────────────────────────────────────────────────────┐ │ Model(&User{}) │ │ ┌─────────────────────────────────────────────────────────────┐ │ │ │ 1. getInstance() → 创建新 DB 实例 │ │ │ │ 2. tx.Statement.Model = &User{} │ │ │ │ 3. 设置 Table、Schema │ │ │ └─────────────────────────────────────────────────────────────┘ │ │ ▼ ┌─────────────────────────────────────────────────────────────────┐ │ Where("age > ?", 18) │ │ ┌─────────────────────────────────────────────────────────────┐ │ │ │ 1. getInstance() → 创建新 DB 实例 │ │ │ │ 2. BuildCondition("age > ?", 18) → 构建表达式 │ │ │ │ 3. AddClause(clause.Where{Exprs: ...}) │ │ │ └─────────────────────────────────────────────────────────────┘ │ │ ▼ ┌─────────────────────────────────────────────────────────────────┐ │ Order("name") │ │ ┌─────────────────────────────────────────────────────────────┐ │ │ │ 1. getInstance() → 创建新 DB 实例 │ │ │ │ 2. AddClause(clause.OrderBy{Columns: ...}) │ │ │ └─────────────────────────────────────────────────────────────┘ │ │ ▼ ┌─────────────────────────────────────────────────────────────────┐ │ Limit(10) │ │ ┌─────────────────────────────────────────────────────────────┐ │ │ │ 1. getInstance() → 创建新 DB 实例 │ │ │ │ 2. AddClause(clause.Limit{Limit: &10}) │ │ │ └─────────────────────────────────────────────────────────────┘ │ │ ▼ ┌─────────────────────────────────────────────────────────────────┐ │ Find(&users) │ │ ┌─────────────────────────────────────────────────────────────┐ │ │ │ 1. getInstance() → 创建新 DB 实例 │ │ │ │ 2. tx.Statement.Dest = &users │ │ │ │ 3. 触发回调链执行 │ │ │ │ → Build SQL │ │ │ │ → Execute Query │ │ │ │ → Scan Results │ │ │ └─────────────────────────────────────────────────────────────┘ │ │ ▼ 返回结果
概念 5: Finisher API 完整实现 定义 : Finisher API 是触发查询执行的终结方法,包括 Find、First、Create、Update、Delete 等。
源码位置 : finisher_api.go
核心方法完整解析 :
1. Find() - 查询多条记录 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 func (db *DB) Find(dest interface {}, conds ...interface {}) (tx *DB) { tx = db.getInstance() tx.Statement.Dest = dest tx.Statement.AddClause(clause.Where{}) if len (conds) > 0 { exprs := tx.Statement.BuildCondition(conds[0 ], conds[1 :]...) tx.Statement.AddClause(clause.Where{Exprs: exprs}) } return tx.callbacks.Query().Execute(tx) }
执行流程 :
创建新的 DB 实例
设置目标变量(用于扫描结果)
构建条件表达式
触发 Query 回调链执行
2. First() - 查询单条记录 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 func (db *DB) First(dest interface {}, conds ...interface {}) (tx *DB) { tx = db.getInstance() tx.Statement.Dest = dest tx.Statement.AddClause(clause.Limit{Limit: clause.Expr{SQL: "1" }}) tx.Statement.AddClause(clause.OrderBy{ Columns: []clause.OrderByColumn{{ Column: clause.Column{Table: tx.Statement.Table, Name: clause.PrimaryKey}, }}, }) if len (conds) > 0 { exprs := tx.Statement.BuildCondition(conds[0 ], conds[1 :]...) tx.Statement.AddClause(clause.Where{Exprs: exprs}) } return tx.callbacks.Query().Execute(tx) }
特点 : 自动添加 LIMIT 1 和主键排序
3. Create() - 创建记录 1 2 3 4 5 6 7 8 9 10 11 12 13 func (db *DB) Create(value interface {}) (tx *DB) { tx = db.getInstance() tx.Statement.Dest = value tx.Statement.AddClause(clause.Insert{}) if tx.Statement.Table == "" { tx.Statement.Table = tx.Statement.Schema.Table } return tx.callbacks.Create().Execute(tx) }
4. Update() - 更新记录 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 func (db *DB) Update(column string , value interface {}) (tx *DB) { tx = db.getInstance() tx.Statement.AddClause(clause.Set{ []clause.Assignment{ {Column: clause.Column{Name: column}, Value: value}, }, }) tx.Statement.AddClause(clause.Where{ Exprs: []clause.Expression{ tx.Statement.BuildCondition(tx.Statement.Model, tx.Statement.clauseExpression(tx.Statement.PrimaryFields)...), }, }) return tx.callbacks.Update().Execute(tx) }
5. Delete() - 删除记录 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 func (db *DB) Delete(value interface {}, conds ...interface {}) (tx *DB) { tx = db.getInstance() if value != nil { tx.Statement.Dest = value tx.Statement.AddClause(clause.Where{ Exprs: []clause.Expression{ tx.Statement.BuildCondition(value), }, }) } if len (conds) > 0 { exprs := tx.Statement.BuildCondition(conds[0 ], conds[1 :]...) tx.Statement.AddClause(clause.Where{Exprs: exprs}) } return tx.callbacks.Delete().Execute(tx) }
Finisher API 执行流程对比 :
方法
目标设置
添加子句
回调类型
返回
Find
Dest
Where
Query
*DB
First
Dest
Limit + OrderBy + Where
Query
*DB
Create
Dest
Insert
Create
*DB
Update
-
Set + Where
Update
*DB
Delete
Dest
Where
Delete
*DB
2.2 理论基础 理论 1: Builder 模式 模式定义 : 将复杂对象的构建与表示分离,使得同样的构建过程可以创建不同的表示。
在查询构建中的体现 :
1 2 3 4 5 6 7 8 9 传统方式: user := User{} db.Select("name").Where("age > ?", 18).Find(&user) Builder 模式: query := db.Model(&User{}) query = query.Select("name") query = query.Where("age > ?", 18) query.Find(&user)
优势 :
分步构建,逻辑清晰
可以复用部分配置
支持条件判断添加
理论 2: 延迟执行模式 模式定义 : 延迟执行是指操作不会立即执行,而是在某个合适的时机才真正执行。
在查询构建中的体现 :
1 2 3 4 5 6 7 query := db.Model(&User{}). Where("age > ?" , 18 ). Order("name" ) query.Find(&users)
延迟执行的好处 :
优化 : 可以在执行前收集所有配置,一次性优化
灵活性 : 可以根据条件决定是否添加某个条件
安全性 : DryRun 模式下可以不执行
实现机制 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 func (db *DB) Where(query interface {}, args ...interface {}) *DB { tx := db.getInstance() tx.Statement.AddClause(clause.Where{...}) return tx } func (db *DB) Find(dest interface {}, conds ...interface {}) *DB { tx := db.getInstance() tx.Statement.Dest = dest return tx.callbacks.Query().Execute(tx) }
理论 3: 不可变对象模式 模式定义 : 创建后状态不能修改的对象,每次修改返回新对象。
在查询构建中的部分应用 :
1 2 3 4 5 6 db1 := db.Model(&User{}) db2 := db1.Where("age > ?" , 18 )
优势 :
权衡 :
创建新对象有开销
通过共享 Statement 优化
2.3 学习方法 方法 1: 调用追踪法 工具 : IDE 的调试功能
步骤 :
设置断点在 Find() 方法
运行查询
查看调用栈
观察每一步的状态变化
关键观察点 :
1 2 3 4 5 6 7 tx.Statement.Clauses["WHERE" ] tx.Statement.Dest tx.Statement.SQL.String() tx.Statement.Vars
方法 2: 对比实验法 对比不同调用方式的效果 :
1 2 3 4 5 6 7 8 9 10 11 users := []User{} db.Where("age > ?" , 18 ).Order("name" ).Find(&users) query := db.Model(&User{}) query = query.Where("age > ?" , 18 ) query = query.Order("name" ) query.Find(&users)
对比不同条件形式 :
1 2 3 4 5 6 7 8 9 10 11 12 13 db.Where("age > ? AND name = ?" , 18 , "John" ) db.Where(map [string ]interface {}{ "age > ?" : 18 , "name" : "John" , }) db.Where(User{Age: 18 , Name: "John" })
2.4 实施策略 策略 1: 渐进式学习 1 2 3 4 5 6 7 8 9 10 11 12 13 14 第 1 阶段: 简单查询 (Day 1) db.Find(&users) db.First(&user) db.Where("age > ?", 18).Find(&users) 第 2 阶段: 复杂查询 (Day 2) db.Joins("Company").Find(&users) db.Preload("Orders").Find(&users) db.Scopes(AgeGT(18)).Find(&users) 第 3 阶段: 高级查询 (Day 3-4) 子查询、分组、聚合 动态条件构建 性能优化
策略 2: 问题驱动 问题序列 :
链式调用为什么不修改原对象?
如何动态构建查询?
如何优化复杂查询?
策略 3: 验证反馈 验证点 1: 链式调用验证
1 2 3 4 5 6 7 8 9 10 11 12 func TestChaining (t *testing.T) { original := db.Model(&User{}) chained := original.Where("age > ?" , 18 ) _, ok1 := original.Statement.Clauses["WHERE" ] assert.False(t, ok1) _, ok2 := chained.Statement.Clauses["WHERE" ] assert.True(t, ok2) }
四、实战代码示例 4.1 基础查询示例 示例 1: 简单查询 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 package mainimport ( "fmt" "gorm.io/driver/mysql" "gorm.io/gorm" ) type User struct { ID uint Name string Email string Age int } func main () { db, _ := gorm.Open(mysql.Open("dsn" ), &gorm.Config{}) var users []User db.Find(&users) db.Where("age > ?" , 18 ).Find(&users) var user User db.First(&user, 1 ) }
示例 2: 链式查询 1 2 3 4 5 6 7 8 db.Model(&User{}). Where("age > ?" , 18 ). Where("status = ?" , "active" ). Order("created_at DESC" ). Limit(10 ). Offset(20 ). Find(&users)
4.2 动态查询构建 示例 3: 条件式查询 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 query := db.Model(&User{}) if name := r.URLParam("name" ); name != "" { query = query.Where("name = ?" , name) } if minAge := r.URLParam("min_age" ); minAge != "" { query = query.Where("age >= ?" , minAge) } if status := r.URLParam("status" ); status != "" { query = query.Where("status = ?" , status) } query.Find(&users)
4.3 高级查询示例 示例 4: 使用 Scopes 复用查询逻辑 1 2 3 4 5 6 7 8 9 10 11 12 13 func AgeGreaterThan (age int ) func (db *gorm.DB) *gorm.DB { return func (db *gorm.DB) *gorm.DB { return db.Where("age > ?" , age) } } func ActiveUsers (db *gorm.DB) *gorm.DB { return db.Where("status = ?" , "active" ) } db.Scopes(AgeGreaterThan(18 ), ActiveUsers).Find(&users)
示例 5: 子查询 1 2 3 4 5 6 7 8 9 10 var users []Userdb.Where("age > (?)" , db.Model(&User{}). Select("AVG(age)" ). Where("status = ?" , "active" )). SubQuery()). Find(&users)
五、最佳实践与故障排查 5.1 查询构建最佳实践 1. 使用 Model 指定表 1 2 3 4 5 db.Model(&User{}).Where("age > ?" , 18 ).Find(&users) db.Table("users" ).Where("age > ?" , 18 ).Find(&users)
2. 避免全局更新 1 2 3 4 5 6 7 8 9 10 db.Model(&User{}).Update("status" , "inactive" ) db.Model(&User{}).Where("status = ?" , "active" ).Update("status" , "inactive" ) db.Session(&gorm.Session{AllowGlobalUpdate: true }). Model(&User{}). Update("status" , "inactive" )
3. 使用 Preload 预加载关联 1 2 3 4 5 6 7 8 9 users := []User{} db.Find(&users) for _, user := range users { db.Model(&user).Related(&orders) } db.Preload("Orders" ).Find(&users)
5.2 常见问题与解决方案 问题 1: 查询条件不生效 症状 : 添加的 WHERE 条件没有生效
原因 : 使用了错误的链式调用顺序
1 2 3 4 5 db.Find(&users).Where("age > ?" , 18 ) db.Where("age > ?" , 18 ).Find(&users)
问题 2: 更新失败 症状 : Update 没有更新任何记录
原因 : 缺少主键或 WHERE 条件
1 2 3 4 5 6 7 8 9 10 user := User{Name: "John" , Age: 30 } db.Model(&user).Update("age" , 31 ) db.First(&user, 1 ) db.Model(&user).Update("age" , 31 ) db.Model(&User{}).Where("name = ?" , "John" ).Update("age" , 31 )
问题 3: 结构体条件只查询非零值 症状 : 使用结构体作为条件时,零值字段被忽略
1 2 3 4 5 6 7 8 9 10 11 12 13 db.Where(User{Name: "John" , Age: 0 }).First(&user) db.Where(map [string ]interface {}{"name" : "John" , "age" : 0 }).First(&user) type User struct { Name string Age *int } age := 0 db.Where(User{Name: "John" , Age: &age}).First(&user)
六、学习验证 6.1 知识自测 基础题
以下哪个是 Finisher API?
A. Where()
B. Select()
C. Find()
D. Order()
链式 API 的返回值是什么?
A. *gorm.DB
B. error
C. int64
D. bool
First() 方法会自动添加什么?
A. WHERE
B. LIMIT 1
C. ORDER BY
D. LIMIT 1 和 ORDER BY
进阶题
以下代码会产生几次查询?
1 db.Preload("Orders" ).Preload("Profile" ).Find(&users)
A. 1 次
B. 2 次
C. 3 次
D. 4 次
如何实现动态查询?
使用 Scope
使用条件判断
使用 map 作为条件
以上都可以
6.2 实践练习 练习 1: 实现分页查询 1 2 3 4 func Paginate (db *gorm.DB, page, pageSize int ) *gorm.DB { }
练习 2: 实现软删除查询 1 2 3 4 func NotDeleted (db *gorm.DB) *gorm.DB { }
三、学习路径建议 3.1 前置知识检查
知识点
要求
检验方式
方法链
理解返回值传递
能设计简单链式 API
SQL 基础
熟悉 SELECT/INSERT/UPDATE/DELETE
能写出复杂查询
反射基础
理解反射获取字段值
能遍历结构体字段
3.2 学习时间分配
内容
理论
实践
产出
Day 1: 链式 API
2h
1.5h
调用流程图
Day 2: 条件构建
1.5h
2h
条件形式对比
Day 3: Finisher
2h
1.5h
执行流程图
Day 4: 高级特性
1.5h
2h
优化示例
3.3 学习成果验收 理论验收 :
实践验收 :