结构体字段必须首字母大写才能被反射读取,小写字段即使有db tag也无法获取;处理指针字段需先判nil再elem();避免使用map而用带db tag的结构体;sqlx.in配合rebind处理in查询;反射无法自动推断业务规则。

反射拼SQL前必须检查字段是否导出
反射读不到小写字段,哪怕加了db tag也没用。比如type User { name string `db:"user_name"` },reflect.Value.FieldByName("name")返回的IsValid()是false,后续直接panic或条件丢失。
实操建议:
- 结构体字段名必须首字母大写,如
UserName string `db:"user_name"` - 若要兼容历史小写字段,得遍历所有字段:
reflect.TypeOf(v).NumField(),再逐个比对field.Tag.Get("db") - 别依赖
v.IsValid()判断是否可取值——它对nil指针也返回true,但v.Interface()会崩
指针字段不判nil就调Interface()必panic
常见错误是字段类型为*string或*int64,值为nil时直接v.Interface(),触发reflect: call of reflect.Value.Interface on zero Value。
正确做法是先解引用并判空:
- 用
v.Kind() == reflect.Ptr && v.IsNil()检测是否为空指针 - 若为空,跳过该字段,或生成
col IS NULL条件 - 非空则
v = v.Elem()后再取值;若字段是interface{},还得额外v.Elem()一层
用结构体+db tag比map安全得多
map[string]interface{}看着灵活,但丢类型、难校验、nil和空字符串语义混淆(IS NULL vs = ''),容易在参数绑定时错位。
推荐方式:
- 定义带
dbtag的结构体,如type Filter { Name *string `db:"user_name"` Age *int `db:"age"` } - 反射时统一读
field.Tag.Get("db"),为空则跳过或报错 - 避免在map里混用
nil、""、0——它们对应不同的SQL语义,不能靠“默认值”掩盖
sqlx.NamedQuery不支持slice自动展开,IN必须手动处理
把[]int{1,2,3}塞进map[string]interface{}{"ids": ids},再写WHERE id IN :ids,结果要么报错,要么查不到数据——sqlx不会帮你展开slice。
必须用sqlx.In配合Rebind:
- 先调
query, args, _ := sqlx.In("SELECT * FROM users WHERE id IN (?)", ids) - 再
query = db.Rebind(query)转成目标驱动格式(?或$1) - 最后
db.Query(query, args...),注意args...展开 - 命名参数场景下,
NamedQuery完全不认:ids这种写法,硬写等于白忙
最易被忽略的一点:反射能拿到字段名和tag,但拿不到业务规则——哪个字段该参与WHERE、哪个该进ORDER BY、空值算忽略还是IS NULL,全得靠代码分支显式控制,不能指望反射自动推断。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











