Go 语言编程 — gormigrate GORM 的数据库迁移助手
目录
前言
- GORM v2
- gormigrate v2
- 程序 Demo:https://github.com/JmilkFan/gormigrate-demo
gormigrate
GORM 本身提供了 AutoMigrate 功能以及 Migrator 提供的 DDL 接口,但 GORM 更加专注于 ORM 层面,所以在 ORM Schema Version Control(数据库版本控制)有所欠缺。而 gormigrate 就是一个轻量化的 Schema Migration Helper(迁移助手),基于 GORM AutoMigrate 和 Migrator 进行封装,用于弥补这一块的缺失。
- Github:https://github.com/go-gormigrate/gormigrate
需要注意的是,简洁清理是 gormigrate 最大的优势也是不足,如果是大型系统有着复杂的版本控制要求,则建议使用 golang-migrate/migrate。
核心结构体
gormigrate 的核心 Struct 是 Gormigrate:
// Gormigrate represents a collection of all migrations of a database schema.
type Gormigrate struct { db *gorm.DB // DBClient 实例 tx *gorm.DB // 事务型 DBClient 实例 options *Options // migrations 配置选项 migrations []*Migration // 版本迁移 Schemas initSchema InitSchemaFunc // 版本初始化 Schemas
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
-
DBClient 实例:最基础的,也是最通用的 GORM DBClient 实例。
-
事务型 DBClient 实例:也是 GORM 的 DBClient 实例,区别在于会使用 RDBMS 的 Transaction(事务)特性来执行 GORM 封装好的 Migrator DDL。注意,是都支持事务性 DBClient 跟 RDBMS 的类型有关。
-
migrations 配置选项:用于配置 migrate 的执行细节。
type Options struct { TableName string // 指定 migrations 版本记录表的表名,默认为 migrations。 IDColumnName string // 指定 migrations 版本记录表的列名,默认为 id。 IDColumnSize int // 指定 migrations 版本记录表的列属性,默认为 varchar(255)。 UseTransaction bool // 指定是否使用 Transaction 来执行 GORM Migrator DDL。 ValidateUnknownMigrations bool // 指定当 migrations 版本记录表有非法记录时,是否触发 ErrUnknownPastMigration 错误。
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 版本迁移 Schemas:用于定义版本迁移的 Schemas。
- 版本初始化 Schemas:用于定义版本初始化的 Schemas。
实现分析
ORM Schema Version Control 需要具备的最基本功能元素:
- 版本定义
- 版本记录(历史)
- 版本升级
- 版本回退
版本定义
正如上文介绍到的,gormigrate 大体上支持两种类型的版本定义:
- 版本迁移 Schemas:用于定义版本迁移的 Schemas。
- 版本初始化 Schemas:用于定义版本初始化的 Schemas。
InitSchema
应用于 init_database_no_table 的场景,可以通过调用 Gormigrate 的 InitSchema 方法注册一个版本初始化 Schemas 函数 “InitSchemaFunc”,并在一个新的干净的数据库中运行它,完成一次全量建表的过程。注意,InitSchema 是没有 Rollback 的。
函数签名:
// InitSchemaFunc is the func signature for initializing the schema.
type InitSchemaFunc func(*gorm.DB) error
- 1
- 2
示例:
type Person struct {
gorm.Model
Name string
Age int
}
type Pet struct {
gorm.Model
Name string
PersonID int
}
m := gormigrate.New(db, gormigrate.DefaultOptions, []*gormigrate.Migration{ // you migrations here
})
m.InitSchema(func(tx *gorm.DB) error {
err := tx.AutoMigrate(
&Person{},
&Pet{},
// all other tables of you app
)
if err != nil {
return err
}
if err := tx.Exec("ALTER TABLE pets ADD CONSTRAINT fk_pets_people FOREIGN KEY (person_id) REFERENCES people (id)").Error; err != nil {
return err
}
// all other foreign keys...
return nil
})
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
Migration
应用于完成初始化之后的 “增量迁移” 场景,可以通过调用 Gormigrate 的 Migration 方法注册一个 MigrateFunc 和一个 RollbackFunc 函数,前者用于 Upgrade,后者则是对应的 Downgrade,以此来完成 Schema 的升级和回退。
NOTE:当我们使用 InitSchema 和 Migration 方法的时候切记不能使用同一个 Gormigrate 实例,否则会出现只执行 InitSchema 不执行 Migration 的情况,导致数据库版本无法迁移到 Latest 的现象。因为 InitSchema 在 DDL 建表的时候会把 Migration 的版本记录都插入到 migrations 表里面去,但实际上并没有执行 Migration 的 DDL。
函数签名:
// MigrateFunc is the func signature for migrating.
type MigrateFunc func(*gorm.DB) error
// RollbackFunc is the func signature for rollbacking.
type RollbackFunc func(*gorm.DB) error
- 1
- 2
- 3
- 4
- 5
示例:
package main
import (
"log"
"github.com/go-gormigrate/gormigrate/v2"
"gorm.io/gorm"
_ "github.com/jinzhu/gorm/dialects/sqlite"
)
func main() {
db, err := gorm.Open("sqlite3", "mydb.sqlite3")
if err != nil {
log.Fatal(err)
}
db.LogMode(true)
m := gormigrate.New(db, gormigrate.DefaultOptions, []*gormigrate.Migration{
// create persons table
{ ID: "201608301400", Migrate: func(tx *gorm.DB) error { // it's a good pratice to copy the struct inside the function, // so side effects are prevented if the original struct changes during the time type Person struct { gorm.Model Name string } return tx.AutoMigrate(&Person{}) }, Rollback: func(tx *gorm.DB) error { return tx.Migrator().DropTable("people") },
},
// add age column to persons
{ ID: "201608301415", Migrate: func(tx *gorm.DB) error { // when table already exists, it just adds fields as columns type Person struct { Age int } return tx.AutoMigrate(&Person{}) }, Rollback: func(tx *gorm.DB) error { return tx.Migrator().DropColumn("people", "age") },
},
// add pets table
{ ID: "201608301430", Migrate: func(tx *gorm.DB) error { type Pet struct { gorm.Model Name string PersonID int } return tx.AutoMigrate(&Pet{}) }, Rollback: func(tx *gorm.DB) error { return tx.Migrator().DropTable("pets") },
},
})
if err = m.Migrate(); err != nil {
log.Fatalf("Could not migrate: %v", err)
}
log.Printf("Migration did run successfully")
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
版本记录(历史)
同样的,gormigrate 也为 InitSchema 和 Migration 提供了两种版本记录的方式,前者的 Version ID 硬编码为 SCHEMA_INIT,后者的 Version ID 则为自定义的 Migration struct 的 ID 字段:
// Migration represents a database migration (a modification to be made on the database).
type Migration struct { // ID is the migration identifier. Usually a timestamp like "201601021504". ID string // Migrate is a function that will br executed while running this migration. Migrate MigrateFunc // Rollback will be executed on rollback. Can be nil. Rollback RollbackFunc
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
而 Version History 则是数据库表 migrations 中的记录:
test_db=# select * from migrations; id
-------------
SCHEMA_INIT
v1
v2
v3
(4 行记录)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
gormigrate 通过 Version History 记录来控制版本的升级和回退,如果已经升级完成(存在记录)的版本则不会被重复执行,否者就会进行全量的初始化或增量的升级。
版本升级和回退
从 Migration 的示例中可以看出,gormigrate 本质上是在封装 GORM 的 AutoMigrate 和 Migrator DDL 接口的基础之上实现了版本记录的功能,所以执行版本升级和回退的实现依旧来自于 GORM 的能力。
对此,笔者在《Go 语言编程 — gorm 数据库版本迁移》已经有过介绍,这里就不再赘述了。
此外,Gormigrate 还提供了
- MigrateTo:升级到指定 ID 的版本。
- RollbackTo:回退到指定 ID 的版本。
- RollbackLast:撤消上一次迁移。
- RollbackMigration:执行自定义的回退函数。
通过这些方法,已经可以在一定程度上满足数据库应用程序在 ORM Schames Version Control 上的需求了。
文章来源: is-cloud.blog.csdn.net,作者:范桂飓,版权归原作者所有,如需转载,请联系作者。
原文链接:is-cloud.blog.csdn.net/article/details/108987019
- 点赞
- 收藏
- 关注作者
评论(0)