模型查询
大约 4 分钟
模型查询
本节展示 Sea-ORM 常用查询:单条/列表、条件过滤、排序、选择列、聚合、关联查询、动态条件与存在性判断等。示例以 sys_user
为例。
依赖引入(示例)
use sea_orm::{
ColumnTrait, Condition, DatabaseConnection, EntityTrait, PaginatorTrait,
QueryFilter, QueryOrder, QuerySelect, sea_query::{Expr, Alias}
};
基础查询
- 单条与可选:
let user = entity::sys_user::Entity::find_by_id(1001).one(&db).await?;
if let Some(u) = user { /* ... */ }
- 列表:
let users = entity::sys_user::Entity::find()
.filter(entity::sys_user::Column::Status.eq(1))
.order_by_desc(entity::sys_user::Column::Id)
.all(&db)
.await?;
- 计数与存在性:
let total = entity::sys_user::Entity::find()
.filter(entity::sys_user::Column::Status.eq(1))
.count(&db)
.await?;
// 是否存在
let exists = total > 0;
条件过滤(Condition)
- 动态拼接(根据可选参数构造条件):
fn user_filters(keyword: Option<String>, dept_id: Option<i64>, enabled: Option<bool>) -> Condition {
let mut cond = Condition::all();
if let Some(kw) = keyword {
// 模糊匹配(使用 like 表达式)
cond = cond.add(
Expr::col(entity::sys_user::Column::Username).like(format!("%{}%", kw))
.or(Expr::col(entity::sys_user::Column::RealName).like(format!("%{}%", kw)))
);
}
if let Some(d) = dept_id {
cond = cond.add(entity::sys_user::Column::DeptId.eq(d));
}
if let Some(e) = enabled {
cond = cond.add(entity::sys_user::Column::Status.eq(if e { 1 } else { 0 }));
}
cond
}
let filters = user_filters(Some("adm".into()), None, Some(true));
let users = entity::sys_user::Entity::find().filter(filters).all(&db).await?;
- IN / NOT IN:
let ids = vec![1_i64, 2, 3];
let users = entity::sys_user::Entity::find()
.filter(entity::sys_user::Column::Id.is_in(ids))
.all(&db).await?;
- NULL 判断:
let users = entity::sys_user::Entity::find()
.filter(entity::sys_user::Column::DeptId.is_null())
.all(&db).await?;
- 区间:
let users = entity::sys_user::Entity::find()
.filter(entity::sys_user::Column::CreatedAt.gte(start_utc))
.filter(entity::sys_user::Column::CreatedAt.lte(end_utc))
.all(&db).await?;
- 大小写不敏感(PostgreSQL iLike):
let kw = "Alice";
let users = entity::sys_user::Entity::find()
.filter(Expr::col(entity::sys_user::Column::Username).ilike(format!("%{}%", kw)))
.all(&db).await?;
注意:ilike
为 sea_query 表达式(PostgreSQL),MySQL 可用 lower(col) like lower(?)
。
排序与分页
- 多字段排序:
let users = entity::sys_user::Entity::find()
.order_by_asc(entity::sys_user::Column::DeptId)
.order_by_desc(entity::sys_user::Column::Id)
.all(&db).await?;
- 分页(更多见“数据库/分页”):
let page = 1_u64;
let size = 20_u64;
let paginator = entity::sys_user::Entity::find().paginate(&db, size);
let total = paginator.num_items().await?;
let items = paginator.fetch_page(page - 1).await?;
选择列与自定义投影
- 只选部分列:
let rows = entity::sys_user::Entity::find()
.select_only()
.column(entity::sys_user::Column::Id)
.column(entity::sys_user::Column::Username)
.column(entity::sys_user::Column::RealName)
.all(&db).await?;
- 映射到自定义 VO(FromQueryResult):
use sea_orm::FromQueryResult;
#[derive(Debug, FromQueryResult)]
struct UserLite {
id: i64,
username: String,
real_name: Option<String>,
}
let items: Vec<UserLite> = entity::sys_user::Entity::find()
.select_only()
.column(entity::sys_user::Column::Id)
.column(entity::sys_user::Column::Username)
.column(entity::sys_user::Column::RealName)
.into_model::<UserLite>()
.all(&db).await?;
- 计算列/别名:
let items: Vec<(i64, String, String)> = entity::sys_user::Entity::find()
.select_only()
.column(entity::sys_user::Column::Id)
.column_as(
Expr::col(entity::sys_user::Column::Username),
Alias::new("name"),
)
.column_as(
Expr::case(Expr::col(entity::sys_user::Column::Status))
.when(1).then("enabled").finally("disabled"),
Alias::new("status_text"),
)
.into_tuple()
.all(&db).await?;
- DISTINCT:
let dept_ids: Vec<i64> = entity::sys_user::Entity::find()
.select_only()
.column(entity::sys_user::Column::DeptId)
.distinct()
.into_tuple()
.all(&db).await?
.into_iter()
.flatten()
.collect();
聚合(count/sum/avg/max/min)
#[derive(FromQueryResult, Debug)]
struct Stat {
total: i64,
enabled: i64,
}
let stat: Option<Stat> = entity::sys_user::Entity::find()
.select_only()
.column_as(Expr::val(1).count(), Alias::new("total"))
.column_as(
Expr::case(Expr::col(entity::sys_user::Column::Status))
.when(1).then(1).finally(0)
.sum(),
Alias::new("enabled"),
)
.into_model::<Stat>()
.one(&db).await?;
关联与连接(Join)
- 通过关系辅助(见“关联关系/预加载”章节):
// 用户和部门(belongs_to Dept)
let with_dept = entity::sys_user::Entity::find()
.find_also_related(entity::sys_dept::Entity)
.all(&db).await?;
// Vec<(UserModel, Option<DeptModel>)>
- 手写 JOIN:
use sea_orm::{JoinType, QueryTrait};
let rows = entity::sys_user::Entity::find()
.join(JoinType::LeftJoin, entity::sys_user::Relation::Dept.def())
.filter(entity::sys_user::Column::Status.eq(1))
.all(&db).await?;
JSON 字段过滤(PostgreSQL)
当表含 json/jsonb 字段时,可用自定义表达式:
// 假设 sys_user.profile (jsonb) 含 { "phone": "..." }
let users = entity::sys_user::Entity::find()
.filter(Expr::cust(r#"profile->>'phone' = '13800000000'"#))
.all(&db).await?;
或使用 json_extract
(MySQL):
let users = entity::sys_user::Entity::find()
.filter(Expr::cust("JSON_EXTRACT(profile, '$.phone') = '13800000000'"))
.all(&db).await?;
注意:不同数据库 JSON 语法不同,按实际驱动编写。
原生 SQL(必要时)
- 查询:
use sea_orm::{DbBackend, Statement, FromQueryResult};
#[derive(FromQueryResult, Debug)]
struct UserLite { id: i64, username: String }
let stmt = Statement::from_string(
DbBackend::Postgres,
"select id, username from sys_user where status = 1 order by id desc limit 10".into()
);
let items = UserLite::find_by_statement(stmt).all(&db).await?;
- 执行(更新/删除):
let aff = db.execute(Statement::from_string(
DbBackend::Postgres,
"update sys_user set status = 0 where last_login_at < now() - interval '180 days'".into()
)).await?;
let rows_affected = aff.rows_affected();
尽量优先使用 ORM API;仅在复杂报表、专用函数场景用原生 SQL。
数据权限条件(示例)
将“数据范围”封装为条件函数,查询时统一拼接:
fn data_scope_cond(claims: &Claims) -> Condition {
match claims.data_scope.as_deref() {
Some("ALL") => Condition::all(),
Some("DEPT") => Condition::all().add(entity::sys_user::Column::DeptId.eq(claims.dept_id)),
Some("SELF") => Condition::all().add(entity::sys_user::Column::Id.eq(claims.sub)),
_ => Condition::all(),
}
}
let users = entity::sys_user::Entity::find()
.filter(data_scope_cond(&claims))
.all(&db).await?;
小结
- 使用
filter/Condition/Expr
构建灵活条件 select_only + into_model/into_tuple
做自定义投影find_also_related/find_with_related
/join
处理关联- 需要时使用
Statement
执行原生 SQL - 将常用模式封装为函数(搜索/排序/数据权限),在 Service 层复用