news 2026/9/24 22:45:58

SeaORM Poem 示例迁移指南:使用 Migrator CLI 管理数据库 Schema

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
SeaORM Poem 示例迁移指南:使用 Migrator CLI 管理数据库 Schema
  • 后端
  • 数据库
  • ORM

【免费下载链接】sea-orm

🐚 A powerful relational ORM for Rust

项目地址:https://gitcode.com/gh_mirrors/se/sea-orm
点击查看免费下载

本篇技术指南基于 sea-orm 仓库中的 examples/poem_example 示例项目,完整讲解 SeaORM 迁移(Migration)子系统在真实 Web 应用中的落地方式:如何通过 Migrator CLI 应用、回滚、重置与查询迁移状态,并结合 Poem 示例的迁移模块源码 深入剖析up/down/fresh/refresh/reset/status六类命令的底层执行逻辑与调用链。读完本文,你将掌握 SeaORM 迁移的 CLI 操作全流程,并能看懂迁移代码的结构与程序内自动迁移的触发机制。

关联文档与仓库定位

本指南的核心文档是 examples/poem_example/migration/README.md,它是一份浓缩的 Migrator CLI 操作速查表,罗列了迁移生命周期中最常用的七个命令场景。该文档所在的migrationcrate 是 Poem Web 示例(一个基于 Tera 模板的博客应用)的数据库迁移模块,与其平级的还有api(Poem 服务端)、entity(实体定义)两个 crate,共同组成一个完整工作区,定义见 examples/poem_example/Cargo.toml。

需要说明的是,SeaORM 迁移 CLI 的行为由sea-orm-migrationcrate 统一提供,因此本文讲解的命令与原理同样适用于仓库中其他示例(如 axum_example/migration、rocket_example/migration 等),下文将以 Poem 示例为具体载体展开。

迁移模块的项目结构

在动手运行命令之前,先看清迁移模块的组成。Poem 示例的迁移 crate 包含 4 个关键文件:

examples/poem_example/migration/ ├── Cargo.toml # 依赖声明与 feature 配置 └── src/ ├── lib.rs # Migrator 聚合器:注册全部迁移 ├── main.rs # CLI 入口:调用 cli::run_cli ├── m20220120_000001_create_post_table.rs # 迁移 1:创建 post 表 └── m20220120_000002_seed_posts.rs # 迁移 2:写入种子数据

其中 src/lib.rs 是整个迁移体系的枢纽:它通过MigratorTrait实现Migrator结构体,在migrations()方法中按顺序返回迁移列表:

pub struct Migrator; #[async_trait::async_trait] impl MigratorTrait for Migrator { fn migrations() -> Vec<Box<dyn MigrationTrait>> { vec![ Box::new(m20220120_000001_create_post_table::Migration), Box::new(m20220120_000002_seed_posts::Migration), ] } }

而 src/main.rs 则极为精简——全部 CLI 逻辑都委托给sea-orm-migration提供的cli::run_cli

use sea_orm_migration::prelude::*; #[tokio::main] async fn main() { cli::run_cli(migration::Migrator).await; }

这意味着:只要你的迁移 crate 实现了MigratorTrait并像上面这样编写入口,就能直接获得一整套迁移命令行工具,无需自己解析参数。

环境准备:数据库连接与依赖配置

Migrator CLI 通过环境变量读取数据库连接信息。从 sea-orm-migration/src/cli.rs 的Cli结构体定义可以看出,CLI 支持以下全局参数:

参数环境变量说明
-u,--database-urlDATABASE_URL数据库连接 URL,必填;未设置时会报错Environment variable 'DATABASE_URL' not set
-s,--database-schemaDATABASE_SCHEMA数据库 schema 名;PostgreSQL 下可选、默认public,MySQL 与 SQLite 下被忽略
-v,--verbose输出 debug 级别的日志

