跳到主要内容

附录:程序化迁移规划

在前面的章节中,我们了解了如何使用 Atlas CLI 生成迁移文件。然而,我们也可以通过编程方式生成这些文件。本章节将介绍如何编写用于自动规划迁移文件的 Go 代码。

1. 启用版本化迁移功能标志

示例仓库

本节描述的变更可在示例仓库的 PR #2 中查看。

第一步是通过传入 sql/versioned-migration 功能标志来启用版本化迁移功能。根据执行 Ent 代码生成器的不同方式,您需要选择以下两种方案之一:

如果使用默认的 go generate 配置,只需在 ent/generate.go 文件中添加 --feature sql/versioned-migration 参数:

package ent

//go:generate go run -mod=mod entgo.io/ent/cmd/ent generate --feature sql/versioned-migration ./schema

接下来,重新运行代码生成:

go generate ./...

运行代码生成后,您会看到以下 新增的方法 出现在 ent/migrate/migrate.go 中:

  • Diff
  • NamedDiff

这些方法用于比较从数据库连接或迁移目录读取的状态与 Ent 架构定义的状态。

2. 自动迁移规划脚本

示例仓库

本节描述的变更可在示例仓库的 PR #4 中查看。

开发数据库

为了能够规划准确且一致的迁移文件,Atlas 引入了 开发数据库 的概念,这是一个用于模拟数据库在不同变更后状态的临时数据库。 因此,要使用 Atlas 自动规划迁移,我们需要向迁移规划脚本提供此类数据库的连接字符串。 此类数据库通常使用本地 Docker 容器创建。现在运行以下命令启动一个:

docker run --rm --name atlas-db-dev -d -p 3306:3306 -e MYSQL_DATABASE=dev -e MYSQL_ROOT_PASSWORD=pass mysql:8

使用刚刚配置的开发数据库,我们可以编写一个使用 Atlas 为我们规划迁移文件的脚本。在项目的 ent/migrate 目录下创建一个名为 main.go 的新文件:

ent/migrate/main.go
//go:build ignore

package main

import (
"context"
"log"
"os"

"<project>/ent/migrate"

atlas "ariga.io/atlas/sql/migrate"
"entgo.io/ent/dialect"
"entgo.io/ent/dialect/sql/schema"
_ "github.com/go-sql-driver/mysql"
)

const (
dir = "ent/migrate/migrations"
)

func main() {
ctx := context.Background()
// 创建能够理解 Atlas 迁移文件格式以进行重放的本地迁移目录。
if err := os.MkdirAll(dir, 0755); err != nil {
log.Fatalf("creating migration directory: %v", err)
}
dir, err := atlas.NewLocalDir(dir)
if err != nil {
log.Fatalf("failed creating atlas migration directory: %v", err)
}
// 迁移差异选项。
opts := []schema.MigrateOption{
schema.WithDir(dir), // 提供迁移目录
schema.WithMigrationMode(schema.ModeReplay), // 提供迁移模式
schema.WithDialect(dialect.MySQL), // 使用的 Ent 方言
schema.WithFormatter(atlas.DefaultFormatter),
}
if len(os.Args) != 2 {
log.Fatalln("migration name is required. Use: 'go run -mod=mod ent/migrate/main.go <name>'")
}
// 使用 Atlas 对 MySQL 的支持生成迁移(注意上面传递的 Ent 方言选项)。
err = migrate.NamedDiff(ctx, "mysql://root:pass@localhost:3306/dev", os.Args[1], opts...)
if err != nil {
log.Fatalf("failed generating migration file: %v", err)
}
}
信息

请注意,您需要修改上面代码中高亮显示的部分。编辑 migrate 包的导入路径以匹配您的项目,并提供开发数据库的连接字符串。

要运行此脚本,首先在项目的 ent/migrate 目录下创建 migrations 目录:

mkdir ent/migrate/migrations

然后运行脚本为项目创建初始迁移文件:

go run -mod=mod ent/migrate/main.go initial

请注意,此处的 initial 只是迁移文件的标签。您可以使用任意名称。

运行脚本后,可以观察到 ent/migrate/migrations 目录中创建了两个新文件。第一个文件名为 atlas.sum,这是 Atlas 用于确保迁移历史线性一致的校验文件:

ent/migrate/migrations/atlas.sum
h1:Dt6N5dIebSto365ZEyIqiBKDqp4INvd7xijLIokqWqA=
20221114165732_initialize.sql h1:/33+7ubMlxuTkW6Ry55HeGEZQ58JqrzaAl2x1TmUTdE=

第二个文件是实际的迁移文件,其命名基于我们传递给脚本的标签:

ent/migrate/migrations/20221114165732_initial.sql
-- create "users" table
CREATE TABLE `users` (`id` bigint NOT NULL AUTO_INCREMENT, `name` varchar(255) NOT NULL, `email` varchar(255) NOT NULL, PRIMARY KEY (`id`), UNIQUE INDEX `email` (`email`)) CHARSET utf8mb4 COLLATE utf8mb4_bin;
-- create "blogs" table
CREATE TABLE `blogs` (`id` bigint NOT NULL AUTO_INCREMENT, `title` varchar(255) NOT NULL, `body` longtext NOT NULL, `created_at` timestamp NOT NULL, `user_blog_posts` bigint NULL, PRIMARY KEY (`id`), CONSTRAINT `blogs_users_blog_posts` FOREIGN KEY (`user_blog_posts`) REFERENCES `users` (`id`) ON DELETE SET NULL) CHARSET utf8mb4 COLLATE utf8mb4_bin;

其他迁移工具

Atlas 与 Ent 集成得非常好,但它并不是 Ent 项目中唯一可用于管理数据库架构的迁移工具。以下是其他受支持的迁移工具列表:

要了解更多关于如何在 Ent 中使用这些工具的信息,请参阅相关的文档