简介
大约 4 分钟
Sea-ORM 简介
Sea-ORM 是一款面向 async/await 的 Rust ORM,提供“Entity/Model/ActiveModel”的数据访问范式与迁移工具链。它与 Axum/Tokio 协同良好,适合在祺洛中实现稳定、可维护的数据库层。
你将学到
- 核心概念:Entity(表/列)、Model(只读数据)、ActiveModel(可写数据)
- CRUD、条件查询、分页/排序
- 关联关系与预加载(1-1/1-N/N-N)
- 事务、软删除、事件钩子(ActiveModelBehavior)
- JSON/枚举等类型映射与序列化
支持数据库
- PostgreSQL、MySQL/MariaDB、SQLite
目录结构与安装
依赖(示例):
# Cargo.toml
sea-orm = { version = "0.12", features = ["sqlx-postgres", "runtime-tokio-native-tls", "macros", "with-chrono", "with-json"] }
sea-orm-migration = "0.12" # 迁移(独立 package 更佳)
chrono = { version = "0.4", features = ["serde"] }
serde = { version = "1", features = ["derive"] }
推荐结构:
src/
db/
entities/ # sea-orm-cli 自动生成
mod.rs # 连接与多数据源
migration/ # 迁移 crate 或模块
连接数据库:
// src/db/mod.rs
use sea_orm::{Database, DatabaseConnection};
pub async fn connect(url: &str) -> anyhow::Result<DatabaseConnection> {
Ok(Database::connect(url).await?)
}
Entity/Model/ActiveModel
- Entity:代表一张表(含列定义、主键、关系)
- Model:一行记录的“只读”结构(查询结果)
- ActiveModel:“可写”结构,通过
Set(NotSet)
标记要写入的列
示例(用户表简化):
// src/db/entities/sys_user.rs(由 cli 生成,示意)
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, serde::Serialize, serde::Deserialize)]
#[sea_orm(table_name = "sys_user")]
pub struct Model {
#[sea_orm(primary_key)]
pub id: i64,
pub username: String,
pub password_hash: String,
pub real_name: Option<String>,
pub dept_id: Option<i64>,
pub status: i32,
pub created_at: DateTimeUtc,
pub updated_at: DateTimeUtc,
pub deleted_at: Option<DateTimeUtc>, // 软删除(可选)
}
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
pub enum Relation {
#[sea_orm(belongs_to = "super::sys_dept::Entity", from = "Column::DeptId", to = "super::sys_dept::Column::Id")]
Dept,
}
impl ActiveModelBehavior for ActiveModel {
// 可选:before_save/after_save 钩子(自动填充时间戳等)
}
生成实体(CLI)
安装:
cargo install sea-orm-cli
从现有数据库逆向生成实体:
sea-orm-cli generate entity \
--database-url postgres://user:pass@127.0.0.1:5432/qiluo \
-o src/db/entities
注意
- 逆向生成后可手工补充 Relation/派生 Serialize/Deserialize/Behavior
- 日常维护建议优先“迁移驱动”,再由 CLI 同步实体
迁移(Migration)
创建迁移:
sea-orm-cli migrate init
sea-orm-cli migrate generate create_sys_user
迁移示例(简化):
// migration/src/m20240101_000001_create_sys_user.rs
use sea_orm_migration::prelude::*;
#[derive(DeriveMigrationName)]
pub struct Migration;
#[async_trait::async_trait]
impl MigrationTrait for Migration {
async fn up(&self, m: &SchemaManager) -> Result<(), DbErr> {
m.create_table(
Table::create()
.table(SysUser::Table)
.if_not_exists()
.col(ColumnDef::new(SysUser::Id).big_integer().not_null().primary_key())
.col(ColumnDef::new(SysUser::Username).string_len(50).not_null().unique_key())
.col(ColumnDef::new(SysUser::PasswordHash).string_len(255).not_null())
.col(ColumnDef::new(SysUser::RealName).string_len(50))
.col(ColumnDef::new(SysUser::DeptId).big_integer())
.col(ColumnDef::new(SysUser::Status).integer().not_null().default(1))
.col(ColumnDef::new(SysUser::CreatedAt).timestamp_with_time_zone().not_null())
.col(ColumnDef::new(SysUser::UpdatedAt).timestamp_with_time_zone().not_null())
.col(ColumnDef::new(SysUser::DeletedAt).timestamp_with_time_zone())
.to_owned()
).await
}
async fn down(&self, m: &SchemaManager) -> Result<(), DbErr> {
m.drop_table(Table::drop().table(SysUser::Table).to_owned()).await
}
}
#[derive(Iden)]
enum SysUser { Table, Id, Username, PasswordHash, RealName, DeptId, Status, CreatedAt, UpdatedAt, DeletedAt }
执行迁移:
sea-orm-cli migrate up
# 或在项目内:
# cargo run -p migration
快速 CRUD 示例
查询:
use sea_orm::{EntityTrait, QueryFilter, ColumnTrait};
let user = entity::sys_user::Entity::find()
.filter(entity::sys_user::Column::Username.eq("admin"))
.one(&db).await?;
插入:
use sea_orm::{ActiveModelTrait, Set};
use chrono::Utc;
let now = Utc::now();
let user = entity::sys_user::ActiveModel {
id: Set(id_gen.next_id()),
username: Set("alice".into()),
password_hash: Set(hash_password("Secret@123")?),
status: Set(1),
created_at: Set(now),
updated_at: Set(now),
..Default::default()
}.insert(&db).await?;
更新:
use sea_orm::IntoActiveModel;
let mut am = user.into_active_model();
am.real_name = Set(Some("Alice".into()));
am.updated_at = Set(chrono::Utc::now());
let user = am.update(&db).await?;
删除(硬删):
use sea_orm::EntityTrait;
entity::sys_user::Entity::delete_by_id(1001).exec(&db).await?;
软删除(推荐):
use sea_orm::{ActiveModelTrait, Set};
let mut am: entity::sys_user::ActiveModel = entity::sys_user::Entity::find_by_id(1001)
.one(&db).await?.ok_or_else(|| anyhow::anyhow!("not found"))?
.into_active_model();
am.deleted_at = Set(Some(chrono::Utc::now()));
am.update(&db).await?;
分页与事务
分页:
use sea_orm::{QuerySelect, PaginatorTrait};
let paginator = entity::sys_user::Entity::find().paginate(&db, 20);
let total = paginator.num_items().await?;
let items = paginator.fetch_page(0).await?;
事务:
use sea_orm::{TransactionTrait, Set};
db.transaction(|txn| {
Box::pin(async move {
// 多表操作...
Ok(())
})
}).await?;
与祺洛工程的配合建议
- 迁移先行:所有表结构变更通过 migration 完成
- 实体生成后统一放置在
src/db/entities
下,启用with-chrono/with-json
等特性 - Service 层封装查询与业务,Handler 不直接依赖实体
- 软删除字段(deleted_at)与审计字段(created_by/updated_by/...)统一风格
后续阅读
- 模型查询、集合与映射
- 新增与更新、删除与软删除
- 关联与预加载
- 作用域(Scope)、事件(Behavior)、类型转换(枚举/JSON)