直接用 reflect.Value.Interface() 拼接 SQL 会导致 panic 和 SQL 注入;反射仅用于字段映射和元数据解析,SQL 构建必须通过预编译语句+参数绑定,字段 nil 判断、嵌入结构体展开、标签解析及类型缓存均需严格处理。

直接用 reflect.Value.Interface() 往 SQL 字符串里塞值,90% 的 panic 和 SQL 注入都从这儿开始。别这么干。
反射只负责字段映射,不负责 SQL 构建
反射能拿到的只有字段名、类型、db tag 和值——它不知道哪个是主键、是否允许 NULL、要不要转义、时间格式对不对。硬拼 "INSERT INTO user (name) VALUES ('" + v.Interface() + "') 是在给自己埋雷。
- ORM(如 gorm、sqlc)只用反射做元数据解析:比如读
ID int `gorm:"primaryKey"`确定主键列,之后所有 SQL 都走预编译语句 + 参数绑定 - 你手写的通用构造器也该遵循同一原则:反射阶段只产出
map[string]interface{}或两个切片(columns []string,args []interface{}),SQL 模板留到调用层用sqlx.Named或db.Query处理 - 如果结构体含嵌入字段(
Extra Info `db:",inline"`),需递归展开并拼前缀,但终止条件必须明确:遇到非 struct 类型、非导出字段、或Tag.Get("db") == "-"就停
v.Kind() == reflect.Ptr && v.IsNil() 必须每字段检查
这是动态拼 WHERE 最常崩的点。字段是 *string 且为 nil 时,v.IsValid() 返回 true,但 v.Interface() 直接 panic。
- 正确顺序:先判
v.Kind() == reflect.Ptr,再v.IsNil(),是则跳过或生成"col IS NULL" - 若字段是
interface{},得先v.Elem()解一层再判断,否则v.Interface()返回空interface{},后续类型断言失败 - 别依赖
v.CanInterface()——它对 nil 指针也返回 true,没用
命名参数和位置参数不能混用
sqlx.Named 要求传 map 或 struct,而原生 database/sql 只认 []interface{}。反射生成的 SQL 字符串里如果含 :name 却喂给 db.Query,会报 sql: expected 0 arguments, got 1。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
- 选
sqlx:反射阶段保留结构体或 map,WHERE 条件用"name = :name"格式,最后交由NamedQuery - 选原生驱动:反射阶段就得拆出
args []interface{}和placeholders []string(如"?"),拼成"name = ? AND age > ?",再db.Query(query, args...) -
sqlx.In处理IN (?)时,必须配db.Rebind(),否则问号不会转成目标驱动格式($1或?)
标签解析要防空、防乱、防嵌套失控
field.Tag.Get("db") 返回空字符串不 panic,但后续 strings.Split(tag, ",")[0] 会 panic——没人检查 tag == "" 就切片。
- 每次取 tag 后先
if tag := strings.TrimSpace(field.Tag.Get("db")); tag == "" || tag == "-" { continue } - 支持
db:"created_at,notnull,default:now()"这种逗号分隔格式,比空格分隔更安全、更接近主流习惯 - 遇到
Anonymous == true且Tag.Get("db") == ",inline",递归处理子字段,但前缀拼接逻辑要收敛:比如Extra Info的CreatedAt映射为"extra_created_at",而非无限嵌套
最易被忽略的是:反射遍历本身有开销,reflect.TypeOf(t).NumField() 不该在每次查询时都调——字段结构不变,缓存 map[reflect.Type][]fieldInfo 才是常态。还有,database/sql 的 Query 函数不接受运行时拼出的表名或列名,这些必须是静态字面量或白名单校验后的结果,否则预编译会失败。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










