正确做法是:用v.CanInterface()判可访问性;对nil指针先用v.IsNil()检查;时间类型走v.Interface().(time.Time).Format();数字/字符串用fmt.Sprintf配合v.Kind()分支处理;字段名从struct tag读,占位符与字段严格对齐;必须缓存reflect.Type避免性能崩。

用 reflect 读结构体字段生成 INSERT SQL,别碰 reflect.Value.Interface() 转类型
Go 反射生成 SQL 的核心难点不是“怎么拼字符串”,而是字段值取出来后怎么安全转成 SQL 字面量。直接调 reflect.Value.Interface() 再类型断言,遇到 nil 指针、未导出字段、time.Time 等就 panic 或丢数据。
正确做法是:用 v.CanInterface() 判可访问性;对 nil 指针先用 v.IsNil() 检查;时间类型走 v.Interface().(time.Time).Format();数字/字符串用 fmt.Sprintf 配合 v.Kind() 分支处理。
- 忽略字段标签(如
json:"-")会导致生成无效字段,必须显式检查structTag.Get("db") == "-" -
sql.NullString这类包装类型要展开一层:v.Elem().Interface()再判断是否有效 - 不要用
fmt.Sprint(v.Interface())—— 它会输出 Go 语法格式(如"hello"带引号),SQL 里需要的是裸字符串hello
INSERT 语句字段名和占位符必须严格对齐,用 strings.Builder 拼接更稳
用 fmt.Sprintf 拼 SQL 容易漏逗号、多空格,且无法动态控制字段顺序。字段名和 ? 占位符数量不一致,执行时直接报 sql.ErrNoRows 或驱动报错。
推荐用两个 strings.Builder:一个攒字段名(name, age, created_at),一个攒问号(?, ?, ?),最后组合成 INSERT INTO t (name,age,created_at) VALUES (?, ?, ?)。这样字段数、占位符数、参数切片长度三者天然一致。
立即学习“go语言免费学习笔记(深入)”;
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
- 字段名从 struct tag 读,比如
db:"user_name"就取user_name,没 tag 就用字段名小写 - 所有字段都加反引号(
`name`)能防关键字冲突,但 MySQL 5.7+ 允许不加;PostgreSQL 必须小写或加双引号,别混用 - 别在 SQL 字符串里拼值 —— 值一律走
db.Exec(query, args...)参数绑定
只反射一次结构体,缓存 reflect.Type 和字段映射,否则性能崩得快
每次插入都 reflect.TypeOf(v) + 遍历字段,QPS 上千时 CPU 会明显卡在 runtime.reflect。实测 10 万次反射调用比缓存慢 8–12 倍。
把 reflect.Type 当 key,value 是预计算好的字段名列表、数据库类型映射、取值函数切片。用 sync.Map 或简单包级变量缓存即可,不用锁也能接受(首次竞争不关键)。
- 缓存 key 用
reflect.TypeOf((*YourStruct)(nil)).Elem(),避免指针/值类型错乱 - 字段映射结构体里存
func(interface{}) interface{},比如时间字段存func(v interface{}) interface{} { return v.(time.Time).Format("2006-01-02 15:04:05") } - 缓存失效几乎不存在 —— 结构体类型编译期固定,无需考虑热更新
别忘了 NULL 和零值的语义区别:空字符串 "" 不等于 NULL
很多人以为 “字段没设值就是 NULL”,但 Go struct 零值(0、""、false)默认不等于 SQL NULL。想让空字符串进库变成 NULL,必须显式判断并传 nil。
典型场景:用户注册时没填昵称,前端传空串,后端该存 NULL 还是 ""?取决于业务。反射层不能擅自转换,得靠字段 tag 控制,例如 db:",nullifempty" 表示字符串为空时塞 nil。
-
sql.NullString类型字段自动视为可空,其Valid字段为false时对应 SQLNULL - 普通字符串字段加
db:",omitempty"是常见误用 —— 这是 JSON tag 语义,反射层不会识别 - 布尔字段零值
false很容易被当成 “未设置”,建议业务层明确传指针或用sql.NullBool

















