AutoMigrate只建表不改字段,因其设计目标是“安全初始化”而非“全量同步”,仅创建表、新增列/索引,绝不删改列、重命名或降级约束,避免线上误操作。

AutoMigrate 为什么只建表不改字段
AutoMigrate 的设计目标是“安全初始化”,不是“全量同步”。它只会创建不存在的表、新增缺失的列、添加缺失的索引,但绝不会删除列、修改列类型、重命名字段或更改默认值——哪怕你改了 struct tag 里的 type 或 default,GORM 也直接跳过。
常见现象:改了 type:string 为 type:varchar(255),或者加了 default:1,执行 AutoMigrate 后数据库毫无反应。这不是 bug,是 GORM 故意为之,避免线上误删数据。
- 若需改类型或删字段,必须手动写
db.Migrator().AlterColumn()或db.Migrator().DropColumn() - 字段名变更(比如
Name→Fullname)不会触发重命名,得先AddColumn再DropColumn - SQLite 不支持
ALTER COLUMN,AlterColumn在 SQLite 下静默失败,需用db.Exec("PRAGMA ...")绕过
如何让 AutoMigrate 正确识别新字段和约束
Struct 字段必须带 GORM tag 才会被纳入迁移逻辑;零值字段(如空字符串、0、nil)会被忽略,除非显式声明 default 或 not null。
关键点在于 tag 的拼写和组合:
立即学习“go语言免费学习笔记(深入)”;
-
gorm:"primaryKey"和gorm:"column:id"效果不同:前者声明主键,后者仅指定列名,不自动加NOT NULL -
gorm:"default:CURRENT_TIMESTAMP"在 MySQL/PostgreSQL 生效,SQLite 需写default:datetime('now') -
gorm:"uniqueIndex"会建唯一索引,但gorm:"unique"只加UNIQUE约束(部分驱动下等价,但行为不一致) - 嵌套 struct 不会被自动展开,
Address字段即使有gorm:"embedded"tag,也必须显式加该 tag 才嵌入
生产环境用 AutoMigrate 前必须检查的三件事
上线前跑 AutoMigrate 是高危操作,尤其当表已有数据时。以下三点漏掉一个就可能锁表或丢数据:
- 确认数据库用户有
CREATE TABLE、ADD COLUMN权限,但**没有**DROP TABLE权限(GORM 不会删表,但某些旧版驱动在 panic 时可能误触发) - 检查 struct 中所有字段是否都有明确的零值语义:比如
UpdatedAt time.Time没加gorm:"autoUpdateTime",迁移后老记录该字段会是0001-01-01 00:00:00,查询时可能被误判为“未更新” - 运行
db.Migrator().HasTable(&User{})先判断表是否存在,避免在空库反复执行迁移(虽无害,但日志刷屏且掩盖真实问题)
替代方案:什么时候该放弃 AutoMigrate 改用手动 Migrate
当业务涉及字段类型变更(如 string → []byte)、JSON 字段结构升级、或需要数据迁移(如把 Status int 映射为新枚举表),AutoMigrate 就力不从心了。
此时应切换到 db.Migrator() 接口或外部工具:
-
db.Migrator().RenameColumn(&User{}, "name", "full_name")—— PostgreSQL/MySQL 支持,SQLite 不支持 - 用
db.Transaction()包裹字段变更 + 数据复制逻辑,确保原子性 - 更稳妥的做法是引入
migrate库(如github.com/golang-migrate/migrate/v4),把 SQL 迁移脚本版本化管理,AutoMigrate只用于本地开发快速对齐
最常被忽略的一点:GORM 的 AutoMigrate 不做字段注释(comment)同步,哪怕你在 tag 里写了 comment:"用户邮箱",MySQL 表字段的 COMMENT 也不会更新——这事得自己 db.Exec("ALTER TABLE ... COMMENT ...") 补。


















