reflect.Value.IsNil()仅对Ptr/Map/Slice/Func/Chan/UnsafePointer安全,需先判Kind;零值字段是否入SQL取决于业务语义,可用omitzero tag控制;nil须转sql.NullXXX;多级指针需循环解引用至非Ptr或Invalid。

struct 字段为 nil 指针时 reflect.Value.IsNil() 才安全调用
直接对非指针类型(比如 int、string)调用 IsNil() 会 panic,这是最常踩的坑。必须先判断 Kind 是否为 Ptr、Map、Slice、Func、Chan 或 UnsafePointer,再调用 IsNil()。
常见错误写法:v.IsNil()(v 是 int 类型的 reflect.Value)→ runtime error: call of reflect.Value.IsNil on int Value
正确做法是:
if v.Kind() == reflect.Ptr && !v.IsNil() {
// 解引用后继续处理
elem := v.Elem()
}
零值字段(如 0、""、false)要不要过滤进 SQL?得看业务语义
Go 的零值不是 SQL 的 NULL:整型字段值为 0、字符串为 ""、布尔为 false,默认都会被拼进 INSERT/UPDATE,但往往这不符合业务意图——比如用户没填年龄,应插入 NULL 而非 0。
实用策略:用结构体 tag 显式声明哪些字段允许“零值跳过”,例如:
type User struct {
ID int `db:"id"`
Name string `db:"name,omitzero"`
Age int `db:"age,omitzero"`
Email string `db:"email"`
}
解析 tag 时检查是否含 omitzero,再结合 v.Interface() == reflect.Zero(v.Type()).Interface() 判断是否跳过。
注意:== 对 slice/map 不可用,需用 reflect.DeepEqual 或单独分支处理。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
SQL 参数化时,nil 必须转成 sql.NullXXX 或显式 nil
原生 database/sql 不接受 Go 的 nil(interface{}(nil)),直接传会导致 sql: converting argument $N: unsupported type 错误。
反射生成 SQL 语句只是第一步;真正执行时,参数列表里对应位置必须是可扫描的 NULL 类型:
- 字符串 →
sql.NullString{Valid: false}或sql.NullString{String: "xxx", Valid: true} - 整数 →
sql.NullInt64,时间 →sql.NullTime - 或者统一用
interface{}+nil(仅当驱动支持,如 pgx 可行,但 database/sql 标准库不推荐)
建议在反射遍历字段后,构建两个切片:columns []string 和 values []interface{},对每个应为 NULL 的字段,append 对应的 sql.NullXXX 实例。
嵌套结构体和指针链(如 **string)需要递归解包
反射遇到 **string 这类多级指针,v.Elem().Elem() 容易越界 panic。更健壮的方式是循环解引用,直到 Kind 不再是 Ptr 或值为 nil:
for v.Kind() == reflect.Ptr && !v.IsNil() {
v = v.Elem()
}
// 此时 v 是最终值,可能是 string、int,也可能是 nil(说明原始为 *string = nil)
如果最终 v.Kind() == reflect.Invalid(即所有指针都解到了 nil),就按 NULL 处理;否则再判断是否为零值或需 omit。
嵌套 struct(如 User.Profile *Profile)同理:解到非指针后,若 Kind 是 Struct,通常不应扁平展开(除非你明确实现嵌套映射),而是跳过或报错提示不支持。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










