自动迁移

AutoMigrate 是 GORM 提供的自动表结构同步功能。定义好模型后,一行代码就能创建或更新表结构,开发阶段非常方便。

基本用法

type User struct {
    ID     uint
    Name   string
    Age    int
    Email  string
}

db.AutoMigrate(&User{})

执行后会:

  • 表不存在则创建
  • 表存在则检查结构差异
  • 缺少的字段会添加
  • 多余的字段不会删除

多模型迁移

同时迁移多个模型:

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

自动迁移的行为

会做的:

  • 创建表
  • 添加缺少的字段
  • 创建缺少的索引
  • 创建缺少的外键

不会做的:

  • 删除多余的字段
  • 删除多余的索引
  • 修改字段类型
  • 删除数据

这种保守策略是为了保护数据安全。

字段标签

通过标签控制字段属性:

type User struct {
    ID        uint   `gorm:"primaryKey"`
    Name      string `gorm:"type:varchar(100);not null"`
    Email     string `gorm:"uniqueIndex;size:255"`
    Age       int    `gorm:"default:18"`
    Bio       string `gorm:"type:text"`
    CreatedAt time.Time
}

常用标签:

标签说明
type字段类型
size字符串长度
not null非空约束
unique唯一约束
default默认值
comment字段注释
index创建索引
uniqueIndex创建唯一索引

禁用自动迁移

生产环境可能想手动控制迁移:

db.Set("gorm:table_options", "ENGINE=InnoDB").AutoMigrate(&User{})

或者完全不使用 AutoMigrate,用迁移工具如 golang-migrate、goose 等。

迁移器接口

GORM 提供了迁移器接口,可以更精细地控制:

type Migrator interface {
    AutoMigrate(dst ...interface{}) error
    CreateTable(dst ...interface{}) error
    DropTable(dst ...interface{}) error
    HasTable(dst interface{}) bool
    RenameTable(oldName, newName interface{}) error
    AddColumn(dst interface{}, field string) error
    DropColumn(dst interface{}, field string) error
    AlterColumn(dst interface{}, field string) error
    HasColumn(dst interface{}, field string) bool
    RenameColumn(dst interface{}, oldName, field string) error
    CreateIndex(dst interface{}, name string) error
    DropIndex(dst interface{}, name string) error
    HasIndex(dst interface{}, name string) bool
}

使用迁移器:

migrator := db.Migrator()

if !migrator.HasTable(&User{}) {
    migrator.CreateTable(&User{})
}

if !migrator.HasColumn(&User{}, "nickname") {
    migrator.AddColumn(&User{}, "nickname")
}

数据库差异

不同数据库的迁移行为有差异:

MySQL

  • 使用 ALTER TABLE 添加字段
  • 支持 AUTO_INCREMENT

PostgreSQL

  • 使用 SERIAL 类型
  • 支持 UUID 类型

SQLite

  • 不支持 ALTER COLUMN
  • 某些修改需要重建表

SQL Server

  • 使用 IDENTITY 自增
  • 字段类型名称不同

GORM 会处理大部分差异,但复杂迁移可能需要手写 SQL。

迁移日志

查看迁移执行的 SQL:

db.Debug().AutoMigrate(&User{})

或者配置日志:

newLogger := logger.New(
    log.New(os.Stdout, "\r\n", log.LstdFlags),
    logger.Config{
        LogLevel: logger.Info,
    },
)

db, _ := gorm.Open(mysql.Open(dsn), &gorm.Config{
    Logger: newLogger,
})

迁移时机

开发阶段:每次启动时执行 AutoMigrate

func InitDB() {
    db, _ := gorm.Open(mysql.Open(dsn), &gorm.Config{})
    db.AutoMigrate(&User{}, &Order{}, &Product{})
}

生产环境:使用版本化迁移工具,手动执行

if os.Getenv("ENV") == "production" {
    // 不自动迁移
} else {
    db.AutoMigrate(&User{})
}

模型变更检测

GORM 如何检测模型变更?

  1. 读取数据库表结构
  2. 与模型定义对比
  3. 生成差异 SQL
  4. 执行变更

只检测结构差异,不检测约束差异(如索引类型变化)。

小结

AutoMigrate 是开发利器,但生产环境要谨慎使用。理解它的行为边界,配合迁移工具,才能安全地管理数据库结构。