bee migrate 是手动 SQL 迁移工具,仅执行注册的 Up/Down 函数,不自动生成或校验 SQL;依赖时间戳文件排序和 name 字段去重,需严格匹配注册名与时间戳,SQL 编写须适配数据库方言。

Beego 的 bee migrate 不是 ORM 自动同步工具,它不读模型结构、不生成 SQL、也不支持反向推导;它只按顺序执行你手写的 Up() 和 Down() 函数里的 SQL 语句。用错预期,迁移就会卡住或破坏数据。
bee migrate 命令实际执行逻辑
bee migrate 启动时会扫描 database/migrations/ 目录下所有以时间戳开头的 .go 文件,按文件名升序排序(即时间先后),再比对数据库中 beego_migrations 表(自动创建)里已记录的 name 字段——只执行那些未登记过的迁移。
关键点在于:name 来自迁移文件中 migration.Register("AddAgeToUser_20240515_103022", m) 的第一个参数,不是文件名,也不是结构体名。一旦注册名写错或重复,bee migrate 就会跳过或报错。
- 执行
bee migrate:只运行未登记的Up() - 执行
bee migrate -rollback=1:回滚最近一次成功的Up(),即调用对应迁移的Down() - 不会自动建表、不会校验字段是否存在、不检查 SQL 语法是否兼容当前数据库版本
迁移文件里 SQL 编写要注意什么
Beego 迁移不封装 DDL 抽象层,所有变更都靠原生 SQL 字符串。这意味着你得自己处理方言差异、空值约束、索引冲突等细节。
常见翻车点:
-
m.SQL("ALTER TABLE users ADD COLUMN age INT")在 MySQL 5.7+ 会失败,因为没设默认值且字段非空;应写成ADD COLUMN age INT DEFAULT 0或ADD COLUMN age INT NULL -
Down()里删字段用DROP COLUMN,但 SQLite 不支持该语法,必须用重建表方式——Beego 不做适配,你得自己判断beego.AppConfig.String("db.driver")或分库维护两套迁移 - 添加唯一索引前,先
SELECT COUNT(*) FROM table GROUP BY col HAVING COUNT(*) > 1检查重复值,否则CREATE UNIQUE INDEX直接报错中断
如何安全地做多环境迁移
开发、测试、生产共用同一套迁移文件,但数据库连接配置不同。Beego 不提供环境隔离开关,全靠你控制 conf/app.conf 中的 db.conn 和 db.driver。
实操建议:
- 本地开发用
sqlite3,迁移可快速试错;上线前手动在 MySQL 环境跑一遍相同 SQL,确认无语法/权限问题 - 禁止在
Up()里写INSERT INTO config VALUES (...)这类初始化数据逻辑——迁移应只管结构,数据初始化交给bee run -d启动时的init()或单独脚本 - 上线前务必在预发库执行
bee migrate -dryrun(需自行加日志打印 SQL),观察实际执行顺序和语句内容
bee generate migration 生成后必须改什么
bee generate migration add_age_to_user 生成的文件里,Up() 和 Down() 是空函数体,且结构体名含时间戳(如 AddAgeToUser_20240515_103022)。这本身没问题,但容易忽略两处硬编码点:
-
m.Created = "20240515_103022"必须与文件名前缀完全一致,否则bee migrate找不到对应记录 -
migration.Register("AddAgeToUser_20240515_103022", m)第一个参数必须和m.Created值相同,大小写、下划线都不能差 - 别直接复制粘贴旧迁移改名——时间戳不匹配会导致迁移被跳过,且不会报错,静默失效
最稳妥的做法:每次新增迁移都用 bee generate migration 生成新文件,然后只改 Up()/Down() 里的 SQL,其他字段不动。


















