go-admin數(shù)據(jù)庫遷移代碼分析-創(chuàng)新互聯(lián)

1,倉庫地址:https://github.com/go-admin-team/go-admin.git 我拉取的是master分支,commit:c973d6819ceb008e0dfa97173ca3c69ce1cfd49a

創(chuàng)新互聯(lián)建站-專業(yè)網(wǎng)站定制、快速模板網(wǎng)站建設、高性價比官渡網(wǎng)站開發(fā)、企業(yè)建站全套包干低至880元,成熟完善的模板庫,直接使用。一站式官渡網(wǎng)站制作公司更省心,省錢,快速模板網(wǎng)站建設找我們,業(yè)務覆蓋官渡地區(qū)。費用合理售后完善,10多年實體公司更值得信賴。

2,這里只分析migrate,go-admin的啟動命令使用cobra封裝(k8s也在用),那第一步就是定義migrate(遷移)指令并且掛載到根Command

import (
    ...
	"go-admin/cmd/migrate"
    ...
)

func init() {
	rootCmd.AddCommand(migrate.StartCmd)
    ...
}

3,migrate.StartCmd定義在cmd/migrate/server.go,因為go-admin是后臺腳手架,自帶了權(quán)限,菜單的管理,作者在定義遷移時,劃分兩種,你可以直接修改go-admin自帶的數(shù)據(jù)模型,也可以自定義模型,詳見代碼注釋

// 我只保留核心邏輯的代碼
import (
	...
	_ "go-admin/cmd/migrate/migration/version" // 系統(tǒng)自帶的遷移目錄
	_ "go-admin/cmd/migrate/migration/version-local" // 自定義遷移目錄
	...
)

var (
	configYml string // 配置文件
	// ./go-admin migrate -g=true -a=false
	generate  bool // 生成自定義遷移文件
	goAdmin   bool // 生成go-admin初始遷移
	host      string // 域, 為什么會有一個域?一個域代表的是一個key,作者是希望支持多庫的,目前 我對多庫的處理是在 ApplicationConfig中新增了一個Database2
    ...
)

...

func run() {

	if !generate { // 默認false,先生成后執(zhí)行
		fmt.Println(`start init`)
		//1. 讀取配置
		config.Setup(
			file.NewSource(file.WithPath(configYml)),
			initDB,
		)
	} else {
		fmt.Println(`generate migration file`)
		_ = genFile() // 生成配置文件
	}
}

func migrateModel() error {
	if host == "" {
		host = "*"
	}
	db := sdk.Runtime.GetDbByKey(host) // 取庫
	if config.DatabasesConfig[host].Driver == "mysql" {
		//初始化數(shù)據(jù)庫時候用
		db.Set("gorm:table_options", "ENGINE=InnoDB CHARSET=utf8mb4")
	}
	err := db.Debug().AutoMigrate(&models.Migration{}) // 遷移庫,維護了一個遷移庫來記錄遷移,根據(jù)版本信息,來確定是否執(zhí)行這次遷移
	if err != nil {
		return err
	}
	migration.Migrate.SetDb(db.Debug()) // 設置 DB
	migration.Migrate.Migrate() // 執(zhí)行遷移
	return err
}
func initDB() {
	//3. 初始化數(shù)據(jù)庫鏈接
	database.Setup()
	//4. 數(shù)據(jù)庫遷移
	fmt.Println("數(shù)據(jù)庫遷移開始")
	_ = migrateModel()
	fmt.Println(`數(shù)據(jù)庫基礎(chǔ)數(shù)據(jù)初始化成功`)
}

func genFile() error {
	t1, err := template.ParseFiles("template/migrate.template") // 解讀模版文件
	if err != nil {
		return err
	}
	m := map[string]string{}
	m["GenerateTime"] = strconv.FormatInt(time.Now().UnixNano()/1e6, 10)
	m["Package"] = "version_local"
    // goAdmin 區(qū)分是 系統(tǒng)級遷移還是自定義的遷移
	if goAdmin {
		m["Package"] = "version"
	}
	var b1 bytes.Buffer
	err = t1.Execute(&b1, m) // map 映射到模版文件并寫入buffer
	if goAdmin {
		pkg.FileCreate(b1, "./cmd/migrate/migration/version/"+m["GenerateTime"]+"_migrate.go")
	} else {
		pkg.FileCreate(b1, "./cmd/migrate/migration/version-local/"+m["GenerateTime"]+"_migrate.go")
	}
	return nil
}

