4. AutoMigrate 入门:基本用法与自动建表

AutoMigrate 是 GORM 提供的最便捷的数据库迁移入口。它允许开发者通过 Go 结构体定义模型,然后由 GORM 自动在数据库中创建、更新表结构,而无需手写 DDL 语句。本章将系统讲解 AutoMigrate 的核心用法、自动建表、自动增加字段以及自动修改字段类型。

4.1 AutoMigrate 基本用法

AutoMigrate 的核心方法是 db.AutoMigrate(&Model{}),它接收一个或多个模型指针,GORM 会根据模型定义自动同步数据库表结构。其执行逻辑如下:

  • 表不存在:自动创建新表。
  • 表已存在,字段缺失:自动增加新字段。
  • 表已存在,字段类型变更:自动修改字段类型(仅支持部分数据库)。
  • 表已存在,字段多余:不会删除字段,保证数据安全。
提示:AutoMigrate 是“增量迁移”工具,它不会删除任何已有列或索引,也不会破坏已有数据。这是生产环境安全迁移的基础保障。

4.2 自动建表示例

以下代码演示如何使用 AutoMigrate 自动创建 users 表:

package main

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

type User struct {
  ID        uint   `gorm:"primaryKey"`
  Name      string `gorm:"size:100;not null"`
  Email     string `gorm:"uniqueIndex;size:255"`
  Age       uint8
  CreatedAt time.Time
  UpdatedAt time.Time
}

func main() {
  dsn := "root:password@tcp(127.0.0.1:3306)/testdb?charset=utf8mb4&parseTime=True"
  db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
  if err != nil {
    panic("数据库连接失败")
  }

  // 自动迁移:创建 users 表
  err = db.AutoMigrate(&User{})
  if err != nil {
    panic("自动迁移失败")
  }
}

执行后,数据库中将自动生成 users 表,包含 id、name、email、age、created_at、updated_at 六个字段,主键、唯一索引、非空约束等均已生效。

4.3 自动增加字段

当业务需求变更,需要为已有表增加字段时,只需在结构体中添加新字段,再次执行 AutoMigrate 即可。

假设 User 表需要增加 Phone 和 Status 字段:

type User struct {
  ID        uint   `gorm:"primaryKey"`
  Name      string `gorm:"size:100;not null"`
  Email     string `gorm:"uniqueIndex;size:255"`
  Age       uint8
  Phone     string `gorm:"size:20"`      // 新增字段
  Status    int    `gorm:"default:1"`    // 新增字段
  CreatedAt time.Time
  UpdatedAt time.Time
}

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

GORM 会自动检测 users 表中缺少 phone 和 status 列,并执行 ALTER TABLE 添加这两列。已有数据不受影响,新列会使用默认值填充。

关键点:AutoMigrate 只增不减。如果从结构体中移除字段,数据库中的对应列不会被删除。这是为了防止误操作导致数据丢失。

4.4 自动修改字段类型

当字段的 GORM 标签或 Go 类型发生变化时,AutoMigrate 会尝试修改字段类型。但不同数据库的支持程度不同:

数据库 支持修改字段类型 注意事项
MySQL ✅ 支持 使用 ALTER TABLE MODIFY,可能因数据不兼容而失败
PostgreSQL ✅ 支持 使用 ALTER TABLE ALTER COLUMN,类型转换规则严格
SQLite ❌ 不支持 SQLite 不支持 ALTER COLUMN,需重建表
SQL Server ✅ 支持 使用 ALTER TABLE ALTER COLUMN

示例:将 Age 字段从 uint8 改为 uint16:

type User struct {
  ID        uint   `gorm:"primaryKey"`
  Name      string `gorm:"size:100;not null"`
  Email     string `gorm:"uniqueIndex;size:255"`
  Age       uint16 // 类型从 uint8 改为 uint16
  Phone     string `gorm:"size:20"`
  Status    int    `gorm:"default:1"`
  CreatedAt time.Time
  UpdatedAt time.Time
}

db.AutoMigrate(&User{})

在 MySQL 中,GORM 会执行 ALTER TABLE users MODIFY COLUMN age smallint unsigned。如果表中已有数据超出新类型的范围,迁移会失败并返回错误。

警告:修改字段类型属于破坏性操作。在生产环境中,建议先评估数据兼容性,必要时先备份数据或使用自定义迁移脚本。

4.5 AutoMigrate 迁移流程

下图展示了 AutoMigrate 从模型定义到数据库表同步的完整流程:

AutoMigrate 迁移流程 Go 结构体模型 db.AutoMigrate() 检查表是否存在 CREATE TABLE ALTER TABLE 增量同步 说明:AutoMigrate 只增不减,不会删除已有列或索引

4.6 多模型迁移

AutoMigrate 支持同时迁移多个模型,GORM 会按顺序处理每个模型:

type Order struct {
  ID         uint `gorm:"primaryKey"`
  UserID     uint
  Product    string
  Amount     float64
}

type Product struct {
  ID    uint `gorm:"primaryKey"`
  Name  string
  Price float64
}

// 同时迁移多个表
db.AutoMigrate(&User{}, &Order{}, &Product{})

执行后,数据库中会创建 users、orders、products 三张表。如果某张表已存在,则只进行增量同步。

4.7 常见问题与避坑

  • 字段标签变更:修改 gorm 标签(如 size、not null)不会触发字段修改,AutoMigrate 只关注 Go 类型变化。
  • 索引变更:新增索引标签(如 uniqueIndex)会自动创建索引,但移除标签不会删除已有索引。
  • 外键约束:AutoMigrate 不会自动创建外键约束,需使用 gorm:"constraint" 标签或手动迁移。
  • 并发安全:多个实例同时执行 AutoMigrate 可能导致冲突,建议在应用启动时单例执行。
最佳实践:在开发环境中频繁使用 AutoMigrate 快速迭代;在测试/预发布环境中验证迁移结果;在生产环境中使用版本化迁移工具(如 golang-migrate)或自定义迁移脚本,避免 AutoMigrate 的隐式变更风险。