附录:程序化迁移规划
在前面的章节中,我们了解了如何使用 Atlas CLI 生成迁移文件。然而,我们也可以通过编程方式生成这些文件。本章节将介绍如何编写用于自动规划迁移文件的 Go 代码。
1. 启用版本化迁移功能标志
本节描述的变更可在示例仓库的 PR #2 中查看。
第一步是通过传入 sql/versioned-migration 功能标志来启用版本化迁移功能。根据执行 Ent 代码生成器的不同方式,您需要选择以下两种方案之一:
- 使用 Ent CLI
- 使用 entc 包
如果使用默认的 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
如果使用代码生成包(例如使用像 entgql 这样的 Ent 扩展),请按如下方式添加功能标志:
//go:build ignore
package main
import (
"log"
"entgo.io/ent/entc"
"entgo.io/ent/entc/gen"
)
func main() {
err := entc.Generate("./schema", &gen.Config{
Features: []gen.Feature{gen.FeatureVersionedMigration},
})
if err != nil {
log.Fatalf("running ent codegen: %v", err)
}
}
接下来,重新运行代码生成:
go generate ./...
运行代码生成后,您会看到以下
新增的方法
出现在 ent/migrate/migrate.go 中:
DiffNamedDiff
这些方法用于比较从数据库连接或迁移目录读取的状态与 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 的新文件:
//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 用于确保迁移历史线性一致的校验文件:
h1:Dt6N5dIebSto365ZEyIqiBKDqp4INvd7xijLIokqWqA=
20221114165732_initialize.sql h1:/33+7ubMlxuTkW6Ry55HeGEZQ58JqrzaAl2x1TmUTdE=
第二个文件是实际的迁移文件,其命名基于我们传递给脚本的标签:
-- 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 中使用这些工具的信息,请参阅相关的文档。