AutoMigrate 是安全增量更新,只增字段、索引、外键,不删改结构;SQLite/MySQL 5.7前字段类型或长度变更会触发无事务表重建;外键和索引需显式启用与声明;多对多中间表须手动定义并传入 AutoMigrate。
automigrate 不是“一键同步”,而是“安全增量更新”:它只加字段、建索引、设外键,从不删列、不删表、不改非空为可空(除非你显式改结构体标签)。生产环境直接跑它,等于把数据库结构交给 gorm 的启发式判断——可行,但得知道边界在哪。
什么时候 AutoMigrate 会悄悄重建整张表
SQLite 和 MySQL 5.7 之前版本不支持 ALTER COLUMN 或重命名列,GORM 遇到这类变更(比如把 Name string 改成 Name string <code>gorm:"size:100")就会触发重建流程:新建临时表 → 拷贝数据 → 删旧表 → 改名。这个过程无事务保护,大表可能卡死或丢数据。
- MySQL 初始化时建议加配置:
DontSupportRenameColumn: true,让 GORM 改用CHANGE COLUMN(更稳妥) - SQLite 下任何字段类型变更、长度调整、
NOT NULL改动,都可能触发重建 - 预检方法:本地用同版本数据库执行一次
AutoMigrate,观察日志是否出现creating new table或大量INSERT INTO ... SELECT
外键和索引为什么没生效
外键默认被禁用,索引不会自动推导——这不是 bug,是 GORM 的保守策略。它怕你线上误删数据,所以所有约束类操作都要求显式声明。
- 启用外键:初始化 DB 时传入
DisableForeignKeyConstraintWhenMigrating: false - 外键字段必须规范命名或打标签:
UserID uint <code>gorm:"foreignKey:UserID",否则 GORM 不识别 - 唯一索引写法:
Email string <code>gorm:"uniqueIndex";复合索引需共用同一名称:Name string <code>gorm:"index:idx_user_name_age"+Age int <code>gorm:"index:idx_user_name_age"
多对多中间表必须手动定义
AutoMigrate 不会自动生成 User ↔ Role 这类关系的 join 表。它只处理模型本身,不推断关联逻辑。
- 必须单独定义中间结构体:
type UserRole struct { UserID uint <code>gorm:"primaryKey"; RoleID uintgorm:"primaryKey"} - 迁移时显式加入:
db.AutoMigrate(&User{}, &Role{}, &UserRole{}) - 顺序无关,但漏掉
&UserRole{}就不会有中间表
最易被忽略的是字段变更的“隐式语义”:把 Age int 改成 Age *int,GORM 会尝试把列从 NOT NULL 改为允许 NULL,但这依赖驱动支持;而把 CreatedAt time.Time 改成 *time.Time,则可能破坏 GORM 内置的时间戳钩子行为。这类改动看似微小,却常在上线后暴露为查询空值或时间未自动填充。


