3,再有的核心就是在version和version-local了

... 
// version, version-local 是一樣的邏輯,都是用init觸發(fā)的

func init() {
	_, fileName, _, _ := runtime.Caller(0)
	migration.Migrate.SetVersion(migration.GetFilename(fileName), _1599190683659Tables) // SetVersion是將當前版本加入到遷移數(shù)組當中
}

// 遷移函數(shù)
func _1599190683659Tables(db *gorm.DB, version string) error {
	return db.Transaction(func(tx *gorm.DB) error {
		if config.DatabaseConfig.Driver == "mysql" {
			tx = tx.Set("gorm:table_options", "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4")
		}
		err := tx.Migrator().AutoMigrate(
			new(models.SysDept)
            ...
		)
		if err != nil {
			return err
		}
		if err := models.InitDb(tx); err != nil {
			return err
		}
		return tx.Create(&common.Migration{
			Version: version, // 這里就是要增加當前遷移的版本信息了
		}).Error
	})
}

4,上面是定義好了遷移的實體方法,我們來看一下調(diào)用方式

package migration

import (
	"log"
	"path/filepath"
	"sort"
	"sync"

	"gorm.io/gorm"
)

var Migrate = &Migration{
	version: make(map[string]func(db *gorm.DB, version string) error),
}
// 遷移的必要元素
type Migration struct {
	db      *gorm.DB // 指定的庫
	version map[string]func(db *gorm.DB, version string) error // 每個版本信息對應的遷移函數(shù),就是上面那串時間戳函數(shù)
	mutex   sync.Mutex 
}

...

// 這就是上面的代碼init中,將version信息和遷移函數(shù)做了綁定
func (e *Migration) SetVersion(k string, f func(db *gorm.DB, version string) error) {
	e.mutex.Lock()
	defer e.mutex.Unlock()
	e.version[k] = f // 給指定版本綁定遷移函數(shù)
}

// 真正執(zhí)行遷移的地方
func (e *Migration) Migrate() {
	versions := make([]string, 0)
	for k := range e.version {
		versions = append(versions, k)
	}
	if !sort.StringsAreSorted(versions) {
		sort.Strings(versions)
	}
	var err error
	var count int64
    
    // versions 是什么意思?只要是包含在version和version-local目錄的init方法內(nèi)的,都會加入到versions
    // 然后去庫里匹配version,匹配不到即將遷移的版本信息的,則開始執(zhí)行對應的遷移函數(shù)
	for _, v := range versions {
		err = e.db.Table("sys_migration").Where("version = ?", v).Count(&count).Error
		if err != nil {
			log.Fatalln(err)
		}
		if count >0 { // 有這個版本跳出
			log.Println(count)
			count = 0
			continue
		}
		err = (e.version[v])(e.db.Debug(), v) // 這是遷移函數(shù)
		if err != nil {
			log.Fatalln(err)
		}
	}
}

func GetFilename(s string) string {
	s = filepath.Base(s)
	return s[:13] // 取名字,這也是setVersion的第一個參數(shù)
}

5,有一張記錄遷移信息的表

只有version和apply_time兩個字段

6,完。

你是否還在尋找穩(wěn)定的海外服務器提供商?創(chuàng)新互聯(lián)www.cdcxhl.cn海外機房具備T級流量清洗系統(tǒng)配攻擊溯源,準確流量調(diào)度確保服務器高可用性,企業(yè)級服務器適合批量采購,新人活動首月15元起,快前往官網(wǎng)查看詳情吧

當前文章:go-admin數(shù)據(jù)庫遷移代碼分析-創(chuàng)新互聯(lián)
轉(zhuǎn)載來源:http://muchs.cn/article6/cochog.html

成都網(wǎng)站建設公司_創(chuàng)新互聯(lián),為您提供企業(yè)建站、網(wǎng)站營銷、軟件開發(fā)網(wǎng)站內(nèi)鏈、企業(yè)網(wǎng)站制作定制網(wǎng)站

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)

網(wǎng)站優(yōu)化排名