GoFr 如何用 gofr migrate create 生成带时间戳的数据库迁移模板与自动注册表
【免费下载链接】gofrAn opinionated GoLang framework for accelerated microservice development. Built in support for databases and observability.项目地址: https://gitcode.com/GitHub_Trending/go/gofr
在一个多人协作的 GoFr 服务里,数据库结构变更需要可追踪、可重放:每个迁移要有明确的版本,且同一批迁移不能在不同环境里重复执行。GoFr CLI 的gofr migrate create命令可以一步完成两件重复工作——在migrations目录生成带时间戳前缀的迁移模板文件,并自动维护一个登记所有迁移的all.go注册表。本文按「安装 CLI → 生成迁移 → 填充模板 → 接入 main.go → 运行验证」的路径走一遍完整流程。
准备条件
根据 GoFr CLI 参考文档,使用 GoFr CLI 需要:
- Go 1.25 或更高版本,可用以下命令检查:
go version- 安装 GoFr CLI(仅作用于本机 Go 环境,下载并安装
gofr命令):
go install gofr.dev/cli/gofr@latest安装后用gofr version确认命令可用。
另外,迁移最终要作用到一个真实数据源上。以 MySQL 为例,可参考 connecting-mysql 文档 启动本地数据库并配置configs/.env:
docker run --name gofr-mysql -e MYSQL_ROOT_PASSWORD=root123 -e MYSQL_DATABASE=test_db -p 3306:3306 -d mysql:8.0.30# configs/.env APP_NAME=test-service HTTP_PORT=8000 DB_HOST=localhost DB_USER=root DB_PASSWORD=root123 DB_NAME=test_db DB_PORT=3306 DB_DIALECT=mysql DB_CHARSET=utf8 #(optional)GoFr 的数据迁移支持 MySQL、Postgres、Redis、ClickHouse 与 Cassandra,d.SQL覆盖 MySQL 和 PostgreSQL,migration.Datasource同时包含 Redis 等其他数据源。
生成迁移模板与 all.go 注册表
在包含go.mod的项目根目录下执行:
gofr migrate create -name=create_employee_table-name参数是迁移名,生成文件时会作为时间戳之后的后缀。根据 gofr migrate create 参考文档,这条命令会生成一个迁移目录,其中包含两类文件:
- 一个带时间戳前缀的新迁移文件(文档示例为
20250127152047_create_employee_table.go,实际前缀取决于执行命令的时刻),内容为预定义的模板:
package migrations import ( "gofr.dev/pkg/gofr/migration" ) func create_employee_table() migration.Migrate { return migration.Migrate{ UP: func(d migration.Datasource) error { // write your migrations here return nil }, } }- 一个自动生成的
all.go,作为所有迁移的注册表:
// This is auto-generated file using 'gofr migrate' tool. DO NOT EDIT. package migrations import ( "gofr.dev/pkg/gofr/migration" ) func All() map[int64]migration.Migrate { return map[int64]migration.Migrate { 20250127152047: create_employee_table(), } }关于命名,handling-data-migrations 文档建议在项目根目录维护一个migrations目录,并且每个迁移文件按创建时间的YYYYMMDDHHMMSS格式编号——这正是gofr migrate create自动生成的时间戳前缀。该格式可以防止编号冲突,并保证迁移在不同文件系统上排序一致。
需要特别注意:all.go头部标注了DO NOT EDIT,它是gofr migrate工具自动维护的。每次执行gofr migrate create时,新迁移会以「时间戳 → 迁移函数」的形式追加进All()返回的 map,你不需要手动改这个文件。
在模板中编写迁移逻辑
模板里的UP函数接收一个migration.Datasource,按文档给出的 SQL 示例填充:在 handling-data-migrations 文档中,文件名20240226153000_create_employee_table.go对应如下实现:
package migrations import "gofr.dev/pkg/gofr/migration" const createTable = `CREATE TABLE IF NOT EXISTS employee ( id int not null primary key, name varchar(50) not null, gender varchar(6) not null, contact_number varchar(10) not null );` func createEmployeeTable() migration.Migrate { return migration.Migrate{ UP: func(d migration.Datasource) error { _, err := d.SQL.Exec(createTable) if err != nil { return err } return nil }, } }几条来自文档的约束:
- 所有迁移都在事务内执行;
- 使用 MySQL 时,DDL 语句会隐式提交,因此 DDL 中要用
IF EXISTS/IF NOT EXISTS保证可重放性; - 目前只支持
UP方法,没有回滚迁移。
文档还建议按**功能(feature)**而不是按单张表或单条操作来组织迁移:同一个功能涉及的建表、加列等操作应放进同一个迁移函数里,这样回滚一个功能只需回滚一条迁移记录。仓库中的 examples/using-migrations/migrations/all.go 展示了多个迁移共存的注册表形态:
func All() map[int64]migration.Migrate { return map[int64]migration.Migrate { 1722507126: createTableEmployee(), 1722507180: addEmployeeInRedis(), } }迁移按该 map 中 key 的升序执行,所以时间戳即执行顺序。
在 main.go 中接入迁移
按文档的初始化方式,把注册表交给 GoFr 应用:
package main import ( "gofr.dev/examples/using-migrations/migrations" "gofr.dev/pkg/gofr" ) func main() { // Create a new application a := gofr.New() // Add migrations to run a.Migrate(migrations.All()) // Run the application a.Run() }其中migrations包的导入路径要换成你自己项目中migrations目录对应的模块路径。a.Migrate(...)之后 GoFr 在启动阶段执行迁移,a.Run()之前迁移已经完成。
运行并验证迁移是否成功
以仓库中的 using-migrations 示例 为参照,先启动 MySQL(该示例用MYSQL_ROOT_PASSWORD=password、数据库test、端口映射 2001:3306 的镜像),然后在示例目录下执行:
go run main.go运行后,文档给出的成功日志形如(GoFr 默认向 stdout 输出结构化 JSON):
{"level":"INFO","time":"2024-02-26T16:55:46.123456789+05:30","message":"Migration 20240226153000 ran successfully","gofrVersion":"v1.56.4"}以上为文档示例输出,time和gofrVersion字段值会随实际运行环境变化,判断成功的依据是出现ran successfully的迁移消息。
GoFr 还会在数据库内部维护迁移记录,用于追踪哪些迁移已经执行过、确保从未运行过的迁移才执行:
- SQL(MySQL/PostgreSQL):记录存于
gofr_migrations表,字段为version(bigint)、method(varchar(4))、start_time(timestamp)、duration(bigint)。 - Redis:记录存于名为
gofr_migrations的 Redis Hash,key 是版本号(如20240226153000),value 为包含method、startTime、duration的 JSON。
因此第二次启动同一服务时,已经ran successfully的迁移不会再次执行。
限制与注意事项
all.go由gofr migrate工具自动维护,不要手工编辑;新增迁移一律通过gofr migrate create -name=<migration-name>生成。- 迁移只支持
UP方向,文档明确说明"only method UP is supported",不存在框架级的 DOWN 回滚。 migration.Datasource的SQL面向 MySQL/PostgreSQL;Redis、PubSub、Clickhouse、Cassandra、Elasticsearch等数据源的迁移写法不同,且 Cassandra 不保证单条 DML 的原子性(需要时用NewBatch/BatchQuery/ExecuteBatch做批量操作)。- 多实例部署时 GoFr 会用分布式锁自动协调迁移(SQL 用
gofr_migration_locks表,Redis 用SETNX,锁 TTL 15 秒),无需改代码;单实例部署行为不变。 - Redis Streams 模式下使用 PubSub 迁移,订阅要求配置 Consumer Group ID,缺失会导致订阅报错。
下一步
- 迁移的完整写法(多数据源、PubSub、按功能组织)见 handling-data-migrations 文档;
- CLI 其他子命令(
gofr init、gofr store、gofr wrap grpc)见 GoFr CLI 参考; - 可运行的迁移示例代码见 examples/using-migrations/main.go 与 examples/using-migrations/migrations/1722507126_create_employee_table.go。
【免费下载链接】gofrAn opinionated GoLang framework for accelerated microservice development. Built in support for databases and observability.项目地址: https://gitcode.com/GitHub_Trending/go/gofr
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考