Revel 无内置迁移命令,需用 golang-migrate 等工具手动管理:按序号命名 .up.sql/.down.sql 文件,置于 migrations/ 目录;启动时通过 m.Version() 判断版本再执行 m.Up(),禁用并发调用;GORM 的 AutoMigrate 仅用于本地验证,生产环境必须手写 SQL 迁移文件。

Revel 没有内置迁移生成命令,必须靠外部工具补位
Revel 本身不提供类似 php artisan make:migration 或 go run migrate create 的命令来生成迁移脚本。它的设计哲学是“专注路由、控制器与模板”,数据库迁移被明确划归为基础设施层职责。你不会在 revel help 里看到任何 migration 相关子命令,硬写一个 revel generate migration 会失败——框架压根没注册这个动作。
这意味着:想让 Revel 项目具备可版本化的数据库变更能力,必须引入第三方迁移工具,并手动对接到 Revel 启动流程中。常见组合是 golang-migrate + GORM(或 sqlx),而不是依赖 Revel 自身扩展。
用 golang-migrate 生成并管理迁移文件的实操要点
生成迁移文件本身不依赖 Revel,而是靠 migrate CLI 工具完成。关键在于命名规范和路径组织,否则 Revel 启动时无法识别或加载:
- 迁移文件必须严格按序号+名称+.up.sql/.down.sql 命名,例如
001_create_users_table.up.sql和001_create_users_table.down.sql;序号决定执行顺序,不能跳号也不能重复 - 文件建议放在项目根目录下的
migrations/文件夹(非 Revel 固定路径,但需在代码中显式指定) - CLI 生成命令示例:
migrate create -ext sql -dir migrations -seq create_users_table,它会自动创建两个文件,你只需填入 SQL 内容 -
.up.sql中只写 DDL/DML 变更语句(如CREATE TABLE、ALTER COLUMN),不要加BEGIN/COMMIT——golang-migrate会自动包裹事务 -
.down.sql必须幂等:用DROP TABLE IF EXISTS users;而非裸DROP TABLE users;,避免重复回滚时报错
在 Revel 启动时安全触发迁移,避开并发与重复执行
很多人把 m.Up() 直接塞进 app/init.go 的 Init() 函数末尾,结果服务一重启就报 dirty database version。根本问题在于:迁移不是“每次启动都该跑”,而是“只在数据库版本落后时才应执行”。
正确做法是先查版本再决策:
- 调用
m.Version()获取当前已应用的最新迁移版本号(若库为空则 panic,需提前建好migrations表) - 用
m.Force(int64)在首次部署时将版本设为 0(对应基线状态),避免从空库直接Up()失败 - 生产环境慎用
m.Down():它不保证数据安全,尤其含DELETE或TRUNCATE时;更推荐用m.Force(targetVersion)手动对齐版本,再人工验证 - 绝对不要在 HTTP handler 里调
m.Up()—— 并发请求可能同时触发迁移,导致锁表或重复执行
GORM 场景下别混淆“模型定义”和“迁移脚本”
如果你用 GORM(尤其是 v2)做 ORM 层,容易误以为 db.AutoMigrate(&User{}) 就是迁移——它不是。这只是一次性同步结构,不记录版本、不可回滚、不生成 SQL 文件,也不适配团队协作场景。
真实项目中,这两者要严格分离:
-
AutoMigrate仅用于本地快速验证或测试环境冷启动,绝不能出现在生产启动逻辑中 - 正式迁移必须走
golang-migrate的.up.sql文件,内容应与 GORM 模型最终状态一致(比如字段类型、NOT NULL、索引),但由人审阅确认,而非自动生成 - 外键、唯一约束、部分索引等复杂结构,
AutoMigrate常常生成不准确的 SQL,必须手工写进迁移文件
最易被忽略的一点:GORM 的 TableName() 返回值、字段标签(如 gorm:"type:jsonb")会影响你手写迁移 SQL 的细节,但这些信息不会自动导出为 SQL —— 你得自己对照着写,漏一个 IF NOT EXISTS 或拼错字段名,上线就挂。


















