automigrate仅在启动时一次性对齐表结构,只增字段、不删不改类型,非数据库迁移工具;需确保db已初始化、dsn配置正确、显式声明表名字段名及主键,并避免在handler中重复调用。

直接用 AutoMigrate 就能建表,但别指望它“自动修复”字段变更
Go 里没有像 Rails 那样的迁移历史追踪机制。AutoMigrate 的作用是「让数据库表结构匹配当前 Go 结构体定义」,只增不减、不改类型、不删字段。它不是数据库版本管理工具,而是启动时的一次性对齐操作。
常见错误现象:AutoMigrate 执行后字段没更新、新加字段没生效、旧字段删了但表里还留着。
- 新增字段:会加到表里(前提是结构体字段有对应 tag,比如
gorm:"column:name"或默认映射) - 修改字段类型(如
string→int):GORM 不处理,MySQL 会报错或静默忽略 - 删除结构体字段:表中对应列不会被删,
AutoMigrate完全不管 - 重命名字段:必须显式用
gorm:"column:old_name"+ 新字段名,否则会被当成新增字段
建表前必须确保 db 实例已初始化且可连通
很多新手卡在「调了 AutoMigrate 却没反应」,本质是 db 变量为 nil 或连接失败后被忽略。
典型问题场景:在 init() 里调用 gorm.Open,但没检查 err;或把 db 声明为包级变量却忘了赋值。
- 务必在
AutoMigrate前验证db是否非空:if db == nil { panic("db not initialized") } - 连接失败时,
gorm.Open返回的err必须显式处理,不能只写if err != nil { log.Fatal(err) }就完事——这会导致后续逻辑跳过 - DSN 中的
parseTime=True和loc=Local缺一不可,否则time.Time字段读写会出错,间接导致AutoMigrate失败
表名和字段名要靠结构体 tag 显式控制,别依赖默认规则
GORM 默认把结构体名转成复数小写下划线形式(如 User → users),字段名同理(CreatedAt → created_at)。但实际项目里几乎没人按这个来,尤其涉及已有数据库或团队命名规范时。
容易踩的坑:结构体字段没加 gorm tag,结果生成的字段名与预期不符,甚至因大小写/下划线问题导致查询失败。
- 指定表名:
func (User) TableName() string { return "t_user" } - 指定字段名:
Name string `gorm:"column:user_name;size:64"` - 主键不是
ID?必须显式标注:ID uint `gorm:"primaryKey"` - 想跳过某个字段不映射到表?加
-tag:Password string `gorm:"-"`
AutoMigrate 执行时机很关键:只应在应用启动早期、且仅一次
有人把 AutoMigrate 放在 HTTP handler 里,每次请求都跑一遍——这不仅慢,还会在并发时触发 MySQL 元数据锁,导致建表卡死或报错 ERROR 1050 (42S01): Table 'xxx' already exists。
更隐蔽的问题是:开发环境跑一次没问题,上线后多个实例同时启动,可能因竞态导致重复建表失败。
- 最佳实践:只在
main()或initDB()函数里调用一次,且放在服务监听之前 - 如果需要多模型建表,传入多个地址:
db.AutoMigrate(&User{}, &Post{}, &Comment{}) - 生产环境建议关闭
AutoMigrate,改用 SQL 迁移脚本——毕竟线上改表结构必须受控,不能靠代码“猜”
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











