Files
go-common/docs/migration.md

11 KiB
Raw Permalink Blame History

数据库迁移工具文档

概述

数据库迁移工具提供了数据库版本管理和迁移功能支持MySQL、PostgreSQL、SQLite等数据库。使用GORM作为数据库操作库可以方便地进行数据库结构的版本控制。

功能特性

  • 支持迁移版本管理
  • 支持迁移和回滚操作
  • 支持从文件系统加载迁移文件
  • 支持迁移状态查询
  • 自动创建迁移记录表
  • 事务支持,确保迁移的原子性

🚀 最简单的使用方式(黑盒模式,推荐)

这是最简单的迁移方式,内部自动处理配置加载、数据库连接、迁移执行等所有细节。

方式一:使用独立迁移工具(推荐)

  1. 复制迁移工具模板到你的项目
mkdir -p cmd/migrate
cp /path/to/go-common/templates/migrate/main.go cmd/migrate/
  1. 创建迁移文件
mkdir -p migrations
# 或使用其他目录,如 scripts/sql

创建 migrations/01_init_schema.sql

CREATE TABLE users (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    username VARCHAR(255) NOT NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
  1. 编译和使用
# 编译
go build -o bin/migrate cmd/migrate/main.go

# 使用(最简单,使用默认配置和默认迁移目录)
./bin/migrate up

# 指定配置文件
./bin/migrate up -config config.json

# 指定配置和迁移目录
./bin/migrate up -config config.json -dir scripts/sql

# 查看状态
./bin/migrate status

# 回滚
./bin/migrate down

特点

  • 零配置:使用默认值即可运行
  • 自动查找配置:支持环境变量、默认路径
  • 自动处理:配置加载、数据库连接、迁移执行全自动

方式二:在代码中直接调用(简单场景)

import "git.toowon.com/jimmy/go-common/migration"

// 最简单:使用默认配置和默认迁移目录
err := migration.RunMigrationsFromConfigWithCommand("", "", "up")

// 指定配置文件,使用默认迁移目录
err := migration.RunMigrationsFromConfigWithCommand("config.json", "", "up")

// 指定配置和迁移目录
err := migration.RunMigrationsFromConfigWithCommand("config.json", "scripts/sql", "up")

// 查看状态
err := migration.RunMigrationsFromConfigWithCommand("config.json", "migrations", "status")

参数说明

  • configFile: 配置文件路径空字符串时自动查找config.json, ../config.json
  • migrationsDir: 迁移文件目录,空字符串时使用默认值 "migrations"
  • command: 命令,支持 "up", "down", "status"

方式三使用Factory如果项目已使用Factory

import "git.toowon.com/jimmy/go-common/factory"

fac, _ := factory.NewFactoryFromFile("config.json")
// 使用默认目录 "migrations"
err := fac.RunMigrations()
// 或指定目录
err := fac.RunMigrations("scripts/sql")

详细使用方法(高级功能)

1. 创建迁移器

import (
    "gorm.io/driver/mysql"
    "gorm.io/gorm"
    "git.toowon.com/jimmy/go-common/migration"
)

// 初始化数据库连接
dsn := "user:password@tcp(localhost:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})

// 创建迁移器
migrator := migration.NewMigrator(db)
// 或者指定自定义的迁移记录表名
migrator := migration.NewMigrator(db, "my_migrations")

2. 添加迁移

方式一:代码方式添加迁移

migrator.AddMigration(migration.Migration{
    Version:     "20240101000001",
    Description: "create_users_table",
    Up: func(db *gorm.DB) error {
        return db.Exec(`
            CREATE TABLE users (
                id INT PRIMARY KEY AUTO_INCREMENT,
                name VARCHAR(255) NOT NULL,
                email VARCHAR(255) UNIQUE NOT NULL,
                created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
            )
        `).Error
    },
    Down: func(db *gorm.DB) error {
        return db.Exec("DROP TABLE IF EXISTS users").Error
    },
})

方式二:批量添加迁移

migrations := []migration.Migration{
    {
        Version:     "20240101000001",
        Description: "create_users_table",
        Up: func(db *gorm.DB) error {
            return db.Exec("CREATE TABLE users ...").Error
        },
        Down: func(db *gorm.DB) error {
            return db.Exec("DROP TABLE users").Error
        },
    },
    {
        Version:     "20240101000002",
        Description: "add_index_to_users",
        Up: func(db *gorm.DB) error {
            return db.Exec("CREATE INDEX idx_email ON users(email)").Error
        },
        Down: func(db *gorm.DB) error {
            return db.Exec("DROP INDEX idx_email ON users").Error
        },
    },
}
migrator.AddMigrations(migrations...)

方式三:从文件加载迁移(推荐)

// 支持的文件命名格式:
//   1. 数字前缀: 01_init_schema.sql
//   2. 时间戳: 20240101000001_create_users.sql
//   3. 带.up后缀: 20240101000001_create_users.up.sql
//   对应的回滚文件: 20240101000001_create_users.down.sql

migrations, err := migration.LoadMigrationsFromFiles("./migrations", "*.sql")
if err != nil {
    log.Fatal(err)
}

migrator.AddMigrations(migrations...)

新特性:

  • 支持数字前缀命名(如 01_init_schema.sql
  • 自动分割多行 SQL 语句
  • 自动处理注释(单行 -- 和多行 /* */
  • 记录执行时间(毫秒)

3. 执行迁移

// 执行所有未应用的迁移
err := migrator.Up()
if err != nil {
    log.Fatal(err)
}

4. 回滚迁移

// 回滚最后一个迁移
err := migrator.Down()
if err != nil {
    log.Fatal(err)
}

5. 查看迁移状态

status, err := migrator.Status()
if err != nil {
    log.Fatal(err)
}

for _, s := range status {
    fmt.Printf("Version: %s, Description: %s, Applied: %v\n", 
        s.Version, s.Description, s.Applied)
}

6. 重置迁移

方式一:仅清空迁移记录(不回滚数据库变更)

// 直接调用(需要传入确认标志)
err := migrator.Reset(true)
if err != nil {
    log.Fatal(err)
}

// 交互式确认(推荐,会提示警告信息)
err := migrator.ResetWithConfirm()
if err != nil {
    log.Fatal(err)
}

方式二:回滚所有迁移并清空记录

// 直接调用(需要传入确认标志)
err := migrator.ResetAll(true)
if err != nil {
    log.Fatal(err)
}

// 交互式确认(推荐,会提示警告信息)
err := migrator.ResetAllWithConfirm()
if err != nil {
    log.Fatal(err)
}

注意:

  • Reset() 仅清空迁移记录表,不会回滚已执行的数据库变更
  • ResetAll() 会回滚所有已应用的迁移执行Down函数然后清空记录
  • 交互式方法会显示详细的警告信息,需要输入确认文本才能执行

7. 生成迁移版本号

// 生成基于时间戳的版本号
version := migration.GenerateVersion()
// 输出: 1704067200 (Unix时间戳)

API 参考

Migration 结构体

type Migration struct {
    Version     string              // 迁移版本号(必须唯一)
    Description string              // 迁移描述
    Up          func(*gorm.DB) error // 升级函数
    Down        func(*gorm.DB) error // 回滚函数(可选)
}

Migrator 方法

NewMigrator(db *gorm.DB, tableName ...string) *Migrator

创建新的迁移器。

参数:

  • db: GORM数据库连接
  • tableName: 可选,迁移记录表名,默认为 "schema_migrations"

返回: 迁移器实例

AddMigration(migration Migration)

添加单个迁移。

AddMigrations(migrations ...Migration)

批量添加迁移。

Up() error

执行所有未应用的迁移。按版本号升序执行。

返回: 错误信息

Down() error

回滚最后一个已应用的迁移。

返回: 错误信息

Status() ([]MigrationStatus, error)

查看所有迁移的状态。

返回: 迁移状态列表和错误信息

Reset(confirm bool) error

重置所有迁移记录(仅清空记录表,不回滚数据库变更)。

参数:

  • confirm: 确认标志必须为true才能执行

返回: 错误信息

注意: 此操作只清空迁移记录不会回滚已执行的迁移操作。如果需要回滚迁移请先使用Down方法逐个回滚。

ResetWithConfirm() error

交互式重置所有迁移记录(带确认提示)。

返回: 错误信息

说明: 会显示警告信息,需要输入"RESET"(全大写)才能执行。

ResetAll(confirm bool) error

重置所有迁移并回滚所有已应用的迁移。

参数:

  • confirm: 确认标志必须为true才能执行

返回: 错误信息

注意: 此操作会回滚所有已应用的迁移执行Down函数然后清空迁移记录。操作不可逆请谨慎使用。

ResetAllWithConfirm() error

交互式重置所有迁移并回滚(带确认提示)。

返回: 错误信息

说明: 会显示警告信息,需要输入"RESET ALL"(全大写)才能执行。

MigrationStatus 结构体

type MigrationStatus struct {
    Version     string // 版本号
    Description string // 描述
    Applied     bool   // 是否已应用
}

辅助函数

LoadMigrationsFromFiles(dir string, pattern string) ([]Migration, error)

从文件系统加载迁移文件。

参数:

  • dir: 迁移文件目录
  • pattern: 文件匹配模式,如 ".sql" 或 ".up.sql"

返回: 迁移列表和错误信息

支持的文件命名格式:

  1. 数字前缀格式(新支持):

    • 01_init_schema.sql
    • 02_init_data.sql
    • 03_add_log_schema.sql
  2. 时间戳格式(现有):

    • 20240101000001_create_users.sql
    • 20240101000002_add_index.sql
  3. 带.up后缀格式(现有):

    • 20240101000001_create_users.up.sql - 升级文件
    • 20240101000001_create_users.down.sql - 回滚文件(可选)

新特性:

  • 自动识别文件命名格式(数字前缀或时间戳)
  • 自动分割多行 SQL 语句(按分号分割)
  • 自动处理注释(单行 -- 和多行 /* */
  • 自动跳过空行和空白字符
  • 支持一个文件包含多个 SQL 语句

GenerateVersion() string

生成基于时间戳的迁移版本号。

返回: Unix时间戳字符串

注意事项

  1. 迁移版本号:必须唯一,建议使用时间戳格式
  2. 事务支持:迁移操作在事务中执行,失败会自动回滚
  3. 自动创建表:迁移记录表会自动创建,无需手动创建
  4. 回滚文件如果迁移文件没有对应的down文件回滚操作会失败
  5. 执行顺序:迁移按版本号升序执行,确保顺序正确
  6. 重置操作
    • Reset() 只清空迁移记录,不会回滚数据库变更
    • ResetAll() 会回滚所有迁移,操作不可逆
    • 建议使用交互式方法(ResetWithConfirmResetAllWithConfirm)以确保安全
    • 在生产环境使用重置功能前,请确保已备份数据库

示例

完整示例请参考 examples/migration_example.go