AutoMigrate会改线上表结构,但仅限安全加法操作:建表、加字段、扩类型、加索引/外键;绝不删字段、不改名、不缩容、不降NOT NULL,且对不兼容变更(如int→string)静默跳过,不报错不提示。

AutoMigrate到底会不会改线上表结构?
它会,但只做“加法”:建表、加字段、扩长度、加索引/外键;绝不会删字段、改字段名、缩类型、降 NOT NULL。你改了 User 结构体加了个 Phone 字段,跑 db.AutoMigrate(&User{}) 就会 ALTER TABLE ADD COLUMN;但如果你把 Age int 改成 Age string,它直接跳过——不报错、不提示、也不执行。
常见静默失败场景:
- 传了
User{}而不是&User{}→ AutoMigrate 什么也不干 - MySQL 默认禁用外键迁移 →
foreignKey标签无效,除非初始化时设DisableForeignKeyConstraintWhenMigrating: false - SQLite 或老 MySQL(int → int64)→ 自动重建表,大表可能卡死或磁盘爆满
字段标签写错,建出来的表就不是你要的
AutoMigrate 完全依赖 gorm: 标签生成 DDL,没标签=按默认规则猜,一猜就错。
关键标签必须显式写:
-
ID uint不等于主键 → 必须加gorm:"primaryKey",否则可能建出无主键表或报错 -
Email string想唯一索引 → 得写gorm:"uniqueIndex",只写"unique"是约束不建索引,"index"又不唯一 -
Name string要 varchar(100) → 写gorm:"size:100",不写默认 255;但 SQLite 忽略size,一律当 text - 外键字段如
CompanyID uint→ 关联字段必须配Company Company `gorm:"foreignKey:CompanyID"`,否则外键约束不会生成
多模型一起迁,漏一个就少一张表
AutoMigrate 不扫描包里所有 struct,你传谁,它才管谁。漏传 &Order{},orders 表永远不出现,哪怕 User 里有 Orders []Order 关联定义。
Go 配置库,使用 spf13/viper — 分层优先级(flag > env >file > KV > default),提供 BindPFlag/BindPFlags、SetEnvPrefix + SetEnvKeyReplace 等功能。
推荐写法:
- 一次传全:
db.AutoMigrate(&User{}, &Order{}, &UserRole{}) - 中间表必须手动定义并显式迁移:
type UserRole struct { UserID uint; RoleID uint }→ 单独db.AutoMigrate(&UserRole{}) - 别依赖“自动推导顺序”:GORM 会解析外键依赖,但若
Order没传进去,User表里的order_id字段仍会建,只是没外键约束
生产环境直接跑 AutoMigrate 是高危操作
它没有版本记录、不支持回滚、不预览 SQL、不处理数据迁移,且静默跳过冲突(比如同名索引已存在)。上线前光看 err == nil 不代表结构对齐了。
真正能落地的检查项:
- 用
db.Migrator().HasTable(&User{})确认表存在 - 用
db.Migrator().HasColumn(&User{}, "email")确认字段到位 - 用
db.Migrator().HasIndex(&User{}, "idx_users_email")验证索引生效 - 字段类型变更(如
int → int64)必须加gorm:"type:bigint",否则 MySQL 里还是 INT
最易被忽略的是:AutoMigrate 永远不会告诉你“这次没做任何事”。它安静得像没运行过,直到某天查询慢得查不出原因,才发现索引根本没建上。

















