# 数据库迁移工具文档 ## 概述 数据库迁移工具提供了数据库版本管理和迁移功能,支持MySQL、PostgreSQL、SQLite等数据库。使用GORM作为数据库操作库,可以方便地进行数据库结构的版本控制。 ## 功能特性 - 支持迁移版本管理 - 支持迁移和回滚操作 - 支持从文件系统加载迁移文件 - 支持迁移状态查询 - 自动创建迁移记录表 - 事务支持,确保迁移的原子性 ## 🚀 最简单的使用方式(黑盒模式,推荐) 这是最简单的迁移方式,内部自动处理配置加载、数据库连接、迁移执行等所有细节。 ### 方式一:使用独立迁移工具(推荐) 1. **复制迁移工具模板到你的项目**: ```bash mkdir -p cmd/migrate cp /path/to/go-common/templates/migrate/main.go cmd/migrate/ ``` 2. **创建迁移文件**: ```bash mkdir -p migrations # 或使用其他目录,如 scripts/sql ``` 创建 `migrations/01_init_schema.sql`: ```sql CREATE TABLE users ( id BIGINT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(255) NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); ``` 3. **编译和使用**: ```bash # 编译 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 ``` **特点**: - ✅ 零配置:使用默认值即可运行 - ✅ 自动查找配置:支持环境变量、默认路径 - ✅ 自动处理:配置加载、数据库连接、迁移执行全自动 ### 方式二:在代码中直接调用(简单场景) ```go 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)或使用环境变量 DATABASE_URL - `migrationsDir`: 迁移文件目录,空字符串时使用默认值 "migrations" - `command`: 命令,支持 "up", "down", "status" ### 方式三:使用Factory(如果项目已使用Factory) ```go import "git.toowon.com/jimmy/go-common/factory" fac, _ := factory.NewFactoryFromFile("config.json") // 使用默认目录 "migrations" err := fac.RunMigrations() // 或指定目录 err := fac.RunMigrations("scripts/sql") ``` --- ## 详细使用方法(高级功能) ### 1. 创建迁移器 ```go 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. 添加迁移 #### 方式一:代码方式添加迁移 ```go 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 }, }) ``` #### 方式二:批量添加迁移 ```go 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...) ``` #### 方式三:从文件加载迁移(推荐) ```go // 支持的文件命名格式: // 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. 执行迁移 ```go // 执行所有未应用的迁移 err := migrator.Up() if err != nil { log.Fatal(err) } ``` ### 4. 回滚迁移 ```go // 回滚最后一个迁移 err := migrator.Down() if err != nil { log.Fatal(err) } ``` ### 5. 查看迁移状态 ```go 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. 重置迁移 #### 方式一:仅清空迁移记录(不回滚数据库变更) ```go // 直接调用(需要传入确认标志) err := migrator.Reset(true) if err != nil { log.Fatal(err) } // 交互式确认(推荐,会提示警告信息) err := migrator.ResetWithConfirm() if err != nil { log.Fatal(err) } ``` #### 方式二:回滚所有迁移并清空记录 ```go // 直接调用(需要传入确认标志) err := migrator.ResetAll(true) if err != nil { log.Fatal(err) } // 交互式确认(推荐,会提示警告信息) err := migrator.ResetAllWithConfirm() if err != nil { log.Fatal(err) } ``` **注意:** - `Reset()` 仅清空迁移记录表,不会回滚已执行的数据库变更 - `ResetAll()` 会回滚所有已应用的迁移(执行Down函数),然后清空记录 - 交互式方法会显示详细的警告信息,需要输入确认文本才能执行 ### 7. 生成迁移版本号 ```go // 生成基于时间戳的版本号 version := migration.GenerateVersion() // 输出: 1704067200 (Unix时间戳) ``` ## API 参考 ### Migration 结构体 ```go 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 结构体 ```go 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()` 会回滚所有迁移,操作不可逆 - 建议使用交互式方法(`ResetWithConfirm`、`ResetAllWithConfirm`)以确保安全 - 在生产环境使用重置功能前,请确保已备份数据库 ## 示例 完整示例请参考 `examples/migration_example.go`