AutoMigrate仅在表完全不存在时建表,否则只增量添加字段或索引,不删列、不缩容、不重命名;依赖显式gorm标签(如primaryKey)、导出结构体及指针传参,不扫描包,不处理数据一致性,生产环境须慎用。

AutoMigrate 不会自动创建数据库,只在数据库已存在前提下建表或同步结构;它不是“一键初始化全栈环境”的工具,而是结构对齐器。
AutoMigrate 什么时候真正建表?
只有当目标表完全不存在时,AutoMigrate 才执行 CREATE TABLE。一旦表存在,它只做增量检查:缺字段就 ADD COLUMN,缺索引就 CREATE INDEX,字段长度可扩(如 size:50 → size:100)就改——但不会删列、不会缩容、不会重命名字段。
- 首次运行
db.AutoMigrate(&User{})→ 表不存在 → 建表(含主键、CreatedAt/UpdatedAt默认时间戳) - 后续加
Email string字段 → 自动ALTER TABLE ADD COLUMN email varchar(255) - 把
Name string改成Name string `gorm:"size:100"`→ MySQL/PostgreSQL 直接改长度;SQLite 因不支持ALTER COLUMN,会重建表(需磁盘空间 + 写权限) - 如果表已存在但字段类型不兼容(如把
Age int改成Age string),AutoMigrate默认跳过,不报错也不执行
结构体标签写错,表就建不对
GORM 完全依赖 gorm: 标签生成 SQL,不看字段名、不猜语义。漏标或写错,建出来的表和你预期的差很远。
-
ID uint≠ 主键 —— 必须显式写ID uint `gorm:"primaryKey"`,否则可能被忽略或报错 -
Email string `gorm:"unique"`只加唯一约束,不建索引;要高效查询得用`gorm:"uniqueIndex"` -
Name string `gorm:"size:100"`在 MySQL 生效为varchar(100),但在 SQLite 被忽略,一律按text处理 - 外键字段如
UserID uint,必须配对声明:要么加`gorm:"foreignKey:UserID"`,要么用嵌套结构体 +BelongsTo关联
多模型迁移必须显式传指针,中间表不能漏
AutoMigrate 不扫描包、不递归查找结构体,你传谁,它才管谁。漏一个,对应表就不会出现。
立即学习“go语言免费学习笔记(深入)”;
- 错误写法:
db.AutoMigrate(&User{}, &Role{})→user_roles中间表不会自动生成 - 正确写法:先定义
type UserRole struct { UserID uint; RoleID uint },再加一句db.AutoMigrate(&User{}, &Role{}, &UserRole{}) - 所有结构体必须是导出的(首字母大写),字段也必须导出,否则映射失败
- 如果用了
TableName()方法,返回值不能是空字符串或含非法字符(如空格、点号);PostgreSQL 多 schema 场景需显式包含 schema 名,如"my_schema.users"
生产环境慎用,尤其注意外键与数据库差异
默认情况下,GORM 禁用外键生成(DisableForeignKeyConstraintWhenMigrating: true)。MySQL 和 SQLite 的行为也不同,开发跑通不等于上线安全。
- MySQL 启用外键:初始化
gorm.Config时设DisableForeignKeyConstraintWhenMigrating: false - PostgreSQL 外键默认开启,但关联字段类型必须严格一致(
UserID uint对应ID uint,不能是int) - SQLite 或旧版 MySQL(5.7 之前)遇到改类型、加 NOT NULL 等操作,会触发“重建表 + 拷贝数据”流程,高峰期可能锁表、耗空间
- 字段从
NOT NULL改为允许 NULL(比如Age int → Age *int),GORM 仅在类型变更同时发生时才执行,且依赖驱动是否支持
最常被忽略的一点:AutoMigrate 不处理已有数据。比如给非空字段加 not null 标签,而表里已有 NULL 值,迁移会直接失败——它不帮你清洗数据,也不提示你该怎么做。


















