🐚 SeaORM
高级关系
以高层、概念化的方式建模 1-1、1-N、M-N 甚至自引用等复杂关系。
熟悉的概念
受 Ruby、Python 和 Node.js 生态系统中流行 ORM 的启发,SeaORM 提供了一种令人倍感亲切的开发者体验。
功能丰富
SeaORM 是一个功能完备的 ORM,内置过滤器、分页和嵌套查询,可加速构建 REST、GraphQL 和 gRPC API。
生产就绪
凭借每周超过 25 万次的下载量,SeaORM 已具备生产就绪能力,受到全球初创企业和大型企业的信赖。
快速入门
集成示例:
- Actix 示例
- Axum 示例
- GraphQL 示例
- jsonrpsee 示例
- Loco 示例 / Loco REST 启动器
- Poem 示例
- Rocket 示例 / Rocket OpenAPI 示例
- Salvo 示例
- Tonic 示例
- Seaography 示例 (Bakery) / Seaography 示例 (Sakila)
如果你想要一个简单、干净且能容纳在单个文件中、展示 SeaORM 最佳实践的示例,可以尝试:
让我们快速浏览一下 SeaORM 的独特功能。
表达力强的实体格式
你不必手动编写这些!实体文件可以使用 sea-orm-cli 从现有数据库生成,
以下是使用 --entity-format dense (2.0 版本新增) 生成的内容。
mod user {
use sea_orm::entity::prelude::*;
#[sea_orm::model]
#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)]
#[sea_orm(table_name = "user")]
pub struct Model {
#[sea_orm(primary_key)]
pub id: i32,
pub name: String,
#[sea_orm(unique)]
pub email: String,
#[sea_orm(has_one)]
pub profile: HasOne<super::profile::Entity>,
#[sea_orm(has_many)]
pub posts: HasMany<super::post::Entity>,
}
}
mod post {
use sea_orm::entity::prelude::*;
#[sea_orm::model]
#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)]
#[sea_orm(table_name = "post")]
pub struct Model {
#[sea_orm(primary_key)]
pub id: i32,
pub user_id: i32,
pub title: String,
#[sea_orm(belongs_to, from = "user_id", to = "id")]
pub author: BelongsTo<super::user::Entity>,
#[sea_orm(has_many, via = "post_tag")] // M-N relation with junction
pub tags: HasMany<super::tag::Entity>,
}
}
智能实体加载器
实体加载器智能地对 1-1 关系使用 join,对 1-N 关系使用数据加载器, 即使在执行嵌套查询时也能消除 N+1 问题。
// join paths:
// user -> profile
// user -> post
// post -> post_tag -> tag
let smart_user = user::Entity::load()
.filter_by_id(42) // shorthand for .filter(user::COLUMN.id.eq(42))
.with(profile::Entity) // 1-1 uses join
.with((post::Entity, tag::Entity)) // 1-N uses data loader
.one(db)
.await?
.unwrap();
// 3 queries are executed under the hood:
// 1. SELECT FROM user JOIN profile WHERE id = $
// 2. SELECT FROM post WHERE user_id IN (..)
// 3. SELECT FROM tag JOIN post_tag WHERE post_id IN (..)
smart_user
== user::ModelEx {
id: 42,
name: "Bob".into(),
email: "bob@sea-ql.org".into(),
profile: HasOne::loaded(Some(profile::ModelEx {
picture: "image.jpg".into(),
})),
posts: HasMany::Loaded(vec![post::ModelEx {
title: "Nice weather".into(),
tags: HasMany::Loaded(vec![tag::ModelEx {
tag: "sunny".into(),
}]),
}]),
};
ActiveModel:让嵌套持久化变得简单
使用流畅的构建器 API,在单次操作中持久化整个对象图:用户、个人资料(1-1)、帖子(1-N)和标签(M-N)。 SeaORM 会自动确定依赖关系,并以正确的顺序插入或删除对象。 这需要 SeaORM 2.0 的密集实体格式。
// this creates the nested object as shown above:
let user = user::ActiveModel::builder()
.set_name("Bob")
.set_email("bob@sea-ql.org")
.set_profile(profile::ActiveModel::builder().set_picture("image.jpg"))
.add_post(
post::ActiveModel::builder()
.set_title("Nice weather")
.add_tag(tag::ActiveModel::builder().set_tag("sunny")),
)
.save(db)
.await?;
模式优先还是实体优先?由你选择
SeaORM 提供了一个强大的迁移系统,让你可以轻松地创建表、修改模式并填充数据。
在 SeaORM 2.0 中,你还可以获得一流的 Entity First Workflow: 只需定义新实体或为现有实体添加列, SeaORM 将自动检测这些更改并创建新的表、列、唯一键和外键。
// SeaORM resolves foreign key dependencies and creates the tables in topological order.
// Requires the `entity-registry` and `schema-sync` feature flags.
db.get_schema_registry("my_crate::entity::*").sync(db).await;
人体工学原生 SQL
让 SeaORM 处理你 95% 的事务性查询。 对于剩余那些过于复杂而无法表达的情况, SeaORM 仍然提供了便捷的原始 SQL 编写支持。
let user = Item { name: "Bob" }; // nested parameter access
let ids = [2, 3, 4]; // expanded by the `..` operator
let user: Option<user::Model> = user::Entity::find()
.from_raw_sql(raw_sql!(
Sqlite,
r#"SELECT "id", "name" FROM "user"
WHERE "name" LIKE {user.name}
AND "id" in ({..ids})
"#
))
.one(db)
.await?;
同步支持
sea-orm-sync 提供了完整的 SeaORM API,无需异步运行时,使其非常适合使用 SQLite 的轻量级 CLI 程序。
请参阅 快速入门示例 了解用法。
基础
查询
SeaORM 在 Entity 层面建模 1-N 和 M-N 关系, 让你能够通过连接表在单次调用中遍历多对多链接。
// find all models
let cakes: Vec<cake::Model> = Cake::find().all(db).await?;
// find and filter
let chocolate: Vec<cake::Model> = Cake::find()
.filter(Cake::COLUMN.name.contains("chocolate"))
.all(db)
.await?;
// find one model
let cheese: Option<cake::Model> = Cake::find_by_id(1).one(db).await?;
let cheese: cake::Model = cheese.unwrap();
// find related models (lazy)
let fruit: Option<fruit::Model> = cheese.find_related(Fruit).one(db).await?;
// find related models (eager): for 1-1 relations
let cake_with_fruit: Vec<(cake::Model, Option<fruit::Model>)> =
Cake::find().find_also_related(Fruit).all(db).await?;
// find related models (eager): works for both 1-N and M-N relations
let cake_with_fillings: Vec<(cake::Model, Vec<filling::Model>)> = Cake::find()
.find_with_related(Filling) // for M-N relations, two joins are performed
.all(db) // rows are automatically consolidated by left entity
.await?;
嵌套查询
部分模型通过让你仅查询所需的字段来防止过度获取; 它还使编写深度嵌套的关系查询变得简单。
use sea_orm::DerivePartialModel;
#[derive(DerivePartialModel)]
#[sea_orm(entity = "cake::Entity")]
struct CakeWithFruit {
id: i32,
name: String,
#[sea_orm(nested)]
fruit: Option<fruit::Model>, // this can be a regular or another partial model
}
let cakes: Vec<CakeWithFruit> = Cake::find()
.left_join(fruit::Entity) // no need to specify join condition
.into_partial_model() // only the columns in the partial model will be selected
.all(db)
.await?;
插入
SeaORM 的 ActiveModel 让你可以直接使用 Rust 数据结构, 并通过简单的 API 进行持久化。 从不同的数据源插入大批量行非常容易。
let apple = fruit::ActiveModel {
name: Set("Apple".to_owned()),
..Default::default() // no need to set primary key
};
let pear = fruit::ActiveModel {
name: Set("Pear".to_owned()),
..Default::default()
};
// insert one: Active Record style
let apple = apple.insert(db).await?;
apple.id == 1;
// insert one: repository style
let result = Fruit::insert(apple).exec(db).await?;
result.last_insert_id == 1;
// insert many returning last insert id
let result = Fruit::insert_many([apple, pear]).exec(db).await?;
result.last_insert_id == Some(2);
插入(高级)
你可以利用数据库特定功能来执行 upsert 和幂等插入。
// insert many with returning (if supported by database)
let models: Vec<fruit::Model> = Fruit::insert_many([apple, pear])
.exec_with_returning(db)
.await?;
models[0]
== fruit::Model {
id: 1, // database assigned value
name: "Apple".to_owned(),
cake_id: None,
};
// insert with ON CONFLICT on primary key do nothing, with MySQL specific polyfill
let result = Fruit::insert_many([apple, pear])
.on_conflict_do_nothing()
.exec(db)
.await?;
matches!(result, TryInsertResult::Conflicted);
更新
ActiveModel 通过仅更新你已修改的字段来避免竞态条件, 绝不覆盖未触及的列。 你还可以使用流畅的查询构建 API 来编写复杂的批量更新查询。
use sea_orm::sea_query::{Expr, Value};
let pear: Option<fruit::Model> = Fruit::find_by_id(1).one(db).await?;
let mut pear: fruit::ActiveModel = pear.unwrap().into();
pear.name = Set("Sweet pear".to_owned()); // update value of a single field
// update one: only changed columns will be updated
let pear: fruit::Model = pear.update(db).await?;
// update many: UPDATE "fruit" SET "cake_id" = "cake_id" + 2
// WHERE "fruit"."name" LIKE '%Apple%'
Fruit::update_many()
.col_expr(fruit::COLUMN.cake_id, fruit::COLUMN.cake_id.add(2))
.filter(fruit::COLUMN.name.contains("Apple"))
.exec(db)
.await?;
保存
你可以使用 ActiveModel 执行“插入或更新”操作,从而轻松组合事务性操作。
let banana = fruit::ActiveModel {
id: NotSet,
name: Set("Banana".to_owned()),
..Default::default()
};
// create, because primary key `id` is `NotSet`
let mut banana = banana.save(db).await?;
banana.id == Unchanged(2);
banana.name = Set("Banana Mongo".to_owned());
// update, because primary key `id` is present
let banana = banana.save(db).await?;
删除
与插入和更新保持一致的相同 ActiveModel API。
// delete one: Active Record style
let orange: Option<fruit::Model> = Fruit::find_by_id(1).one(db).await?;
let orange: fruit::Model = orange.unwrap();
orange.delete(db).await?;
// delete one: repository style
let orange = fruit::ActiveModel {
id: Set(2),
..Default::default()
};
fruit::Entity::delete(orange).exec(db).await?;
// delete many: DELETE FROM "fruit" WHERE "fruit"."name" LIKE '%Orange%'
fruit::Entity::delete_many()
.filter(fruit::COLUMN.name.contains("Orange"))
.exec(db)
.await?;
原始 SQL 查询
raw_sql! 宏类似于 format! 宏,但没有 SQL 注入的风险。
它支持嵌套参数插值、数组和元组展开,甚至支持重复组,
在构建复杂查询时提供了极大的灵活性。
#[derive(FromQueryResult)]
struct CakeWithBakery {
name: String,
#[sea_orm(nested)]
bakery: Option<Bakery>,
}
#[derive(FromQueryResult)]
struct Bakery {
#[sea_orm(alias = "bakery_name")]
name: String,
}
let cake_ids = [2, 3, 4]; // expanded by the `..` operator
// can use many APIs with raw SQL, including nested select
let cake: Option<CakeWithBakery> = CakeWithBakery::find_by_statement(raw_sql!(
Sqlite,
r#"SELECT "cake"."name", "bakery"."name" AS "bakery_name"
FROM "cake"
LEFT JOIN "bakery" ON "cake"."bakery_id" = "bakery"."id"
WHERE "cake"."id" IN ({..cake_ids})"#
))
.one(db)
.await?;
🧭 Seaography: instant GraphQL API
Seaography 是一个为 SeaORM 构建的 GraphQL 框架。 Seaography 允许你快速构建 GraphQL resolvers。 只需几条命令,你就可以从 SeaORM entities 启动一个功能齐全的 GraphQL server, 包含 filter、pagination、relational queries 和 mutations!
查看 Seaography Example 以了解更多信息。
🖥️ SeaORM Pro: 专业管理面板
SeaORM Pro 是一个管理面板解决方案,允许您快速轻松地为您的应用启动一个管理面板 - 无需前端开发技能,但当然有会更好!
SeaORM Pro 已更新以支持 SeaORM 2.0 中的最新功能。
特性:
- 完整的 CRUD
- 基于 React + GraphQL 构建
- 内置 GraphQL resolver
- 使用 TOML 配置自定义 UI
- 基于角色的访问控制 (2.0 新增)
阅读 快速入门 指南以了解更多信息。

