子句系统模块原理说明
基于1.31 本文档深入解析子句系统模块的设计原理和核心原理,阐述 SQL 组件化构建的理论基础。
一、设计原理 1.1 模块定位 在整体架构中的位置
子句系统是 GORM SQL 构建的核心组件化引擎,位于 Statement 层之下,直接负责将用户意图转换为 SQL 片段。
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 ┌─────────────────────────────────────────────────────────────┐ │ 用户 API 调用 │ │ db.Where("age > ?", 18).Order("name").Limit(10) │ └────────────────────────┬────────────────────────────────────┘ │ 解析意图 ▼ ┌─────────────────────────────────────────────────────────────┐ │ Statement 层 │ │ Statement.Clauses["WHERE"] = clause.Where{...} │ │ Statement.Clauses["ORDER BY"] = clause.OrderBy{...} │ └────────────────────────┬────────────────────────────────────┘ │ 构建顺序 ▼ ┌─────────────────────────────────────────────────────────────┐ │ 子句系统 (本模块) ★ │ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │ │ SELECT │ │ WHERE │ │ JOIN │ │ ORDER │ │ │ │ 子句 │ │ 子句 │ │ 子句 │ │ BY子句 │ │ │ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │ └────────────────────────┬────────────────────────────────────┘ │ 拼接 SQL ▼ ┌─────────────────────────────────────────────────────────────┐ │ 完整 SQL │ │ SELECT * FROM users WHERE age > 18 ORDER BY name LIMIT 10 │ └─────────────────────────────────────────────────────────────┘
核心价值
子句系统的核心价值在于将复杂的 SQL 语句分解为可组合、可复用的组件:
1 2 3 4 5 6 7 8 9 传统 SQL 构建: sql := "SELECT * FROM users WHERE age > 18 ORDER BY name" // 难以复用、难以维护、容易出错 子句系统: select := clause.Select{Columns: []clause.Column{{Name: "*"}}} where := clause.Where{Exprs: []clause.Expression{...}} order := clause.OrderBy{Columns: []clause.OrderByColumn{{Column: "name"}}} // 可组合、可测试、可扩展
1.2 设计目的 问题 1: 如何将复杂的 SQL 分解为可管理的单元?
挑战 : SQL 语句各部分有依赖关系(如 SELECT 后才能用 ORDER BY)
挑战 : 不同数据库的语法差异(如 LIMIT 用法)
解决方案 : 将每个 SQL 关键字抽象为一个 Clause
问题 2: 如何支持灵活的组合和扩展?
挑战 : 用户可能需要组合任意子句
挑战 : 需要支持数据库特定的语法
解决方案 : 定义统一的 Clause 接口,支持自定义实现
问题 3: 如何保证构建顺序的正确性?
挑战 : SQL 子句有严格的语法顺序
挑战 : 不同操作的子句需求不同(SELECT vs INSERT)
解决方案 : BuildClauses 数组定义构建顺序
1.3 结构安排依据 3 天学习时间的分配逻辑
1 2 3 4 5 6 7 8 9 10 11 Day 1: Clause 接口体系 目标: 理解组件化的设计思想 重点: Interface、Builder、Writer Day 2: 常用子句分析 目标: 理解各类子句的实现 重点: SELECT、WHERE、JOIN Day 3: 高级子句实现 目标: 理解复杂子句和自定义 重点: INSERT/UPDATE、自定义子句
由抽象到具体的学习路径
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 第 1 层: 接口抽象 ├─ Clause Interface ├─ Builder Interface └─ Expression Interface 第 2 层: 基础实现 ├─ Select ├─ Where └─ Order By 第 3 层: 复杂实现 ├─ Join ├─ On Conflict └─ Locking 第 4 层: 自定义扩展 └─ 实现 Clause Interface
1.4 与其他模块的逻辑关系 依赖关系
依赖 Schema : 子句构建需要字段的数据库列名
依赖 Dialector : 占位符、引号等需要数据库特定处理
支撑关系
支撑查询构建 : 查询 API 通过添加子句来构建 SQL
支撑回调系统 : 回调中可以修改子句来影响 SQL
二、核心原理 2.1 关键概念 概念 1: Clause 接口 定义 : Clause 是所有 SQL 子句的抽象接口,定义了子句的核心行为。
1 2 3 4 5 type Interface interface { Name() string Build(Builder) MergeClause(*Clause) }
设计原理 :
1 2 3 4 5 6 7 8 9 10 11 12 接口最小化原则: ┌─────────────────────────────────────┐ │ Clause Interface │ │ ┌──────────┐ ┌──────────┐ │ │ │ Name() │ │ Build() │ │ 核心方法 │ └──────────┘ └──────────┘ │ │ │ │ 子类实现: │ │ ├─ Where.Name() = "WHERE" │ │ ├─ Where.Build() = 构建 WHERE 部分 │ │ └─ OrderBy.Name() = "ORDER BY" │ └─────────────────────────────────────┘
Clause 结构体 :
1 2 3 4 5 6 7 8 type Clause struct { Name string BeforeExpression Expression AfterNameExpression Expression AfterExpression Expression Expression Expression Builder ClauseBuilder }
学习要点 :
Interface 定义契约,具体实现由各子句类型提供
Name() 返回 SQL 关键字(大写)
Build() 负责生成 SQL 字符串和参数
Clause 接口完整解析 (clause.go:3-90) 完整定义 :
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 type Interface interface { Name() string Build(Builder) MergeClause(*Clause) } type ClauseBuilder func (Clause, Builder) type Writer interface { WriteByte(byte ) error WriteString(string ) (int , error ) } type Builder interface { Writer WriteQuoted(field interface {}) AddVar(Writer, ...interface {}) AddError(error ) error } type Clause struct { Name string BeforeExpression Expression AfterNameExpression Expression AfterExpression Expression Expression Expression Builder ClauseBuilder }
接口方法说明 :
方法
作用
返回值
使用场景
Name()
获取子句名称
string
用于标识和排序子句
Build()
构建 SQL
无
生成 SQL 字符串和参数
MergeClause()
合并子句
无
合并多个相同类型的子句
Clause.Build() 完整实现 :
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 func (c Clause) Build(builder Builder) { if c.Builder != nil { c.Builder(c, builder) return } if c.Expression != nil { if c.BeforeExpression != nil { c.BeforeExpression.Build(builder) builder.WriteByte(' ' ) } if c.Name != "" { builder.WriteString(c.Name) builder.WriteByte(' ' ) } if c.AfterNameExpression != nil { c.AfterNameExpression.Build(builder) builder.WriteByte(' ' ) } c.Expression.Build(builder) if c.AfterExpression != nil { builder.WriteByte(' ' ) c.AfterExpression.Build(builder) } } }
Column 和 Table 结构 :
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 type Column struct { Table string Name string Alias string Raw bool } type Table struct { Name string Alias string Raw bool } const ( PrimaryKey string = "~~~py~~~" CurrentTable string = "~~~ct~~~" Associations string = "~~~as~~~" ) var ( currentTable = Table{Name: CurrentTable} PrimaryColumn = Column{Table: CurrentTable, Name: PrimaryKey} )
Clause 构建流程图 :
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 51 52 53 Clause.Build(builder) │ ▼ [有自定义构建器?] Builder != nil? │ ├─ Yes ──► 调用 c.Builder(c, builder) │ 返回 │ └─ No │ ▼ [有表达式?] Expression != nil? │ ├─ No ──► 返回 (无操作) │ └─ Yes │ ▼ [构建 BeforeExpression] if BeforeExpression != nil │ ├─► Build(builder) └─► WriteByte(' ') │ ▼ [写入子句名称] if Name != "" │ ├─► WriteString(Name) // "WHERE" └─► WriteByte(' ') │ ▼ [构建 AfterNameExpression] if AfterNameExpression != nil │ ├─► Build(builder) └─► WriteByte(' ') │ ▼ [构建主表达式] Expression.Build(builder) │ ▼ [构建 AfterExpression] if AfterExpression != nil │ ├─► WriteByte(' ') └─► Build(builder) │ ▼ 返回
MergeClause 的作用 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 func (where Where) MergeClause(clause *Clause) { if w, ok := clause.Expression.(Where); ok { exprs := make ([]Expression, len (w.Exprs)+len (where.Exprs)) copy (exprs, w.Exprs) copy (exprs[len (w.Exprs):], where.Exprs) where.Exprs = exprs } clause.Expression = where }
概念 2: Builder 接口 定义 : Builder 是 SQL 构建器的抽象,提供写入 SQL 和变量的能力。
1 2 3 4 5 6 7 8 9 10 11 type Builder interface { Writer WriteQuoted(field interface {}) AddVar(Writer, ...interface {}) AddError(error ) error } type Writer interface { WriteByte(byte ) error WriteString(string ) (int , error ) }
设计原理 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 Builder 的职责分解: 1. Writer (基础写入) └─ 直接写入字符串到 SQL buffer 2. WriteQuoted (标识符处理) └─ 根据数据库规则添加引号 └─ MySQL: `column` └─ PostgreSQL: "column" 3. AddVar (变量处理) └─ 将变量添加到 Vars 数组 └─ 写入占位符 └─ MySQL: ? └─ PostgreSQL: $1
实现示例 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 func (stmt *Statement) WriteQuoted(field interface {}) { switch v := field.(type ) { case clause.Column: stmt.QuoteTo(&stmt.SQL, v.Name) case string : stmt.QuoteTo(&stmt.SQL, v) } } func (stmt *Statement) AddVar(writer Writer, vars ...interface {}) { for _, v := range vars { switch val := v.(type ) { case clause.Expression: val.Build(stmt) default : stmt.Vars = append (stmt.Vars, val) stmt.DB.Dialector.BindVarTo(stmt, stmt, val) } } }
概念 3: Expression 表达式 定义 : Expression 是 SQL 表达式的抽象,可以是一个值、一个字段、或一个复杂表达式。
1 2 3 type Expression interface { Build(Builder) }
常用表达式类型 :
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 type Expr struct { SQL string Vars []interface {} } func (expr Expr) Build(builder Builder) { } type NamedExpr struct { SQL string Vars []interface {} } type Eq struct { Column interface {} Value interface {} } func (eq Eq) Build(builder Builder) { builder.WriteQuoted(eq.Column) builder.WriteString(" = " ) builder.AddVar(builder, eq.Value) } type And []Expressiontype Or []Expressiontype Not Expression
表达式组合 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 clause.Eq{"name" : "John" } clause.And( clause.Eq{"age" : 18 }, clause.Gt{"score" : 80 }, ) clause.Or( clause.And(clause.Eq{"age" : 18 }, clause.Eq{"status" : "active" }), clause.Eq{"vip" : true }, )
Expression 表达式完整解析 (expression.go:10-386) 完整定义 :
1 2 3 4 5 6 7 8 9 10 type Expression interface { Build(builder Builder) } type NegationExpressionBuilder interface { NegationBuild(builder Builder) }
Expr 原始表达式 :
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 51 52 53 54 55 56 57 58 59 60 61 62 type Expr struct { SQL string Vars []interface {} WithoutParentheses bool } func (expr Expr) Build(builder Builder) { var ( afterParenthesis bool idx int ) for _, v := range []byte (expr.SQL) { if v == '?' && len (expr.Vars) > idx { if afterParenthesis || expr.WithoutParentheses { if _, ok := expr.Vars[idx].(driver.Valuer); ok { builder.AddVar(builder, expr.Vars[idx]) } else { switch rv := reflect.ValueOf(expr.Vars[idx]); rv.Kind() { case reflect.Slice, reflect.Array: if rv.Len() == 0 { builder.AddVar(builder, nil ) } else { for i := 0 ; i < rv.Len(); i++ { if i > 0 { builder.WriteByte(',' ) } builder.AddVar(builder, rv.Index(i).Interface()) } } default : builder.AddVar(builder, expr.Vars[idx]) } } } else { builder.AddVar(builder, expr.Vars[idx]) } idx++ } else { if v == '(' { afterParenthesis = true } else { afterParenthesis = false } builder.WriteByte(v) } } if idx < len (expr.Vars) { for _, v := range expr.Vars[idx:] { builder.AddVar(builder, sql.NamedArg{Value: v}) } } }
比较表达式类型 :
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 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 type Eq struct { Column interface {} Value interface {} } func (eq Eq) Build(builder Builder) { builder.WriteQuoted(eq.Column) switch eq.Value.(type ) { case []string , []int , []int32 , []int64 , []uint , []uint32 , []uint64 , []interface {}: rv := reflect.ValueOf(eq.Value) if rv.Len() == 0 { builder.WriteString(" IN (NULL)" ) } else { builder.WriteString(" IN (" ) for i := 0 ; i < rv.Len(); i++ { if i > 0 { builder.WriteByte(',' ) } builder.AddVar(builder, rv.Index(i).Interface()) } builder.WriteByte(')' ) } default : if eqNil(eq.Value) { builder.WriteString(" IS NULL" ) } else { builder.WriteString(" = " ) builder.AddVar(builder, eq.Value) } } } type Neq Eqfunc (neq Neq) Build(builder Builder) { builder.WriteQuoted(neq.Column) } type Gt Eqfunc (gt Gt) Build(builder Builder) { builder.WriteQuoted(gt.Column) builder.WriteString(" > " ) builder.AddVar(builder, gt.Value) } type Gte Eqfunc (gte Gte) Build(builder Builder) { builder.WriteQuoted(gte.Column) builder.WriteString(" >= " ) builder.AddVar(builder, gte.Value) } type Lt Eqfunc (lt Lt) Build(builder Builder) { builder.WriteQuoted(lt.Column) builder.WriteString(" < " ) builder.AddVar(builder, lt.Value) } type Lte Eqfunc (lte Lte) Build(builder Builder) { builder.WriteQuoted(lte.Column) builder.WriteString(" <= " ) builder.AddVar(builder, lte.Value) } type Like Eqfunc (like Like) Build(builder Builder) { builder.WriteQuoted(like.Column) builder.WriteString(" LIKE " ) builder.AddVar(builder, like.Value) } type IN struct { Column interface {} Values []interface {} } func (in IN) Build(builder Builder) { builder.WriteQuoted(in.Column) switch len (in.Values) { case 0 : builder.WriteString(" IN (NULL)" ) case 1 : if _, ok := in.Values[0 ].([]interface {}); !ok { builder.WriteString(" = " ) builder.AddVar(builder, in.Values[0 ]) break } fallthrough default : builder.WriteString(" IN (" ) builder.AddVar(builder, in.Values...) builder.WriteByte(')' ) } }
逻辑表达式 :
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 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 func And (exprs ...Expression) Expression { if len (exprs) == 0 { return nil } if len (exprs) == 1 { if _, ok := exprs[0 ].(OrConditions); !ok { return exprs[0 ] } } return AndConditions{Exprs: exprs} } type AndConditions struct { Exprs []Expression } func (and AndConditions) Build(builder Builder) { if len (and.Exprs) > 1 { builder.WriteByte('(' ) buildExprs(and.Exprs, builder, " AND " ) builder.WriteByte(')' ) } else { buildExprs(and.Exprs, builder, " AND " ) } } func Or (exprs ...Expression) Expression { if len (exprs) == 0 { return nil } return OrConditions{Exprs: exprs} } type OrConditions struct { Exprs []Expression } func (or OrConditions) Build(builder Builder) { if len (or.Exprs) > 1 { builder.WriteByte('(' ) buildExprs(or.Exprs, builder, " OR " ) builder.WriteByte(')' ) } else { buildExprs(or.Exprs, builder, " OR " ) } } func Not (exprs ...Expression) Expression { if len (exprs) == 0 { return nil } if len (exprs) == 1 { if andCondition, ok := exprs[0 ].(AndConditions); ok { exprs = andCondition.Exprs } } return NotConditions{Exprs: exprs} } type NotConditions struct { Exprs []Expression } func (not NotConditions) Build(builder Builder) { anyNegationBuilder := false for _, c := range not.Exprs { if _, ok := c.(NegationExpressionBuilder); ok { anyNegationBuilder = true break } } if anyNegationBuilder { if len (not.Exprs) > 1 { builder.WriteByte('(' ) } for idx, c := range not.Exprs { if idx > 0 { builder.WriteString(" AND " ) } if negationBuilder, ok := c.(NegationExpressionBuilder); ok { negationBuilder.NegationBuild(builder) } else { builder.WriteString("NOT " ) c.Build(builder) } } if len (not.Exprs) > 1 { builder.WriteByte(')' ) } } else { builder.WriteString("NOT " ) if len (not.Exprs) > 1 { builder.WriteByte('(' ) } for idx, c := range not.Exprs { if idx > 0 { switch c.(type ) { case OrConditions: builder.WriteString(" OR " ) default : builder.WriteString(" AND " ) } } c.Build(builder) } if len (not.Exprs) > 1 { builder.WriteByte(')' ) } } }
表达式构建流程图 :
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 表达式构建流程 Expression.Build(builder) │ ▼ [表达式类型判断] │ ├─► Expr (原始 SQL) │ │ │ └─► 遍历 SQL 字符串 │ ├─► 遇到 '?' ──► AddVar(vars[idx++]) │ └─► 其他字符 ──► WriteByte(c) │ ├─► Eq (等于) │ │ │ ├─► WriteQuoted(Column) │ ├─► 检查 Value 类型 │ │ ├─► 切片 ──► IN (...) │ │ ├─► nil ──► IS NULL │ │ └─► 其他 ──► = ? │ └─► AddVar(Value) │ ├─► AndConditions (逻辑与) │ │ │ ├─► 多个表达式? ──Yes──► WriteByte('(') │ │ │ │ │ └─► 遍历 Exprs │ │ ├─► Build(expr) │ │ ├─► i > 0? ──► WriteString(" AND ") │ │ └─► i++ │ │ │ └─► 多个表达式? ──Yes──► WriteByte(')') │ ├─► OrConditions (逻辑或) │ │ (类似 And,但使用 " OR ") │ └─► NotConditions (逻辑非) │ (见上述详细流程)
Where 子句完整解析 (where.go:12-246) 完整定义 :
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 type Where struct { Exprs []Expression } func (where Where) Name() string { return "WHERE" } func (where Where) Build(builder Builder) { if len (where.Exprs) == 1 { if andCondition, ok := where.Exprs[0 ].(AndConditions); ok { where.Exprs = andCondition.Exprs } } for idx, expr := range where.Exprs { if v, ok := expr.(OrConditions); !ok || len (v.Exprs) > 1 { if idx != 0 { where.Exprs[0 ], where.Exprs[idx] = where.Exprs[idx], where.Exprs[0 ] } break } } buildExprs(where.Exprs, builder, " AND " ) }
buildExprs 辅助函数 :
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 51 52 53 54 55 func buildExprs (exprs []Expression, builder Builder, joinCond string ) { wrapInParentheses := false for idx, expr := range exprs { if idx > 0 { if v, ok := expr.(OrConditions); ok && len (v.Exprs) == 1 { builder.WriteString(" OR " ) } else { builder.WriteString(joinCond) } } if len (exprs) > 1 { switch v := expr.(type ) { case OrConditions: if len (v.Exprs) == 1 { if e, ok := v.Exprs[0 ].(Expr); ok { sql := strings.ToUpper(e.SQL) wrapInParentheses = strings.Contains(sql, " AND " ) || strings.Contains(sql, " OR " ) } } case AndConditions: if len (v.Exprs) == 1 { if e, ok := v.Exprs[0 ].(Expr); ok { sql := strings.ToUpper(e.SQL) wrapInParentheses = strings.Contains(sql, " AND " ) || strings.Contains(sql, " OR " ) } } case Expr: sql := strings.ToUpper(v.SQL) wrapInParentheses = strings.Contains(sql, " AND " ) || strings.Contains(sql, " OR " ) case NamedExpr: sql := strings.ToUpper(v.SQL) wrapInParentheses = strings.Contains(sql, " AND " ) || strings.Contains(sql, " OR " ) } } if wrapInParentheses { builder.WriteByte('(' ) expr.Build(builder) builder.WriteByte(')' ) wrapInParentheses = false } else { expr.Build(builder) } } }
Where 子句构建示例 :
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 where := clause.Where{ Exprs: []clause.Expression{ clause.Eq{"age" : 18 }, }, } where := clause.Where{ Exprs: []clause.Expression{ clause.Eq{"status" : "active" }, clause.Gt{"score" : 80 }, }, } where := clause.Where{ Exprs: []clause.Expression{ clause.Or( clause.Eq{"status" : "active" }, clause.Eq{"vip" : true }, ), }, } where := clause.Where{ Exprs: []clause.Expression{ clause.And( clause.Eq{"age" : 18 }, clause.Gt{"score" : 80 }, ), clause.Eq{"status" : "active" }, }, }
MergeClause 完整实现 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 func (where Where) MergeClause(clause *Clause) { if w, ok := clause.Expression.(Where); ok { exprs := make ([]Expression, len (w.Exprs)+len (where.Exprs)) copy (exprs, w.Exprs) copy (exprs[len (w.Exprs):], where.Exprs) where.Exprs = exprs } clause.Expression = where }
WHERE 子句流程图 :
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 Where.Build(builder) │ ▼ [只有一个表达式?] len(Exprs) == 1? │ ├─ Yes ──► 是 AndConditions? ──Yes──► 展开为多个表达式 │ ▼ [调整 Or 位置] 将单个 Or 条件移到前面 │ ▼ [调用 buildExprs] │ ├─► 遍历表达式 │ │ │ ├─► 添加连接符 (AND/OR) │ │ │ ├─► 检查是否需要括号 │ │ └─► 表达式包含 AND/OR ──► wrapInParentheses = true │ │ │ └─► 构建表达式 │ ├─► wrapInParentheses? ──Yes──► ( expr ) │ └─► No ──► expr │ ▼ 返回
2.2 理论基础 理论 1: 组合模式在子句系统中的应用 模式定义 : 将对象组合成树形结构以表示”部分-整体”的层次结构,使用户对单个对象和组合对象的使用具有一致性。
在子句系统中的体现 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 Expression 的组合层次: Expression (接口) │ ├─ Expr (叶子) │ └─ "age > ?" │ ├─ And (组合) │ ├─ Eq{"age": 18} │ └─ Gt{"score": 80} │ └─ Or (组合) ├─ And{...} └─ Eq{"vip": true} 构建时的递归处理: Build(Or) { Write("(") Build(And) // 递归 Write(") OR (") Build(Eq) // 递归 Write(")") }
优势 :
可以无限嵌套
统一的 Build 接口
类型安全的组合
理论 2: 访问者模式在构建中的应用 模式定义 : 在不改变集合元素的前提下,为一个对象集合上的元素定义新的操作。
在子句构建中的体现 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 Builder 作为访问者: ┌──────────────────────────────────────┐ │ Builder (访问者) │ │ ┌────────────────────────────────┐ │ │ │ Visit(Clause) → Build() │ │ │ └────────────────────────────────┘ │ └──────────────────────────────────────┘ │ 访问 ▼ ┌──────────────────────────────────────┐ │ Clause (元素) │ │ Accept(Builder) { │ │ Build(Builder) │ │ } │ └──────────────────────────────────────┘
代码体现 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 for _, name := range stmt.BuildClauses { if c, ok := stmt.Clauses[name]; ok { c.Build(stmt) } } func (where Where) Build(builder Builder) { builder.WriteString("WHERE " ) for _, expr := range where.Exprs { expr.Build(builder) } }
理论 3: 模板方法模式 模式定义 : 在父类中定义算法的骨架,将某些步骤延迟到子类实现。
在 Clause.Build 中的体现 :
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 (c Clause) Build(builder Builder) { if c.Builder != nil { c.Builder(c, builder) } else if c.Expression != nil { if c.BeforeExpression != nil { c.BeforeExpression.Build(builder) builder.WriteByte(' ' ) } if c.Name != "" { builder.WriteString(c.Name) builder.WriteByte(' ' ) } c.Expression.Build(builder) if c.AfterExpression != nil { builder.WriteByte(' ' ) c.AfterExpression.Build(builder) } } }
优势 :
2.3 学习方法 方法 1: 逆向工程法 步骤 :
从一个完整的 SQL 开始
分解为子句
找到对应的 Clause 实现
追踪 Build 过程
示例 :
1 2 SELECT id, name FROM users WHERE age > 18 ORDER BY name DESC LIMIT 10
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 -- 分解为子句 stmt.Clauses = map [string ]clause.Clause{ "SELECT" : { Name: "SELECT" , Expression: clause.Select{ Columns: []clause.Column{ {Name: "id" }, {Name: "name" }, }, }, }, "FROM" : { Name: "FROM" , Expression: clause.From{Tables: []clause.Table{{Name: "users" }}}, }, "WHERE" : { Name: "WHERE" , Expression: clause.Where{ Exprs: []clause.Expression{ clause.Expr{SQL: "age > ?" , Vars: []interface {}{18 }}, }, }, }, "ORDER BY" : { Name: "ORDER BY" , Expression: clause.OrderBy{ Columns: []clause.OrderByColumn{ {Column: clause.Column{Name: "name" }, Desc: true }, }, }, }, "LIMIT" : { Name: "LIMIT" , Expression: clause.Limit{Limit: 10 }, }, }
方法 2: 对比分析法 对比不同子句的构建方式 :
子句
结构
关键字段
Build 特点
SELECT
Select{Columns}
Columns
逗号分隔列名
WHERE
Where{Exprs}
Exprs
AND 连接表达式
JOIN
Join{Table, ON}
Table, ON
复杂的 ON 条件
ORDER BY
OrderBy{Columns}
Columns
支持 ASC/DESC
LIMIT
Limit{Limit, Offset}
Limit, Offset
简单数值
对比不同数据库的差异 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 LIMIT 10 OFFSET 20 LIMIT 10 OFFSET 20 -- 或 LIMIT 10 OFFSET 20 OFFSET 20 ROWS FETCH NEXT 10 ROWS ONLY -- 或 FETCH FIRST OFFSET 20 ROWS FETCH NEXT 10 ROWS ONLY
方法 3: 实验验证法 实验 1: 追踪 Build 过程
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 type TracingBuilder struct { gorm.Statement trace []string } func (b *TracingBuilder) WriteString(str string ) (int , error ) { b.trace = append (b.trace, fmt.Sprintf("WriteString: %q" , str)) return b.Statement.WriteString(str) } func (b *TracingBuilder) AddVar(writer Writer, vars ...interface {}) { b.trace = append (b.trace, fmt.Sprintf("AddVar: %v" , vars)) b.Statement.AddVar(writer, vars...) } stmt := &TracingBuilder{...} stmt.Clauses["WHERE" ].Build(stmt) for _, entry := range stmt.trace { fmt.Println(entry) }
实验 2: 自定义子句
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 type Top struct { N int } func (t Top) Name() string { return "TOP" } func (t Top) Build(builder Builder) { builder.WriteString(fmt.Sprintf("TOP (%d) " , t.N)) } func (t Top) MergeClause(c *Clause) { c.Expression = t } db.Clauses(clause.Top{N: 10 }).Find(&users)
2.4 实施策略 策略 1: 分层学习 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 第 1 层: 接口层 (Day 1) 目标: 理解接口设计 内容: - Clause Interface - Builder Interface - Expression Interface 第 2 层: 实现层 (Day 2) 目标: 理解具体实现 内容: - 基础子句 (SELECT, WHERE) - 复杂子句 (JOIN, ON CONFLICT) - 对比不同实现 第 3 层: 扩展层 (Day 3) 目标: 掌握自定义扩展 内容: - 实现自定义子句 - 实现自定义表达式 - 集成到查询流程
策略 2: 问题驱动 问题序列 :
为什么需要子句系统?
子句如何组合?
如何自定义子句?
策略 3: 验证反馈 验证点 1: 理解验证
1 2 3 4 5 6 7 8 9 10 11 12 13 14 func TestClauseBuild (t *testing.T) { stmt := &gorm.Statement{...} where := clause.Where{ Exprs: []clause.Expression{ clause.Expr{SQL: "age > ?" , Vars: []interface {}{18 }}, }, } where.Build(stmt) assert.Equal(t, "WHERE age > ?" , stmt.SQL.String()) assert.Equal(t, []interface {}{18 }, stmt.Vars) }
验证点 2: 组合验证
1 2 3 4 5 6 7 8 9 10 11 12 13 func TestClauseComposition (t *testing.T) { stmt := &gorm.Statement{...} expr := clause.And( clause.Eq{"status" : "active" }, clause.Gt{"score" : 80 }, ) expr.Build(stmt) expected := "(status = 'active') AND (score > 80)" assert.Equal(t, expected, stmt.SQL.String()) }
三、学习路径建议 3.1 前置知识检查
知识点
要求
检验方式
SQL 语法
熟悉 DML、DDL
能写出复杂查询
接口设计
理解接口组合
能设计简单接口
设计模式
了解组合模式
能识别应用场景
字符串处理
理解 Builder 模式
能实现简单 Builder
3.2 学习时间分配
内容
理论
实践
产出
Day 1: 接口体系
2h
1.5h
接口图
Day 2: 子句实现
1.5h
2h
对比表
Day 3: 自定义扩展
1.5h
2h
自定义子句
3.3 学习成果验收 理论验收 :
实践验收 :
四、实战代码示例 4.1 基础子句使用示例 示例 1: 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 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 package mainimport ( "fmt" "gorm.io/gorm" "gorm.io/gorm/clause" ) type User struct { ID uint Name string Email string Age int } func main () { db, _ := gorm.Open(mysql.Open(dsn), &gorm.Config{}) var users1 []User db.Find(&users1) var users2 []User db.Select("id" , "name" ).Find(&users2) var users3 []User db.Clauses(clause.Select{ Columns: []clause.Column{ {Name: "id" }, {Name: "name" }, }, }).Find(&users3) var ages []int db.Distinct("age" ).Pluck("age" , &ages) var users4 []User db.Select("id as user_id" , "name" ).Find(&users4) }
示例 2: WHERE 子句 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 db.Where("age > ?" , 18 ).Find(&users) db.Where(clause.Eq{"status" : "active" }).Find(&users) db.Where( clause.And( clause.Eq{"status" : "active" }, clause.Gt{"age" : 18 }, ), ).Find(&users) db.Where( clause.Or( clause.Eq{"status" : "active" }, clause.Eq{"vip" : true }, ), ).Find(&users) db.Where( clause.Or( clause.And( clause.Eq{"age" : 18 }, clause.Eq{"status" : "active" }, ), clause.Eq{"vip" : true }, ), ).Find(&users) db.Where( clause.Not( clause.Eq{"status" : "deleted" }, ), ).Find(&users)
示例 3: ORDER BY 子句 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 db.Order("name" ).Find(&users) db.Order("name desc" ).Find(&users) db.Clauses(clause.OrderBy{ Columns: []clause.OrderByColumn{ {Column: clause.Column{Name: "age" }, Desc: true }, }, }).Find(&users) db.Order("age desc" ).Order("name asc" ).Find(&users)
4.2 高级子句示例 示例 4: JOIN 子句 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 db.Table("users" ). Joins("JOIN profiles ON profiles.user_id = users.id" ). Find(&users) db.Table("users" ). Clauses(clause.Join{ Type: "LEFT" , Table: clause.Table{Name: "profiles" }, ON: clause.Where{ Exprs: []clause.Expression{ clause.Eq{ Column: clause.Column{Table: "profiles" , Name: "user_id" }, Value: clause.Column{Table: "users" , Name: "id" }, }, }, }, }). Find(&users)
示例 5: LIMIT 和 OFFSET 1 2 3 4 5 6 7 8 9 10 db.Limit(10 ).Offset(20 ).Find(&users) db.Clauses(clause.Limit{ Limit: 10 , Offset: 20 , }).Find(&users)
示例 6: IN 查询 1 2 3 4 5 6 7 8 9 10 11 ids := []uint {1 , 2 , 3 } db.Where("id IN ?" , ids).Find(&users) db.Where(clause.IN{ Column: "id" , Values: []interface {}{1 , 2 , 3 }, }).Find(&users)
4.3 自定义子句示例 示例 7: 实现 FOR UPDATE 子句 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 type ForUpdate struct { Tables []string Wait bool SkipLocked bool NoWait bool } func (f ForUpdate) Name() string { return "FOR UPDATE" } func (f ForUpdate) Build(builder Builder) { builder.WriteString("FOR UPDATE" ) if len (f.Tables) > 0 { builder.WriteString(" OF " ) for i, table := range f.Tables { if i > 0 { builder.WriteByte(',' ) } builder.WriteString(table) } } if f.NoWait { builder.WriteString(" NOWAIT" ) } else if f.SkipLocked { builder.WriteString(" SKIP LOCKED" ) } else if f.Wait { builder.WriteString(" WAIT" ) } } func (f ForUpdate) MergeClause(c *Clause) { c.Expression = f } db.Clauses(ForUpdate{SkipLocked: true }).Find(&user)
五、最佳实践与故障排查 5.1 子句使用最佳实践 1. 使用类型安全的表达式 1 2 3 4 5 6 7 8 9 10 db.Where( clause.And( clause.Eq{"status" : "active" }, clause.Gt{"age" : 18 }, ), ).Find(&users) db.Where("status = ? AND age > ?" , "active" , 18 ).Find(&users)
2. 合理使用子句合并 1 2 3 4 query := db.Where("age > ?" , 18 ) query = query.Where("status = ?" , "active" )
3. 使用 Distinct 去重 1 2 3 4 var uniqueAges []int db.Model(&User{}).Distinct("age" ).Pluck("age" , &uniqueAges)
5.2 常见问题与解决方案 问题 1: 表达式括号不正确 症状 :
解决方案 :
1 2 3 4 5 6 7 8 9 10 db.Where( clause.Or( clause.And( clause.Eq{"age" : 18 }, clause.Eq{"status" : "active" }, ), clause.Eq{"vip" : true }, ), ).Find(&users)
问题 2: IN 查询为空 症状 : 当传入空切片时,IN 查询语法错误
解决方案 :
1 2 3 4 5 6 7 8 9 10 11 12 13 ids := []uint {} if len (ids) > 0 { db.Where("id IN ?" , ids).Find(&users) } else { db.Find(&users) } query := db.Where("1 = 1" ) if len (ids) > 0 { query = query.Where("id IN ?" , ids) }
问题 3: ORDER BY 方向错误 症状 : 排序结果与预期相反
解决方案 :
1 2 3 4 5 6 7 8 9 db.Order("age DESC" ).Find(&users) db.Clauses(clause.OrderBy{ Columns: []clause.OrderByColumn{ {Column: clause.Column{Name: "age" }, Desc: true }, }, }).Find(&users)
六、学习验证 6.1 知识自测 基础题
Clause 接口必须实现哪些方法?
A. Name() 和 Build()
B. Name()、Build() 和 MergeClause()
C. Build() 和 MergeClause()
D. 只有 Build()
以下哪个是正确的 WHERE 子句构建方式?
db.Where("age > 18").Where("status = 'active'")
db.Where("age > 18 AND status = 'active'")
以上都可以
Expression 接口的 Build 方法接收什么参数?
A. *Statement
B. Builder
C. *DB
D. Writer
进阶题
如何实现自定义子句?
实现 Clause 接口
实现 Expression 接口
直接嵌入 Clause 结构体
继承现有子句
以下代码生成的 SQL 是什么?
1 2 3 4 5 6 7 8 9 db.Where( clause.Or( clause.And( clause.Eq{"age" : 18 }, clause.Eq{"status" : "active" }, ), clause.Eq{"vip" : true }, ), ).Find(&users)
WHERE age = 18 AND status = 'active' OR vip = true
WHERE ((age = 18 AND status = 'active') OR vip = true)
WHERE (age = 18 AND status = 'active') OR (vip = true)
6.2 实践练习 练习 1: 实现自定义表达式 实现一个 Between 表达式,用于生成 BETWEEN SQL:
1 2 3 4 5 6 7 8 9 10 11 12 13 type Between struct { Column interface {} Min interface {} Max interface {} } func (b Between) Build(builder Builder) { }
练习 2: 实现 FULL OUTER JOIN 为不支持 FULL OUTER JOIN 的数据库(如 MySQL)实现等价查询:
1 2 3 4 5 6 7 8 9 10 11 12 13 type FullJoin struct { LeftTable string RightTable string OnClause string } func (f FullJoin) Build(builder Builder) { }