目錄
文章目錄
- 目錄
- 前言
- gormigrate
- 核心結構體
- 實作分析
- 版本定義
- InitSchema
- Migration
- 版本記錄(歷史)
- 版本升級和回退
前言
- 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
}
-
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 錯誤,
}
- 版本遷移 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
示例:
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
})
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
示例:
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")
}
版本記錄(歷史)
同樣的,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
}
而 Version History 則是資料庫表 migrations 中的記錄:
test_db=# select * from migrations;
id
-------------
SCHEMA_INIT
v1
v2
v3
(4 行記錄)
gormigrate 通過 Version History 記錄來控制版本的升級和回退,如果已經升級完成(存在記錄)的版本則不會被重復執行,否者就會進行全量的初始化或增量的升級,
版本升級和回退
從 Migration 的示例中可以看出,gormigrate 本質上是在封裝 GORM 的 AutoMigrate 和 Migrator DDL 介面的基礎之上實作了版本記錄的功能,所以執行版本升級和回退的實作依舊來自于 GORM 的能力,
對此,筆者在《Go 語言編程 — gorm 資料庫版本遷移》已經有過介紹,這里就不再贅述了,
此外,Gormigrate 還提供了
- MigrateTo:升級到指定 ID 的版本,
- RollbackTo:回退到指定 ID 的版本,
- RollbackLast:撤消上一次遷移,
- RollbackMigration:執行自定義的回退函式,
通過這些方法,已經可以在一定程度上滿足資料庫應用程式在 ORM Schames Version Control 上的需求了,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/169479.html
標籤:其他
上一篇:Java核心技術-鎖
