Go 数据库教程:从零构建 DB Migration 工具

Go

可以只使用 Go 标准库来编写后端应用。只不过,会缺乏某些框架特性(支持),若有这方面的需求,您得自己构建它。

其中一个特性就是数据库迁移。本文将向您展示创建一个简单的数据库迁移工具。让我们开始吧。

当然,类似 Go 工具库 go-migrategoose 可帮助您进行数据库迁移。 但是,它使用原始.sql 文件进行迁移,这需要复制一堆.sql 文件以及二进制文件。

第一步:连接数据库#

作为第一步,我们编写一连接数据库返回连接的函数。

./app/db.go

package app

import (
    "database/sql"
    "fmt"

        _ "github.com/go-sql-driver/mysql"
)

// NewDB .
func NewDB() *sql.DB {
    fmt.Println("Connecting to MySQL database...")

    db, err := sql.Open("mysql", "root:welcome@tcp(127.0.0.1:3306)/migrationtest")
    if err != nil {
        fmt.Println("Unable to connect to database", err.Error())
        return nil
    }

    if err := db.Ping(); err != nil {
        fmt.Println("Unable to connect to database", err.Error())
        return nil
    }

    fmt.Println("Database connected!")

    return db
}

参考阅读: 在 Golang 中将数据库连接传递到控制器的不同方法 *

第二步:定义迁移和迁移器#

我们先定义一个 struct 。

一个迁移文件有 version 以及 Up 函数 、 Down 函数,还有 done。 version 是版本号, Up 函数用来执行迁移, Down 函数用来回滚迁移, done 用来表示迁移是否已经执行过了。

./migrations/migrator.go

package migrations

type Migration struct {
    Version string
    Up      func(*sql.Tx) error
    Down    func(*sql.Tx) error

    done bool
}

下一步,我们将定一个迁移器。我们创建一个全局的 type Migrator

迁移器中,定义了数据库的引用,版本号的数组,还有迁移文件的 map。

./migrations/migrator.go

package migrations

// Code removed for brevity

type Migrator struct {
    db         *sql.DB
    Versions   []string
    Migrations map[string]*Migration
}

var migrator = &Migrator{
    Versions:   []string{},
    Migrations: map[string]*Migration{},
}`

第三步:生成新迁移#

在创建迁移之前,我们先在迁移器中增加 AddMigration 方法。该方法用来创建新的迁移。

./migrations/migrator.go

package migrations

// Code removed for brevity

func (m *Migrator) AddMigration(mg *Migration) {
    // Add the migration to the hash with version as key
    m.Migrations[mg.Version] = mg

    // Insert version into versions array using insertion sort
    index := 0
    for index < len(m.Versions) {
        if m.Versions[index] > mg.Version {
            break
        }
        index++
    }

    m.Versions = append(m.Versions, mg.Version)
    copy(m.Versions[index+1:], m.Versions[index:])
    m.Versions[index] = mg.Version
}

我们将基于模板生成新的迁移文件。这个模板文件里有 *Migration 指针 和 init() 方法,使用 AddMigration 方法将迁移数据推送到迁移器里。、

./migrations/template.txt

package migrations

import "database/sql"

func init() {
    migrator.AddMigration(&Migration{
        Version: "{{.Version}}",
        Up:      mig_{{.Version}}_{{.Name}}_up,
        Down:    mig_{{.Version}}_{{.Name}}_down,
    })
}

func mig_{{.Version}}_{{.Name}}_up(tx *sql.Tx) error {
    return nil
}

func mig_{{.Version}}_{{.Name}}_down(tx *sql.Tx) error {
    return nil
}

如果我们有了这个模板文件,就可以编写一个函数来执行创建新的文件。

./migrations/migrator.go

package migrations 

// Code removed for brevity

func Create(name string) error {
    version := time.Now().Format("20060102150405")

    in := struct {
        Version string
        Name    string
    }{
        Version: version,
        Name:    name,
    }

    var out bytes.Buffer

    t := template.Must(template.ParseFiles("./migrations/template.txt"))
    err := t.Execute(&out, in)
    if err != nil {
        return errors.New("Unable to execute template:" + err.Error())
    }

    f, err := os.Create(fmt.Sprintf("./migrations/%s_%s.go", version, name))
    if err != nil {
        return errors.New("Unable to create migration file:" + err.Error())
    }
    defer f.Close()

    if _, err := f.WriteString(out.String()); err != nil {
        return errors.New("Unable to write to migration file:" + err.Error())
    }

    fmt.Println("Generated new migration files...", f.Name())
    return nil
}

这些就是生成新我迁移文件所需要的。

第四步:存储和检查迁移状态#

我们需要一个方法来找出哪些迁移文件被执行了,哪些没有。

为了实现这个,我们创建一个 schema_migrations 表,这个表只有一个字段 version。执行 Up 迁移时,会将该迁移的版本号插入到这个表中,执行 Down 回滚时,会将该迁移的版本号从这个表删除。

当初始化迁移时,我们可以读取这个表,并将迁移文件中的 done 标记一下。

./migrations/migrator.go

代码已被折叠,点此展开

go run main.go migrate create -n migration_name
go run main.go migrate status
go run main.go migrate up [-s 2]
go run main.go migrate down [-s 2]


We will use Cobra for creating the CLI. Go ahead and install it.

go get -u github.com/spf13/cobra/cobra

代码已被折叠,点此展开

go run main.go migrate create -n init_schema


Let us open the newly created migration file and write our schema migration queries to create a new table `users` with one column `name`.

./migrations/20200830120717_init_schema.go

`package migrations

