主流Go ORM仅识别特定结构体标签:GORM认gorm标签(如column、primaryKey),SQLBoiler认boil,Ent不依赖标签而用schema DSL;json等标签不会被迁移工具解析,必须显式声明gorm标签才能生成建表语句。

结构体标签里哪些字段能被迁移工具识别
主流 Go ORM(如 GORM、SQLBoiler、Ent)只认特定标签名,gorm 用 gorm 标签,ent 用 ent,sqlc 则基本不依赖结构体标签而靠 SQL 文件驱动。别把 json 或自定义标签当迁移依据——它们不会被解析进建表语句。
常见误操作:写 type User struct { Name string `json:"name"` },以为迁移时会生成 name 字段。实际什么都不会生成。必须显式声明,比如:Name string `gorm:"column:name;type:varchar(100);not null"`。
-
gorm标签支持column、type、not null、default、primaryKey、index、uniqueIndex等,但不支持外键约束语法(得靠foreignKey+ 关联字段配合) -
ent不读结构体标签,它靠 schema DSL 定义,结构体只是生成后的数据载体;想“从结构体反推迁移”,得用第三方工具如entc插件或手写ent/schema - 如果用
goose或migrate这类纯 SQL 迁移工具,结构体标签完全无用——它们根本不解析 Go 代码
GORM 的 auto-migration 能做什么、不能做什么
db.AutoMigrate(&User{}) 是最常被误用的功能:它只做增量同步,且仅限于字段增删和类型宽松变更(比如 int → int64),绝不重命名字段、不改主键、不处理索引变更,更不回滚。
典型翻车场景:上线后发现 user_name 字段要改成 name,AutoMigrate 不会帮你 rename,只会新建 name 字段,旧字段残留。生产环境必须配手动 SQL 迁移文件。
立即学习“go语言免费学习笔记(深入)”;
- 支持的变更:新增字段、增加 NOT NULL(若列允许 NULL 且有默认值)、扩大 varchar 长度(如 50→100)
- 不支持的变更:字段重命名、删除字段(除非加
gorm:" 且开启 <code>DisableForeignKeyConstraintWhenMigrating)、修改主键类型、添加唯一约束(需额外调用CreateIndex) - 每次运行都检查 schema,但不会输出 SQL —— 想预览?得开日志:
db.Debug().AutoMigrate(&User{}),看 console 输出的 CREATE/ALTER 语句
如何让结构体标签真正驱动可复现的迁移脚本
靠 AutoMigrate 无法生成带版本号、可回滚、可 review 的迁移文件。真要“自动生成脚本”,得借助代码生成器,而非运行时迁移。
推荐组合:golang-migrate + gomodifytags(或 struct2sql 类小工具)+ 手动模板。核心思路是:把结构体标签解析成 SQL DDL,再按版本号写入 .up.sql 和 .down.sql。
- 用
go list -f '{{.Dir}}' ./... | xargs -I{} go run github.com/freddierice/struct2sql -dir {} -pkg models可批量提取含gorm标签的 struct 并转基础 CREATE TABLE 语句(注意:它不处理关联、索引、注释) - 字段类型映射有坑:
time.Time默认转DATETIME,但 MySQL 8+ 推荐TIMESTAMP;bool在 SQLite 是BOOLEAN,在 PostgreSQL 是BOOLEAN,但在 MySQL 是TINYINT(1)—— 标签里写type:bool会被忽略,必须明确写type:tinyint(1) - 外键必须拆开写:
gorm:"foreignKey:UserID"只影响关联加载,不生成 FOREIGN KEY 语句;得在迁移脚本里单独加ADD CONSTRAINT fk_users_posts FOREIGN KEY (user_id) REFERENCES users(id)
为什么别指望一个命令搞定所有环境的迁移
开发机上 AutoMigrate 跑通,不等于 CI 或生产库能用。PostgreSQL 的 CITEXT、MySQL 的 utf8mb4_0900_as_cs 排序规则、SQLite 的严格模式——这些全得在标签里显式声明,而 GORM 默认不透出方言细节。
比如 gorm:"type:varchar(255);collate:en_US.utf8" 在 PostgreSQL 有效,在 MySQL 会报错;gorm:"type:jsonb" 在 PostgreSQL 可用,在 SQLite 直接失败。
- 跨数据库迁移几乎必然失败:没有通用标签能同时适配三者。选型时就得锁定方言,然后在标签里补全方言专属参数
- 时间字段陷阱最多:
CreatedAt默认用datetime,但 PostgreSQL 建议用timestamp with time zone,得写gorm:"type:timestamptz" - 哪怕同一方言,不同版本行为也不同:MySQL 5.7 不支持
JSON类型的索引,8.0 支持——标签写了type:json;index,5.7 会建表失败
结构体标签不是银弹,它只是迁移起点。真正的迁移脚本必须结合目标数据库能力、团队协作流程、以及上线前的手动验证。漏掉 collation、时区、索引类型中的任何一个,都可能让自动脚本在凌晨三点报错。


