SQL Server 支持
SQL Server for SeaORM 为 MSSQL 提供与 SeaORM 相同的 API。我们移植了所有测试用例和示例,并补充了 MSSQL 特定的文档。如果您正在开发企业级软件,可以申请商业访问权限。目前基于 SeaORM 1.0,但当 SeaORM 2.0 最终确定时,我们将为现有用户提供免费升级。
发布
SeaORM 2.0 已进入发布候选阶段。我们非常希望您能试用它,并通过分享您的反馈来帮助塑造最终版本。
SeaORM 2.0 正在成为我们迄今为止最重要的版本——包含一些破坏性变更、大量增强功能,并明确聚焦于开发者体验。
- SeaORM 2.0 一瞥
- SeaORM 2.0:深入观察
- SeaORM 2.0 中的基于角色的访问控制
- Seaography 2.0:一个强大且可扩展的 GraphQL 框架
- SeaORM 2.0:新实体格式
- SeaORM 2.0:实体优先工作流
- SeaORM 2.0:强类型列
- SeaORM Pro 2.0 的新特性
- SeaORM 2.0:嵌套 ActiveModel
- SeaORM 2.0 详解
- 我们如何让 SeaORM 变为同步
- SeaORM 2.0 迁移指南
- SeaORM 现已支持 Arrow & Parquet
- SeaORM 2.0 支持 SQL Server
如果您大量使用 SeaQuery,我们建议查看我们关于 SeaQuery 1.0 发布的博文:
许可证
在以下任一许可证下授权
- Apache License, Version 2.0 (LICENSE-APACHE 或 http://www.apache.org/licenses/LICENSE-2.0)
- MIT license (LICENSE-MIT 或 http://opensource.org/licenses/MIT)
at your option.
Contribution
除非您明确声明其他情况,否则您有意提交以纳入该作品的任何贡献,如 Apache-2.0 许可证中所定义,均将按上述方式双重授权,不附加任何额外条款或条件。
我们邀请您参与、贡献,并共同帮助构建 Rust 的未来。
向我们的贡献者致以诚挚的感谢!
谁在使用 SeaORM?
以下是一些使用 SeaORM 构建的优秀开源软件的简短列表。欢迎提交你的项目!
| 项目 | GitHub | 标语 |
|---|---|---|
| Zed | 一款高性能、多人协作的代码编辑器 | |
| Servo | Servo 并行浏览器引擎项目 | |
| OpenObserve | 开源可观测性平台 | |
| RisingWave | 流处理与管理平台 | |
| Warpgate | 可与任何 SSH 客户端配合使用的智能 SSH 堡垒机 | |
| LLDAP | 用于用户管理的轻量级 LDAP 服务器 | |
| Svix | 企业级 Webhooks 服务 | |
| Ryot | 你唯一需要的自托管追踪器 | |
| OctoBase | 轻量级、可扩展、离线协作数据后端 | |
| System Initiative | DevOps 自动化平台 |
赞助
SeaQL.org 是一个由充满热情的开发者运营的独立开源组织。 如果你愿意慷慨解囊,通过 GitHub Sponsor 进行的小额捐赠将不胜感激,并对维持该组织的运营大有裨益。
银牌赞助商
感谢我们的银牌赞助商:Digital Ocean,赞助了我们的服务器。以及 JetBrains,赞助了我们的 IDE。
|
|
|
吉祥物
Ferris 的朋友,寄居蟹 Terres 是 SeaORM 的官方吉祥物。他的爱好是收集贝壳。
🦀 Rustacean 贴纸包
Rustacean 贴纸包是表达你对 Rust 热爱的完美方式。我们的贴纸采用优质防水乙烯基材料制成,具有独特的哑光质感。
贴纸包内容:
- SeaQL 项目的标志:SeaQL、SeaORM、SeaQuery、Seaography
- 吉祥物:螃蟹 Ferris x 3、寄居蟹 Terres
- Rustacean 字标
支持 SeaQL 并获取贴纸包! 所有收益将直接用于 SeaQL 项目的持续开发。
