4. AutoMigrate 入门:基本用法与自动表结构管理

AutoMigrate 是 GORM 提供的最便捷的数据库迁移入口。它不需要编写任何 DDL 语句,只需定义好 Go 结构体,调用 AutoMigrate 方法即可自动完成表的创建与字段同步。本章将围绕其基本用法、自动建表与自动更新表结构三个核心场景展开,帮助你快速建立对 AutoMigrate 的直观认知。

4.1 AutoMigrate 核心机制

AutoMigrate 本质上是一个“增量同步”工具。它读取结构体字段定义,与数据库中已有表结构进行对比,然后执行必要的 ALTER TABLE 或 CREATE TABLE 操作。它不会删除已有字段或表,也不会修改字段类型(除非显式配置)。

关键原则:AutoMigrate 只做“加法”和“非破坏性变更”,不会删除列或索引,也不会改变已有列的数据类型。这是它与传统迁移工具最大的区别。

4.2 基本用法:一行代码完成迁移

在 GORM 中,调用 AutoMigrate 只需要传入一个或多个结构体指针。以下是最简示例:

package main

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

type User struct {
  ID   uint
  Name string
  Age  int
}

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

  // 自动迁移:创建或更新 User 表
  db.AutoMigrate(&User{})
}

执行上述代码后,GORM 会自动在数据库中创建 users 表(表名由结构体名自动转为蛇形复数),包含 id、name、age 三个字段。

4.3 自动创建表:从零到表

当数据库中不存在对应表时,AutoMigrate 会执行 CREATE TABLE。下表展示了结构体字段与数据库列的默认映射规则:

Go 类型 数据库列类型(SQLite) 数据库列类型(MySQL) 数据库列类型(PostgreSQL)
uint / int INTEGER BIGINT UNSIGNED / BIGINT BIGSERIAL / BIGINT
string TEXT VARCHAR(255) TEXT
bool NUMERIC TINYINT(1) BOOLEAN
time.Time DATETIME DATETIME(3) TIMESTAMP WITH TIME ZONE
float64 REAL DOUBLE DOUBLE PRECISION
提示:可以通过标签 gorm:"type:varchar(100)" 自定义列类型,覆盖默认映射。

4.4 自动更新表结构:新增字段与索引

当表已存在,但结构体新增了字段时,AutoMigrate 会自动执行 ALTER TABLE ADD COLUMN。以下示例演示了新增 Email 字段后的迁移行为:

// 假设数据库已有 users 表(id, name, age)
type User struct {
  ID    uint
  Name  string
  Age   int
  Email string  // 新增字段
}

// 执行迁移
db.AutoMigrate(&User{})
// 数据库 users 表自动增加 email TEXT 列

除了新增列,AutoMigrate 还会自动创建索引(如果结构体字段定义了索引标签):

type Product struct {
  Code  string `gorm:"uniqueIndex"`
  Price uint
}

db.AutoMigrate(&Product{})
// 自动创建 products 表,并为 code 列创建唯一索引

4.5 AutoMigrate 执行流程

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

AutoMigrate 执行流程 Go 结构体定义 解析字段与标签 对比数据库现有表结构 CREATE TABLE ALTER TABLE ADD COLUMN

4.6 多模型迁移与批量操作

AutoMigrate 支持同时传入多个结构体,一次性完成多张表的创建或更新:

type Order struct {
  ID        uint
  UserID    uint
  Amount    float64
  CreatedAt time.Time
}

type Payment struct {
  ID      uint
  OrderID uint
  Method  string
  Status  string
}

// 批量迁移
db.AutoMigrate(&User{}, &Order{}, &Payment{})

执行后,数据库中会同时存在 users、orders、payments 三张表。如果某张表已存在,则只执行增量变更。

4.7 常见避坑清单

  • 字段删除不生效:AutoMigrate 不会删除结构体中已移除的字段。如需删除列,需手动执行 Migrator().DropColumn()。
  • 类型变更不生效:将 string 改为 int 不会触发列类型修改。需先手动删除旧列再迁移。
  • 表名冲突:默认表名为结构体名的蛇形复数。可通过 TableName() 方法自定义表名。
  • 外键自动创建:GORM 默认会为 belongs to 关系自动创建外键约束,可通过标签 gorm:"constraint:OnUpdate:CASCADE,OnDelete:SET NULL;" 控制。
警告:在生产环境中,不建议直接使用 AutoMigrate 管理已有大量数据的表。新增列时,如果列设置了 NOT NULL 且无默认值,会导致迁移失败。建议先在测试环境验证迁移行为。

4.8 迁移前后对比验证

为了确认 AutoMigrate 是否按预期执行,可以通过 GORM 的 Migrator() 接口进行验证:

// 迁移前检查表是否存在
if db.Migrator().HasTable(&User{}) {
  // 检查列是否存在
  if db.Migrator().HasColumn(&User{}, "Email") {
    println("Email 列已存在,跳过迁移")
  } else {
    println("Email 列不存在,执行迁移")
    db.AutoMigrate(&User{})
  }
} else {
  println("表不存在,执行创建")
  db.AutoMigrate(&User{})
}

这种前置检查方式在冒烟回归测试中非常实用,可以精确控制迁移的执行路径,避免重复操作。