AutoMigrate仅在安全时建表或加字段,不删表/字段、不自动改非空约束;需手动启用外键、规范标签建索引;多对多中间表须单独定义迁移;SQLite/旧MySQL可能重建表,生产环境须预检并避免高峰期使用。

AutoMigrate 会建表,但只在“它认为安全”的时候
它不会删字段、不会删表、不会改非空约束为可空(除非你显式改结构体标签),所以放心用——但别指望它帮你回滚或清理冗余列。
-
AutoMigrate(&User{})第一次运行:建表 + 主键 + 默认时间戳字段(CreatedAt/UpdatedAt) - 后续加字段(比如新增
Email string):自动执行ALTER TABLE ADD COLUMN - 把
Name string改成Name string `gorm:"size:100"`:会尝试修改列长度(MySQL/PostgreSQL 支持;SQLite 不支持,GORM 会重建表) - 把
Age int改成Age *int(即允许 NULL):GORM 会把列从NOT NULL改为NULL,前提是数据库驱动支持
外键和索引不是“自动默认开启”,得手动配或开全局
很多人跑完 AutoMigrate 发现外键没生效、联合索引没建上,不是 bug,是 GORM 默认保守——它怕你线上误删数据。
- 外键默认被禁用:初始化 DB 时要显式关掉开关:
DisableForeignKeyConstraintWhenMigrating: false - 外键字段必须命名规范或打标签,否则 GORM 不识别:
UserID uint不行,得写UserID uint `gorm:"foreignKey:UserID"`或用嵌套结构体 +BelongsTo - 唯一索引直接写标签:
Email string `gorm:"uniqueIndex"`;复合索引要用index标签 + 相同名称:Name string `gorm:"index:idx_user_name_age"`+Age int `gorm:"index:idx_user_name_age"`
多个模型一起迁移,顺序不重要,但中间表得单独处理
多对多关系(比如 User ↔ Role)的 join 表,GORM 不会自动生成,必须自己定义结构体并显式迁移。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
- 错例:
db.AutoMigrate(&User{}, &Role{})→ 中间表user_roles不会出现 - 正解:定义
type UserRole struct { UserID uint; RoleID uint },再加一句db.AutoMigrate(&UserRole{}) - 批量迁移推荐写法:
db.AutoMigrate(&User{}, &Product{}, &Order{}, &UserRole{}),比多次调用更高效 - 如果表名要加前缀或引擎选项,用
db.Set("gorm:table_options", "ENGINE=InnoDB").AutoMigrate(...)
SQLite / MySQL 旧版本会悄悄“重建表”,这很危险
SQLite 根本不支持 ALTER COLUMN,MySQL 5.7 之前也不支持重命名列。GORM 遇到这类操作,会新建临时表 → 拷贝数据 → 删旧表 → 改名,整个过程无事务保护,大表可能卡死或丢数据。
立即学习“go语言免费学习笔记(深入)”;
- MySQL 初始化时建议加兼容配置:
DontSupportRenameColumn: true,让 GORM 改用CHANGE COLUMN(更稳妥) - 生产环境绝对不要在高峰期跑
AutoMigrate,尤其涉及字段类型变更(如string→*string) - 上线前务必在同版本数据库上测试迁移效果,用
db.Migrator().HasTable(&User{})和HasColumn做预检
最常被忽略的一点:AutoMigrate 不是“一键部署神器”,它是开发阶段的辅助工具。线上变更仍需配合 SQL 审核、备份与灰度验证——毕竟它再聪明,也读不懂你注释里写的“这个字段下周就下线”。

