在运行迁移命令前,通常先在工作区根目录创建.env文件写入DATABASE_URL。CLI 启动时会通过dotenv().ok()自动加载.env(见 cli.rs 的run_cli_with_connection函数)。Poem 示例默认使用 SQLite,因此典型的配置形如:

DATABASE_URL=sqlite://posts.db?mode=rwc

?mode=rwc表示读写并在不存在时自动创建数据库文件。若使用 PostgreSQL,还需在DATABASE_SCHEMA中指定 schema(默认public)。

依赖配置上,migration/Cargo.toml 给出了两个关键点:

[dependencies.sea-orm-migration] features = [ # Enable following runtime and db backend features if you want to run migration via CLI "runtime-tokio-native-tls", "sqlx-sqlite", ] path = "../../../sea-orm-migration" # remove this line in your own project version = "~2.0.3" # sea-orm-migration version
  • 运行时与数据库后端 feature 必须匹配你的目标数据库:注释明确提示,若要经由 CLI 运行迁移,需要启用对应的 runtime(如runtime-tokio-native-tls)与 db backend(如sqlx-sqlitesqlx-postgres)feature;
  • path指向的是本仓库内的 crate:在独立项目中应删除这一行,仅保留version
  • 此外还声明了对entitytokio的依赖,后者为 CLI 的异步运行时提供支持。

Migrator CLI 命令速查:完整继承原文档

原 README 文档给出的全部命令如下,每一条均可在迁移模块目录下直接运行:

应用全部待执行的迁移

cargo run
cargo run -- up

应用前 10 个待执行的迁移

cargo run -- up -n 10

回滚最后应用的迁移

cargo run -- down

回滚最后 10 个已应用的迁移

cargo run -- down -n 10

删除数据库中的全部表,然后重新应用所有迁移

cargo run -- fresh

回滚所有已应用的迁移,然后重新应用所有迁移

cargo run -- refresh

回滚所有已应用的迁移

cargo run -- reset

检查所有迁移的状态

cargo run -- status

关于-n参数需要补充两点语义:其一,up -n 10中的-n意为 "number of migrations to be applied",即本次最多应用 10 个待执行迁移;其二,down -n 10表示回滚最近应用的 10 个迁移。从 cli.rs 的run_migrate_inner可以看到,down分支会把numSome(num)传入,而up分支(默认分支)在无子命令时以None表示“应用全部”。

命令底层的执行逻辑与调用链

上述每个命令最终都落在MigratorTrait的方法上。对照 cli.rs 中run_migrate_inner的匹配逻辑:

match command { Some(MigrateSubcommands::Fresh) => migrator.fresh(db).await?, Some(MigrateSubcommands::Refresh) => migrator.refresh(db).await?, Some(MigrateSubcommands::Reset) => migrator.reset(db).await?, Some(MigrateSubcommands::Status) => migrator.status(db).await?, Some(MigrateSubcommands::Up { num }) => migrator.up(db, num).await?, Some(MigrateSubcommands::Down { num }) => migrator.down(db, Some(num)).await?, _ => migrator.up(db, None).await?, }

各命令的语义差异可以这样理解:

命令底层方法行为
cargo run(无参数)/upMigratorTrait::up按注册顺序应用所有未执行的迁移;每次迁移调用其up()方法
up -n 10MigratorTrait::up(num=10)只应用前 10 个待执行迁移
down/down -n 10MigratorTrait::down逆序回滚最近应用的迁移,逐个调用迁移的down()方法
freshMigratorTrait::fresh先删除库中全部表(等价于重置到空库),再应用全部迁移
refreshMigratorTrait::refresh先回滚全部已应用迁移,再重新应用全部迁移
resetMigratorTrait::reset仅回滚全部已应用迁移,不重新应用
statusMigratorTrait::status列出每个迁移的待执行/已应用状态,不修改数据库

其中freshrefreshreset三者的差别值得重点记忆:fresh走的是“删表”路线,refresh走的是“回滚再应用”路线,而reset只回滚不重建——日常开发中refresh是最常用的“重建数据库”方式,因为它能同时执行迁移的downup两个方向的代码,可更完整地验证迁移的可逆性。

此外,CLI 还支持两个无需数据库连接的子命令(见run_non_db_command):init(初始化迁移目录)与generate(生成新的迁移文件模板),它们在 Poem 示例中未直接使用,但在用sea-orm-cli从零搭建项目时非常有用。

从源码看迁移如何被执行

以 Poem 示例的第一个迁移 m20220120_000001_create_post_table.rs 为例,它展示了迁移文件的标准骨架:

#[derive(DeriveMigrationName)] pub struct Migration; #[async_trait::async_trait] impl MigrationTrait for Migration { async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { manager .create_table( Table::create() .table("post") .if_not_exists() .col(pk_auto("id")) .col(string("title")) .col(string("text")) .to_owned(), ) .await } async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { manager .drop_table(Table::drop().table("post").to_owned()) .await } }

要点拆解:

  • #[derive(DeriveMigrationName)]自动根据类型名Migration派生迁移名,与文件前缀的m20220120_000001时间戳配合,形成全局唯一、按时间排序的迁移标识;
  • up()使用sea_orm_migration::schema提供的辅助函数(如pk_autostring)配合Table::create()构建建表语句,if_not_exists()保证重复执行安全;
  • down()是对称的撤销操作,调用drop_table删除post表。fresh/refresh/reset等命令能工作,前提就是每个迁移都实现了可逆的down()

第二个迁移 m20220120_000002_seed_posts.rs 演示了如何在迁移中执行数据操作(种子数据):

let db = manager.get_connection(); let seed_data = vec![ ("First Post", "This is the first post."), ("Second Post", "This is another post."), ]; for (title, text) in seed_data { let model = post::ActiveModel { title: Set(title.to_string()), text: Set(text.to_string()), ..Default::default() }; model.insert(db).await?; }

关键点在于:迁移不仅能改表结构,还能通过manager.get_connection()拿到数据库连接,直接以ActiveModel的方式插入记录——这也是为什么种子数据、字典表初始化等任务可以自然地放进迁移脚本中。对应的down()则通过post::Entity::delete_many().filter(post::Column::Title.is_in(...))按标题精确清理种子数据,保证回滚后数据状态与迁移前一致。

程序内自动迁移:不依赖 CLI 的另一种执行方式

值得注意的一个细节是,Poem 示例的 Web 服务在启动时就会自动执行迁移,而无需手动运行 CLI。见 api/src/lib.rs 的启动流程:

// create post table if not exists let conn = Database::connect(&db_url).await.unwrap(); Migrator::up(&conn, None).await.unwrap();

这段代码在服务启动后直接调用Migrator::up,将所有未应用的迁移应用一遍(迁移表内部保证幂等,已应用的会跳过)。这是一种非常实用的生产部署模式:把迁移与应用启动绑定,省去额外运维步骤。它说明MigratorTrait的方法既可以被 CLI 包装调用,也可以在你的业务代码里直接调用,两条路径共用同一套迁移列表与幂等机制。

因此在实际项目中,你通常有两种选择:

  1. 开发阶段:在迁移模块目录下使用本文速查表中的 CLI 命令(up/down/fresh等),随时调整 Schema;
  2. 部署阶段:像 Poem 示例这样在应用启动时调用Migrator::up(&conn, None),让迁移随服务一起完成。

两者可以共存——CLI 管理的迁移状态表与程序内执行的迁移状态表是同一份,不会互相冲突。

小结

本文以 examples/poem_example/migration/README.md 的命令速查表为骨架,结合 Poem 示例的迁移源码,完整覆盖了 SeaORM Migrator CLI 的七个核心命令场景,并深入到了sea-orm-migration的 CLI 实现层:从DATABASE_URL/DATABASE_SCHEMA的连接配置,到up/down/fresh/refresh/reset/statusMigratorTrait方法的映射关系,再到迁移文件up()/down()的编写范式与程序内自动迁移的用法。掌握这些内容后,无论你是要在新项目中用 SeaORM 搭建 Schema 管理,还是接手现有 sea-orm 工程,都能准确、安全地执行每一次迁移操作。

  • 后端
  • 数据库
  • ORM

【免费下载链接】sea-orm

🐚 A powerful relational ORM for Rust

项目地址:https://gitcode.com/gh_mirrors/se/sea-orm
点击查看免费下载

相关推荐

上一篇:三步彻底卸载Windows系统Microsoft Edge浏览器的专业方案
下一篇:网盘直链下载终极解决方案:一键获取九大网盘真实下载链接

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/24 22:45:43

工厂数字孪生落地实战:从建模到数据联动的性能优化与避坑指南

数字孪生这个概念&#xff0c;这几年在工业圈里被提得太多&#xff0c;但真正落到工厂车间里跑起来、还能跑得稳的&#xff0c;比例其实并不高。我参与过几个智慧工厂的数字孪生项目&#xff0c;从最开始的产线级试点到后来的整厂级平台&#xff0c;踩过的坑基本都集中在两个地…

作者头像 李华
网站建设 2026/9/24 22:45:01

激光深熔焊小孔演化自编程:原理、实现与工程实战

激光深熔焊这个领域&#xff0c;做了这么多年工艺开发&#xff0c;我越来越觉得一个道理&#xff1a;焊得稳不稳&#xff0c;很多时候不取决于你参数表里那几档功率和速度&#xff0c;而是焊接过程中那个看不见摸不着的“小孔”到底在干什么。小孔&#xff08;keyhole&#xff…

作者头像 李华
网站建设 2026/9/24 22:44:39

WorkBuddy、豆包办公都来了,企业AI如何统一纳管?

AI助手进入企业&#xff0c;IT管理变得复杂 随着WorkBuddy、豆包办公、千问办公等AI智能体工具在企业办公场景加速落地&#xff0c;员工效率得到显著提升。与此同时&#xff0c;企业引入这类工具时也普遍会遇到一些治理课题&#xff1a;文件与数据分散、自动化执行边界、网络访…

作者头像 李华
网站建设 2026/9/24 22:43:42

彻底搞懂MyBatis关联映射:association与collection实战与源码剖析

做后端这些年&#xff0c;MyBatis的关联映射翻车现场我见过太多&#xff1a;明明SQL join出来了&#xff0c;结果对象里子集合是空的&#xff1b;加了resultMap之后&#xff0c;分页总数突然对不上了&#xff1b;还有那种日志里疯狂刷同一条SQL的N1问题。这中间最核心的两个标签…

作者头像 李华
网站建设 2026/9/24 22:43:26

YOLO安全帽手套检测数据集:三种格式标签与完整训练指南

简介&#xff1a;面向目标检测初学者与工业安全场景开发者&#xff0c;YOLO安全帽手套检测数据集提供真实场景下5000张高质量图片&#xff0c;覆盖工地、厂区等多种作业环境&#xff0c;并配套VOC、COCO、YOLO三种格式标签&#xff0c;标注框质量高&#xff0c;可直接用于YOLO系…

作者头像 李华
网站建设 2026/9/24 22:43:11

MACE端侧深度学习推理框架架构解析与部署实战

1. 端侧部署为什么这么难——MACE的设计初衷1.1 端侧场景和云端场景完全是两回事干了这些年移动端AI&#xff0c;我最大的感受就是&#xff1a;很多人把端侧部署想得太简单了。在云端&#xff0c;你有一堆GPU、有充足的内存、有无限的电量&#xff0c;最多就是多花点钱的事。但…

作者头像 李华