用 reflect.TypeOf 获取结构体字段名和类型需传入结构体值(如 User{}),而非指针;遍历 Field(i) 得 StructField,含 Name、Type、Tag;仅导出字段可见,私有字段被跳过;Tag 需手动解析(如 db:"name,unique"),Name 不等价于数据库列名。

用 reflect.TypeOf 获取结构体字段名和类型
直接调用 reflect.TypeOf 能拿到结构体的 reflect.Type,但注意:必须传入类型本身(如 reflect.TypeOf(User{})),不能传指针类型(reflect.TypeOf(&User{}))否则得到的是 *User 类型,后续遍历字段会失败。
每个字段通过 Field(i) 拿到 reflect.StructField,它包含 Name、Type、Tag 等关键信息。常见错误是误以为 Name 就是数据库列名——其实应优先看 Tag.Get("db") 或 Tag.Get("gorm") 的值。
- 私有字段(首字母小写)不会出现在
FieldByName或遍历中,CanInterface()为 false,反射直接跳过 -
Type.Kind()返回底层类别(如reflect.Int64、reflect.Ptr),而Type.Name()只对命名类型(如type UserID int64)非空,别混用 - 嵌套结构体字段需递归调用
Field(i).Type再判断是否为reflect.Struct,否则NumField()panic
解析 struct tag 提取数据库列名和行为标记
struct tag 是模型与数据库映射的契约,但解析方式直接影响生成逻辑的健壮性。比如 db:"name,unique,ignore" 这类复合 tag,不能简单用 strings.Split 拆分——reflect.StructTag.Get("db") 返回完整字符串,需手动解析语义。
典型标签组合及处理建议:
立即学习“go语言免费学习笔记(深入)”;
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
-
db:"id,pk,auto"→ 主键 + 自增,INSERT 时跳过该字段,UPDATE/DELETE 用作 WHERE 条件 -
db:"created_at,default:now()"→ 需识别default:前缀,生成 SQL 时替换为函数而非参数占位符 -
db:"-"或db:"-,"→ 显式忽略,比没 tag 更明确,应优先匹配此规则 - 没
dbtag 但有jsontag?不建议 fallback,字段名大小写不一致(如CreatedAt→created_at)易出错,宁可报错也不自动转换
生成 model 文件时绕不开的可寻址性陷阱
生成代码不是运行时赋值,但很多工具(如 gentool)内部仍依赖反射遍历字段并拼接 Go 源码字符串。这时候容易忽略:反射拿不到未导出字段,也拿不到匿名嵌入字段的 tag,除非显式设置 Anonymous: true 并递归展开。
更隐蔽的问题是字段类型别名(如 type NullString sql.NullString):其 Type.Name() 为空,Type.Kind() 是 Struct,但真实字段是 sql.NullString.String 和 .Valid。若不做特殊处理,生成的 model 会漏掉 Valid 字段或类型写成 NullString 而非 sql.NullString。
- 用
field.Type.PkgPath()判断是否来自标准库(如"database/sql"),针对性展开内嵌字段 - 对
time.Time类型,检查是否有db:"...,timezone=utc"类 tag,决定是否加time.Local解析逻辑 - 生成的 struct 字段注释要和原表 COMMENT 对齐,这需要额外查
INFORMATION_SCHEMA.COLUMNS,反射本身做不到
为什么不用反射直接生成 model,而要用 gentool 这类工具
反射只能读取已存在的 Go 结构体定义,无法反向从数据库建表语句生成 Go 代码。所谓“反射生成 model”,其实是混淆了两个阶段:一是数据库 schema → Go struct 源码(需连接 DB 查询元数据,gentool 干这事);二是已有 struct → 动态操作字段(ORM 运行时干这事)。
如果你手写了一个 User struct,再用反射去“生成 model”,那只是在重复造轮子——生成结果和源码一模一样,毫无意义。真正省时间的是从 MySQL 的 DESCRIBE user 或 PostgreSQL 的 pg_attribute 查出字段名、类型、NULL 约束、默认值、注释,再结合预设规则(如 int → int64、varchar(255) → string)输出 Go 源文件。
-
gentool底层用gorm.io/gorm/schema解析 DSN 并查询 schema,不是靠反射推导类型 - 反射只用于加载生成后的 model 文件,做运行时字段映射(如 ORM 插入时跳过
db:",ignore"字段) - 想支持自定义类型映射(如把 MySQL
ENUM转成 Goconst+string),必须改工具源码或写插件,反射解决不了

















