automigrate只接受结构体指针(如&user{}),传值或new(user)会导致静默失败;它仅安全新增表、字段、索引,不删字段、不改类型、不重命名,外键和索引需显式配置并启用。

db.AutoMigrate(&User{}) 传值不对就完全没反应
AutoMigrate 只接受结构体指针,传 User{} 或 new(User) 都不行,必须是 &User{}。它靠反射读取结构体标签,传值会丢失字段元信息,函数静默返回,不报错也不建表。
常见错误现象:执行完 db.AutoMigrate(User{}),查数据库发现表根本没创建,日志也空空如也。
- 务必检查调用时是否带取地址符:
db.AutoMigrate(&User{}) - 多个模型一起迁更稳妥:
db.AutoMigrate(&User{}, &Order{}, &UserRole{}),GORM 会内部按外键依赖排序,比循环调用安全 - 如果只传一个模型却依赖另一个(比如
Order有UserID uint),而User表还没建,AutoMigrate 可能跳过外键——建议批量传或手动控制顺序
外键、索引、唯一约束全都不生效?缺这三样配置
GORM 默认禁用外键生成,也不自动建索引,哪怕你写了 gorm:"uniqueIndex" 标签。这不是 bug,是设计上的保守策略。
必须显式启用外键支持,并确保字段标签完整:
- 初始化 DB 时加配置:
&gorm.Config{DisableForeignKeyConstraintWhenMigrating: false} - 外键字段需配完整关联标签,例如:
UserID uint `gorm:"foreignKey:ID;constraint:OnUpdate:CASCADE,OnDelete:CASCADE;"`,光写UserID uint不会生成 FOREIGN KEY - 唯一索引要写
gorm:"uniqueIndex",联合索引用同名index标签:Name string `gorm:"index:idx_user_name_age"`+Age int `gorm:"index:idx_user_name_age"`
字段改了类型或加了 not null,数据库里却没变
AutoMigrate 不会主动降级约束(比如把可空字段改成非空),也不会在所有数据库上无条件改类型。它只在“安全扩增”场景下 ALTER COLUMN:扩大长度、允许 NULL → NOT NULL(需驱动支持)、增加精度等。
典型失效场景:
-
Name string→Name string `gorm:"size:100"`:MySQL/PostgreSQL 会改,SQLite 直接重建表(大表危险) -
Age int→Age *int:GORM 尝试去掉 NOT NULL,但 SQLite 和老 MySQL 5.7 之前不支持,会静默失败 - 字段重命名(
Name→FullName)会被当两个字段:旧列残留,新列新增——AutoMigrate 永远不删字段、不改名 - 想删字段或改类型?得手动:
db.Migrator().DropColumn(&User{}, "old_field"),再AddColumn
生产环境别在 init() 里无脑 AutoMigrate
并发启动多个实例时,同时跑 AutoMigrate 可能触发重复建表或锁表失败;更麻烦的是,它永远不告诉你“这次什么都没做”。上线前结构是否就位,得自己验证。
- 加开关控制:
if os.Getenv("ENABLE_MIGRATE") == "true" { db.AutoMigrate(...) } - 关键结构主动检查:
db.Migrator().HasTable(&User{})和db.Migrator().HasColumn(&User{}, "email") - 中间表(如多对多
UserRole)GORM 绝对不自动生成,必须单独定义 struct 并显式调一次db.AutoMigrate(&UserRole{}) - 表选项(引擎、字符集)要用
db.Set("gorm:table_options", "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4")提前设置