import "database/sql"

func init() {
    migrator.AddMigration(&Migration{
        Version: "20200830120717",
        Up:      mig_20200830120717_init_schema_up,
        Down:    mig_20200830120717_init_schema_down,
    })
}

func mig_20200830120717_init_schema_up(tx *sql.Tx) error {
    _, err := tx.Exec("CREATE TABLE users ( name varchar(255) );")
    if err != nil {
        return err
    }
    return nil
}

func mig_20200830120717_init_schema_down(tx *sql.Tx) error {
    _, err := tx.Exec("DROP TABLE users")
    if err != nil {
        return err
    }
    return nil
}`

We can now try and execute the migration by running the following command.

go run main.go migrate up


If you see output like the following, the migration has finished successfully.

![running db migrations in go](https://cdn.learnku.com/uploads/images/202009/18/1/8fyXHv51Kx.png!large)

We can check our database to see if the schema changes were applied as expected.

![database output showing db migration in go](https://cdn.learnku.com/uploads/images/202009/18/1/3AF6oEzyvk.png!large)

We can check the status of migrations by running the following command.

go run main.go migrate status


And that should produce an output like the following.

![output of migrate status command](https://cdn.learnku.com/uploads/images/202009/18/1/Z9Rps5Ipet.png!large)

Finally, let us try reverting the schema changes by running the `down` command.

go run main.go migrate down
```

If you see an output like the following, the migration was reverted successfully.

output of migration down command

Now, if you check the database you can see the table is dropped and the schema_migration table is empty again.

database reflecting schema changes

Awesome! Everything seems to be working fine. That gives us our very own tool to do DB migrations in Go that will allow us to write migrations in a .go file.

You can see the full source code here: github.com/praveen001/go-db-migration

本文中的所有译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。

原文地址:https://techinscribed.com/create-db-migr...

译文地址:https://learnku.com/go/t/51228

本文为协同翻译文章,如您发现瑕疵请点击「改进」按钮提交优化建议
讨论数量: 2
朕略显ぼうっと萌

抓取的数据中代码块不对

4年前 评论

file

这个可以噢。虽然没 laravel 的好用。作者实现的也类似。

不过 laravel 的迁移,模型,orm 真的太好用。

4年前 评论