- 后端
- 数据库
- ORM
【免费下载链接】sea-orm
🐚 A powerful relational ORM for Rust
SeaORM 2.0.0-rc.31(发布于 2.0.0-rc.30 之后)是 2.0 系列迈向稳定版过程中的一次功能与稳定性迭代:一方面为查询条件、宏派生与类型系统新增能力,另一方面修复了 MySQL / SQLite 上COUNT(*)的整数溢出隐患。本文以该变更日志为骨架,结合仓库源码逐一拆解这些改动背后的实现细节与使用方式,帮助读者在升级到 rc.31 后快速掌握新 API 并规避兼容性陷阱。
新特性:ne_all条件方法,补全 PostgreSQL 数组取反查询
rc.31 为ColumnTrait新增了ne_all方法,用于对多个值做“不等于任意一个”的取反查询,与既有的eq_any形成互补。
ne_all的语义与生成 SQL
从源码看,ne_all是eq_any的“相反操作”,等价于is_not_in。其实现位于 src/entity/column.rs:
#[cfg(feature = "postgres-array")] fn ne_all<V, I>(&self, v: I) -> Expr where V: Into<Value> + sea_query::postgres_array::NotU8, I: IntoIterator<Item = V>, { use sea_query::extension::postgres::PgFunc; let values: Vec<Value> = v.into_iter().map(|v| v.into()).collect(); if let Some(first) = values.first() { Expr::col(self.as_column_ref()).ne(PgFunc::all(Value::Array( first.array_type(), Some(Box::new(values)), ))) } else { Expr::col(self.as_column_ref()).is_not_in(std::iter::empty::<V>()) } }注意两点关键行为:
- 空集合的边界语义:当传入的迭代器为空时,
ne_all退化为is_not_in(空集),生成WHERE 1 = 1(而非恒假条件),与eq_any空集生成WHERE 1 = 2的行为对称,保证“全部不匹配空集合”逻辑恒真。这一点在源码的 doctest 中有明确断言(src/entity/column.rs)。 - 仅限 PostgreSQL:该方法是 PostgreSQL 数组扩展的一部分,由
postgres-arrayfeature 门控。它利用PgFunc::all生成<> ALL(ARRAY [...])语法,例如cake::Column::Id.ne_all(vec![4, 5])会编译为WHERE "cake"."id" <> ALL(ARRAY [4,5])。
与eq_any的对比使用
eq_any同样位于 src/entity/column.rs,生成= ANY(ARRAY [...])。两者搭配可在一条查询中表达“匹配任一”与“不匹配任一”两种集合语义:
// 匹配任意一个 cake::Entity::find() .filter(cake::Column::Id.eq_any(vec![4, 5])); // 不匹配任意一个(rc.31 新增) cake::Entity::find() .filter(cake::Column::Id.ne_all(vec![4, 5]));泛型边界放宽:eq_any/ne_all兼容性提升
变更日志同时提到“放宽了eq_any/ne_all的 trait bounds”,并依赖最新版sea-query。结合实现可以看出,方法签名中V: Into<Value> + sea_query::postgres_array::NotU8的组合边界(NotU8用于排除歧义的u8数组类型),使得更多标量类型可以直接传入集合迭代器,而无需手动构造Value。升级提示:使用这两个方法前请同步升级sea-query依赖,否则可能出现 trait bound 不满足的编译错误。
新特性:model_attrs/model_ex_attrs自定义派生属性
rc.31 为#[sea_orm::model]宏引入了model_attrs与model_ex_attrs两个属性,允许把自定义 derive 和 attribute 透传给生成的Model与ModelEx结构体。
宏展开原理
该功能在 sea-orm-macros/src/derives/model_ex.rs 中实现。宏展开流程如下:
- 遍历输入结构体上的所有属性,凡是
#[sea_orm(...)]之外的属性(如#[derive(TS)])会被同时收进model_attrs与inherited_model_ex_attrs(第 31-35 行); - 解析
#[sea_orm(model_attrs(...))]与#[sea_orm(model_ex_attrs(...))]括号内的嵌套 meta,通过parse_quote!(#[#m])逐个转换成真正的Attribute,分别存入model_attrs与model_ex_attrs(第 40-56 行); - 展开时,
model_attrs前缀在Model结构体上,inherited_model_ex_attrs与model_ex_attrs前缀在ModelEx结构体上(第 151-166 行)。
其中inherited_model_ex_attrs还会经过一轮过滤:自动剔除Eq(嵌套关系可能包含非Eq字段,无法安全转发),并把DeriveEntityModel替换为DeriveModelEx/DeriveActiveModelEx(第 89-115 行)。
典型用法:定制 TypeScript 接口名
变更日志给出了配合ts-rs生成 TypeScript 类型、并重命名接口的示例:
#[sea_orm::model] #[derive(TS, ...)] #[sea_orm(model_attrs(ts(rename = "Fruit")))] #[sea_orm(model_ex_attrs(ts(rename = "FruitEx")))] struct Model { ... }展开后Model获得#[ts(rename = "Fruit")]、ModelEx获得#[ts(rename = "FruitEx")],从而在前端类型导出中获得自定义接口名。除此之外,它同样可用于注入serde、Clone、Debug等任意 derive 或 attribute,为同一实体派生“展示用”与“完整关联用”两套结构体提供了更灵活的定制入口。
新特性:TextUuid类型列(#2717)
rc.31 为TextUuid增加了类型化列(typed column)支持。TextUuid是 SeaORM 内置的 UUID 字符串包装类型,定义于 src/value/text_uuid.rs,内部持有uuid::Uuid,以字符串形式与数据库交互,并实现了ValueType、TryGetable、TryFromU64(用于主键场景)等 trait。
类型化列位于 src/entity/column/types.rs 的TextUuidColumn<E>,并基于 src/entity/column/types/with_uuid.rs 中的bind_oper!/bind_oper_2!/bind_vec_func!生成全套运算符:
bind_oper!(pub eq, eq, type TextUuid); bind_oper!(pub ne, ne, type TextUuid); bind_oper!(pub gt, gt, type TextUuid); bind_oper!(pub gte, gte, type TextUuid); bind_oper!(pub lt, lt, type TextUuid); bind_oper!(pub lte, lte, type TextUuid); bind_oper_2!(pub between, between, type TextUuid); bind_oper_2!(pub not_between, not_between, type TextUuid); bind_oper!(pub if_null, if_null, type TextUuid); bind_vec_func!(pub is_in, is_in, type TextUuid); bind_vec_func!(pub is_not_in, is_not_in, type TextUuid);这意味着以TextUuid为主键或普通列的实体,现在可以用强类型方式书写过滤条件,例如entity::Column::Uuid.eq(some_uuid_text)、is_in(vec![...])等,无需手工做字符串/Uuid转换,减少类型错误并提升可读性。
Bug 修复:COUNT(*)在 MySQL / SQLite 上的溢出问题(#2944)
rc.31 修复了一个在大数据集下可能静默出错的隐患:COUNT(*)在所有后端统一返回i64。
背景是 MySQL 的COUNT(*)返回BIGINT(64 位),而此前 SeaORM 将其按i32读取,当表行数超过 21 亿时会发生溢出。rc.31 起统一按i64解析。仓库中的文档示例已同步更新,例如 src/executor/select.rs 中的 doctest 使用Into::<Value>::into(2i64)构造计数结果,并将结果类型声明为Vec<(String, i64)>,配合cake::Column::Id.count()与into_values::<_, QueryAs>()完成分组计数查询:
let res: Vec<(String, i64)> = cake::Entity::find() .select_only() .column_as(cake::Column::Name, QueryAs::CakeName) .column_as(cake::Column::Id.count(), QueryAs::NumOfCakes) .group_by(cake::Column::Name) .into_values::<_, QueryAs>() .all(&db) .await?;升级影响:如果你此前将计数查询结果绑定到i32变量,升级到 rc.31 后需要把目标类型改为i64(或让类型推导自动适配),否则会出现类型不匹配的编译错误。
Bug 修复:Proxy 错误处理(#2935)
rc.31 修复了ProxyDatabase的错误处理路径。代理模式允许在 Rust 侧自定义数据库实现(例如转发到远程服务),其核心 trait 定义于 src/database/proxy.rs,query/execute返回Result<_, DbErr>,ping用于探测数据库可用性并应返回错误以标识不可用。本次修复确保了代理层在查询失败、执行失败等场景下能够正确传播DbErr,而不是被吞掉或错误转换,从而让上层拿到准确的错误信息。如果项目通过ProxyDatabaseTrait实现自定义数据源,建议升级后补充针对异常路径的回归测试。
改进:移除原始字符串哈希(#2381)
变更日志还提到一次内部重构:“移除原始字符串哈希,以提升可读性”。这类改动通常发生在标识符生成、内部名称编码等环节——将原先基于HashSet/ 哈希去重或哈希键控的不可读字符串,替换为更直观的生成逻辑。该改动不改变对外 API,但会减少--release下二进制或诊断信息中出现的不可读哈希字符串,属于对开发者友好的内部整理。若你的项目依赖 SeaORM 内部行为(例如宏生成结构体的Debug输出),建议在升级后快速跑一遍测试确认无回归。
升级建议与总结
综合 rc.31 的变更,升级时建议依次确认:
- 同步升级
sea-query:eq_any/ne_all的 trait bound 放宽依赖最新sea-query; - 检查计数查询的目标类型:
COUNT(*)结果统一为i64,排查原先绑定到i32的代码; - 按需使用新 API:PostgreSQL 用户可引入
ne_all简化取反集合过滤;使用ts-rs、serde等派生库的实体可通过model_attrs/model_ex_attrs定制派生属性;UUID 字符串主键场景可借助TextUuidColumn获得类型化运算符; - 回归代理与计数场景:若使用了
ProxyDatabase或大表计数,优先验证错误传播与数值范围。
整体来看,rc.31 延续了 SeaORM 2.0 系列“类型安全 + 后端一致”的路线:既在查询层补齐了集合取反能力,也在宏层放开了派生定制空间,同时修复了影响大数据量场景的计数溢出问题,是向 2.0 稳定版推进过程中值得关注的一个版本。
- 后端
- 数据库
- ORM
【免费下载链接】sea-orm
🐚 A powerful relational ORM for Rust
相关推荐
SeaORM 2.0.0-rc.28 版本解读:ActiveValue 条件更新、主键 auto_increment 修正与 Postgres 事务配置修复
SeaORM 2.0.0 rc.28 版本解读:ActiveValue 条件更新、主键 auto_increment 修正与 Postgres 事务配置修复 本
后端数据库ORMSeaORM 2.0.0-rc.29 版本解读:分布式 Tracing 支持、TextUuid 与 TryInsert 新特性全解析
SeaORM 2.0.0 rc.29 版本解读:分布式 Tracing 支持、TextUuid 与 TryInsert 新特性全解析 本篇文章以 SeaORM
后端数据库ORMmidir Android开发实战:API 29+平台上的MIDI应用构建指南
midir Android开发实战:API 29+平台上的MIDI应用构建指南 midir是一个基于Rust的跨平台实时MIDI处理库,为Android API
后端数据库ORM
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考