2. AutoMigrate 基础:自动建表、增字段与索引

AutoMigrate 是 GORM 提供的最便捷的数据库 Schema 同步工具。它根据 Go 结构体(Model)的定义,自动创建表、添加缺失的字段和索引,而不会删除或修改已有列的类型。本章将深入讲解 AutoMigrate 的基本用法、执行时机以及它在开发与生产环境中的适用边界。

2.1 AutoMigrate 核心机制

AutoMigrate 本质上是一个“增量同步”工具。它读取结构体字段定义,与数据库中现有表结构进行对比,然后执行 ALTER TABLE 语句来补齐差异。它不会删除任何列或改变列的数据类型,因此相对安全。

关键行为: 只增不减,不修改已有列类型,不删除列或索引。
行为 支持 说明
创建新表 ✅ 表不存在时,根据结构体生成 CREATE TABLE
添加新字段 ✅ 检测到结构体新增字段,执行 ALTER TABLE ADD COLUMN
添加索引 ✅ 检测到新索引标签,执行 CREATE INDEX
修改字段类型 ❌ 不会执行,需手动迁移或使用 Migrator
删除字段/索引 ❌ 不会执行,需手动 DROP

2.2 基本用法与自动建表

在项目初始化阶段,通常将 AutoMigrate 放在数据库连接之后、业务启动之前。以下是最基本的调用方式:

import (
  "gorm.io/gorm"
  "gorm.io/driver/sqlite"
)

type User struct {
  gorm.Model
  Name  string
  Email string `gorm:"uniqueIndex"`
  Age   int
}

func main() {
  db, err := gorm.Open(sqlite.Open("test.db"), &gorm.Config{})
  if err != nil {
    panic("failed to connect database")
  }

  // 自动迁移 —— 创建表、添加字段和索引
  err = db.AutoMigrate(&User{})
  if err != nil {
    panic("migration failed")
  }
}

执行上述代码后,GORM 会在数据库中生成一张名为 users 的表,包含 id、created_at、updated_at、deleted_at、name、email、age 七个字段,并在 email 列上创建唯一索引。

表名约定: GORM 默认将结构体名转为蛇形复数作为表名(User → users)。可通过 TableName() 方法自定义。

2.3 自动增加字段

当业务迭代需要给已有表添加新字段时,只需在结构体中增加字段定义,再次调用 AutoMigrate 即可。

// 假设 users 表已存在,现在需要增加 Phone 字段
type User struct {
  gorm.Model
  Name  string
  Email string `gorm:"uniqueIndex"`
  Age   int
  Phone string `gorm:"default:'unknown'"`  // 新增字段
}

// 再次执行迁移
db.AutoMigrate(&User{})

GORM 会自动执行 ALTER TABLE users ADD COLUMN phone TEXT DEFAULT 'unknown'。已有数据行的新字段会被填充为默认值。

注意: 对于 SQLite,ALTER TABLE ADD COLUMN 不支持设置 NOT NULL 且无默认值的字段。建议新增字段时始终提供合理的默认值或允许为空。

2.4 自动增加索引

索引通过结构体标签 gorm:"index" 或 gorm:"uniqueIndex" 声明。AutoMigrate 会自动检测新增的索引标签并创建对应的数据库索引。

type Product struct {
  gorm.Model
  Code  string `gorm:"index"`         // 普通索引
  Price uint   `gorm:"index:idx_price"` // 自定义索引名
  SKU   string `gorm:"uniqueIndex"`   // 唯一索引
}

db.AutoMigrate(&Product{})

上述代码会创建三个索引:

  • idx_products_code —— 普通索引
  • idx_price —— 自定义名称的普通索引
  • idx_products_sku —— 唯一索引

2.5 AutoMigrate 执行流程

下图展示了 AutoMigrate 从结构体解析到数据库执行的完整链路:

AutoMigrate 执行流程 Go 结构体定义 解析 gorm 标签 对比数据库表 生成 ALTER TABLE 执行 SQL 语句 迁移完成 💡 每次调用 AutoMigrate 都会重新对比全表结构,但只执行必要的 ALTER 语句,不会重复创建已存在的对象。

2.6 多表迁移与批量执行

AutoMigrate 支持同时传入多个模型,按顺序依次执行迁移:

db.AutoMigrate(&User{}, &Product{}, &Order{})

也可以使用 ...interface{} 切片动态传入:

models := []interface{}{&User{}, &Product{}, &Order{}}
db.AutoMigrate(models...)
推荐做法: 在应用启动时集中执行所有模型的 AutoMigrate,确保 Schema 与代码始终保持一致。

2.7 生产环境使用建议

AutoMigrate 虽然便捷,但在生产环境中需要谨慎使用。以下表格总结了不同场景下的推荐策略:

环境 推荐策略 原因
开发/测试 ✅ 直接使用 AutoMigrate 快速迭代,无需手动维护 DDL
生产(小表) ⚠️ 谨慎使用,建议配合备份 ALTER 操作可能锁表,影响读写
生产(大表) ❌ 使用版本化迁移工具 需要控制变更顺序、回滚、灰度

2.8 常见陷阱与避坑

  • 字段类型变更无效: 将 string 改为 int 不会生效,需手动 ALTER COLUMN。
  • 索引重命名不生效: 修改索引名不会自动删除旧索引,需手动清理。
  • 外键约束: AutoMigrate 默认不创建外键,需通过标签 constraint 显式声明。
  • 并发迁移: 多个实例同时启动可能导致冲突,建议加锁或串行化。
生产警告: 永远不要在未备份的生产数据库上直接运行 AutoMigrate。建议先在预发布环境验证迁移结果。

2.9 本章小结

AutoMigrate 是 GORM 迁移体系的基石,适用于开发阶段的快速原型和中小型项目的持续集成。它通过“只增不改”的策略降低了误操作风险,但无法替代专业的版本化迁移工具。掌握 AutoMigrate 的边界和用法,是后续学习契约校验和高级迁移模式的前提。