尝试从Go中使用golang-migrate/migrate作为一个包
golang-migrate/migrate 是什么?
使用Go编写的数据库迁移工具
与pressly/goose并列,这是一个受欢迎的Go制作的迁移工具
可以从CLI和Go项目(作为导入的包使用)两者中使用。
-
- レポジトリ
-
- golang-migrate/migrate
- mattes/migrateをforkしたもので、もともとのmattes/migrateは非推奨になってメンテナンスすでに行われていない
因为CLI在安装环境上会有可能出现安装或不安装的问题,并且也很容易忘记命令或出错,所以为了将其编码,我尝试将其用作Go语言包的备忘录。
环境
-
- AWS Cloud9 (シンガポールリージョンのEC2上で動作)
-
- Amazon Linux2
-
- zsh
- mysql
Go的版本
使用Go的版本为1.11.5。
从1.11开始,可以使用go mod而不是dep包管理器来管理模块。
dep还不支持语义化版本控制。
由于golang-migrate/migrate未来将优先从v4的语义化版本开始进行维护和更新,因此建议使用go mod。
-
- go modについてはこの記事が詳しかった
- Go 1.11 の modules・vgo を試す – 実際に使っていく上で考えないといけないこと #golang
创建SQL文件(向上、向下)
将_.up.sql和_.down.sql两个SQL文件作为一个集合进行处理
例如
1_create_table.up.sql
1_create_table.down.sql
在这种情况下,migrate只关注于的部分,而部分则只需关注可读性,因此最好为其命名为可以理解SQL内容的名称。
向上
填写要进行更改的 SQL 文件,例如编写 create table 语句、update 语句和 insert 语句。
下去 (xià qù)
从进行了更改的状态返回到更改之前的状态,编写查询以执行此操作。例如,对于创建表(create table)可以使用删除表(drop table)等操作。
将进行了更改的状态还原为之前的状态,编写查询以实现此操作。例如,可以使用删除表(drop table)等语句来对创建表(create table)进行操作。
请用汉语衍述以下内容,仅需要一个选项:
版本
可以按照1 → 2 → 3 …的方式递增,也可以使用日期时间的时间戳,如201902101130 → 201902101230。关键是只要能确定SQL文件的顺序,就需要将其设置为升序。
我們決定將創建檔案的日期和時間作為來使用。
-
- いちいち自分でファイル名を書くのは面倒なのでシェルスクリプトを書きました
- 引数にとった文字列でを先頭につけた空ファイルを作成する
创建一个导入包的批处理文件
-
- Goのパッケージとしてのドキュメントはこちら
https://godoc.org/github.com/golang-migrate/migrate
package main
import (
"flag"
"fmt"
"github.com/golang-migrate/migrate/v4"
_ "github.com/golang-migrate/migrate/v4/database/mysql"
_ "github.com/golang-migrate/migrate/v4/source/file"
"os"
)
//sql and database info
const (
Source = "file://./sql/"
Database = "mysql://user:password@tcp(0.0.0.0:3306)/database"
)
//declare command line options
var (
Command = flag.String("exec", "", "set up or down as a argument")
Force = flag.Bool("f", false, "force exec fixed sql")
)
//available command list
var AvailableExecCommands = map[string]string{
"up": "Execute up sqls",
"down": "Execute down sqls",
"version": "Just check current migrate version",
}
func main() {
flag.Parse()
if len(*Command) < 1 {
fmt.Println("\nerror: no argument\n")
showUsageMessge()
os.Exit(1)
return
}
m, err := migrate.New(Source, Database)
if err != nil {
fmt.Println("err", err)
}
version, dirty, err := m.Version()
showVersionInfo(version, dirty, err)
fmt.Println("command: exec", *Command)
applyQuery(m, version, dirty)
}
//exec up or down sqls
//with force option if needed
func applyQuery(m *migrate.Migrate, version uint, dirty bool) {
if dirty && *Force {
fmt.Println("force=true: force execute current version sql")
m.Force(int(version))
}
var err error
switch *Command {
case "up":
err = m.Up()
case "down":
err = m.Down()
case "version":
//do nothing
return
default:
fmt.Println("\nerror: invalid command '" + *Command + "'\n")
showUsageMessge()
os.Exit(1)
}
if err != nil {
fmt.Println("err", err)
os.Exit(1)
} else {
fmt.Println("success:", *Command+"\n")
fmt.Println("updated version info")
version, dirty, err := m.Version()
showVersionInfo(version, dirty, err)
}
}
func showUsageMessge() {
fmt.Println("-------------------------------------")
fmt.Println("Usage")
fmt.Println(" go run migrate.go -exec <command>\n")
fmt.Println("Available Exec Commands: ")
for available_command, detail := range AvailableExecCommands {
fmt.Println(" " + available_command + " : " + detail)
}
fmt.Println("-------------------------------------")
}
func showVersionInfo(version uint, dirty bool, err error) {
fmt.Println("-------------------")
fmt.Println("version : ", version)
fmt.Println("dirty : ", dirty)
fmt.Println("error : ", err)
fmt.Println("-------------------")
}
总之,只要使用’up’和’down’,如果需要的话可以使用’force’,就可以将代码设定为如上所示的形式。
往上
% go run migrate.go -exec up
向下
% go run migrate.go -exec down
– 强制选项
% go run migrate.go -exec up -f
项目结构
% tree
.
├── create_sql.sh
├── go.mod
├── go.sum
├── migrate.go
└── sql/
因为不知道什么是最佳实践,所以希望今后在开发过程中寻找答案。