正确做法是只用反射读字段名和标签,值一律参数化处理;sqlx的NamedExec已安全实现该逻辑,除非有动态表名等特殊需求,否则不应手写反射生成INSERT语句。

用 reflect 从结构体生成 INSERT 语句,别碰 reflect.Value.Interface() 直接拼 SQL
直接把反射取出来的值转成字符串再拼进 SQL,等于裸奔——SQL 注入风险拉满,nil 字段会 panic,时间类型、指针、切片全都会崩。正确做法是:只用反射读字段名和标签,值一律走参数化(? 或 $1),让数据库驱动处理类型转换。
实操要点:
- 用
reflect.TypeOf拿结构体字段名,配合structTag(比如db:"user_name")映射列名 - 用
reflect.Value遍历时,对每个字段先调.IsValid()和.CanInterface(),再判断是否为指针/接口,用.Elem()解引用 - 跳过匿名字段、未导出字段(
IsExported() == false),否则 runtime panic - 值本身不参与字符串拼接,只放进
[]interface{}参数切片,交给db.Exec(query, args...)
sqlx 的 StructScan 和 NamedExec 已经做了反射,自己手写容易翻车
你写的“反射生成 SQL”大概率是在重复造轮子。sqlx 库的 NamedExec 支持结构体直接传参:db.NamedExec("INSERT INTO users (name, age) VALUES (:name, :age)", user),它内部用 reflect 提取字段+标签,自动绑定命名参数,还支持嵌套结构体、忽略字段(db:"-" )。
除非你有特殊需求(比如动态表名、条件列集合),否则别自己解析结构体。常见翻车点:
- 没处理
time.Time的Scan/Value方法,导致插入时变成空字符串或 panic - 把
*string当成string处理,nil指针直接.String()崩溃 - 字段标签写错(比如
json:"name"但没写db:"name"),反射读出来为空,SQL 列名错位
手动反射生成 INSERT 时,字段顺序必须和 reflect.Type.Field(i) 一致
Go 的 reflect.StructField 顺序就是源码中定义的顺序,不是按字母或标签排序。如果你结构体字段顺序和数据库建表语句列顺序不一致,生成的 VALUES(?, ?, ?) 就会错位——比如 id 插到 name 列里。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
安全做法:
- 建表 SQL 显式声明列顺序,和结构体字段顺序对齐(推荐)
- 或者用 map 先收集字段名→值映射,再按目标列名列表(如
[]string{"id", "name", "created_at"})顺序提取值 - 千万别依赖
range structMap这种无序遍历,map key 顺序在 Go 中不保证 - 注意
omitempty类似逻辑要自己实现:检查字段值是否为零值(reflect.Zero(field.Type).Interface() == fieldValue.Interface()),再决定是否包含该字段
PostgreSQL 的 $1, $2 和 SQLite/MySQL 的 ? 占位符不能混用
反射生成的 SQL 字符串本身不含参数值,但占位符风格必须匹配驱动。用 database/sql 接 PostgreSQL 时,如果生成的是 INSERT INTO t(a,b) VALUES (?, ?),lib/pq 会报错 ERROR: syntax error at or near "?" —— 它只认 $1。
所以生成 SQL 字符串前,得知道目标方言:
- MySQL / SQLite:统一用
?,简单 - PostgreSQL:必须用
$1, $2, ...,且参数顺序严格对应 - 别试图在 SQL 字符串里写
:name(NamedQuery 风格),标准database/sql不支持,除非用sqlx或squirrel - 如果项目要兼容多库,建议放弃手写反射 SQL,改用
squirrel这类构建器,它内部已处理方言差异
真正难的不是反射怎么取字段,而是字段值的类型安全透出、空值语义对齐、以及不同数据库对 NULL / DEFAULT 的行为差异——这些靠几行 reflect.Value.Kind() 判断根本 cover 不住。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










