news 2026/9/24 14:35:22

SeaORM 2.0.0-rc.31 变更解读:`ne_all` 条件、`model_attrs` 宏定制、`TextUuid` 类型列与 `COUNT(*)` 溢出修复

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
SeaORM 2.0.0-rc.31 变更解读:`ne_all` 条件、`model_attrs` 宏定制、`TextUuid` 类型列与 `COUNT(*)` 溢出修复
  • 后端
  • 数据库
  • ORM

【免费下载链接】sea-orm

🐚 A powerful relational ORM for Rust

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

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_alleq_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_attrsmodel_ex_attrs两个属性,允许把自定义 derive 和 attribute 透传给生成的ModelModelEx结构体。

宏展开原理

该功能在 sea-orm-macros/src/derives/model_ex.rs 中实现。宏展开流程如下:

  1. 遍历输入结构体上的所有属性,凡是#[sea_orm(...)]之外的属性(如#[derive(TS)])会被同时收进model_attrsinherited_model_ex_attrs(第 31-35 行);
  2. 解析#[sea_orm(model_attrs(...))]#[sea_orm(model_ex_attrs(...))]括号内的嵌套 meta,通过parse_quote!(#[#m])逐个转换成真正的Attribute,分别存入model_attrsmodel_ex_attrs(第 40-56 行);
  3. 展开时,model_attrs前缀在Model结构体上,inherited_model_ex_attrsmodel_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")],从而在前端类型导出中获得自定义接口名。除此之外,它同样可用于注入serdeCloneDebug等任意 derive 或 attribute,为同一实体派生“展示用”与“完整关联用”两套结构体提供了更灵活的定制入口。

新特性:TextUuid类型列(#2717)

rc.31 为TextUuid增加了类型化列(typed column)支持。TextUuid是 SeaORM 内置的 UUID 字符串包装类型,定义于 src/value/text_uuid.rs,内部持有uuid::Uuid,以字符串形式与数据库交互,并实现了ValueTypeTryGetableTryFromU64(用于主键场景)等 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 的变更,升级时建议依次确认:

  1. 同步升级sea-queryeq_any/ne_all的 trait bound 放宽依赖最新sea-query
  2. 检查计数查询的目标类型COUNT(*)结果统一为i64,排查原先绑定到i32的代码;
  3. 按需使用新 API:PostgreSQL 用户可引入ne_all简化取反集合过滤;使用ts-rsserde等派生库的实体可通过model_attrs/model_ex_attrs定制派生属性;UUID 字符串主键场景可借助TextUuidColumn获得类型化运算符;
  4. 回归代理与计数场景:若使用了ProxyDatabase或大表计数,优先验证错误传播与数值范围。

整体来看,rc.31 延续了 SeaORM 2.0 系列“类型安全 + 后端一致”的路线:既在查询层补齐了集合取反能力,也在宏层放开了派生定制空间,同时修复了影响大数据量场景的计数溢出问题,是向 2.0 稳定版推进过程中值得关注的一个版本。

  • 后端
  • 数据库
  • ORM

【免费下载链接】sea-orm

🐚 A powerful relational ORM for Rust

项目地址:https://gitcode.com/gh_mirrors/se/sea-orm
点击查看免费下载
上一篇:为什么OpenRadar是毫米波雷达数据处理的最佳Python方案?
下一篇:soci-snapshotter CLI命令详解:轻松掌握容器镜像懒加载

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

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

Claude Code嵌入式开发实战:STM32项目配置与AI辅助编程技巧

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/24 14:31:31

密码学入门:从古典加密到现代网络安全

什么是密码学&#xff1f; 密码学是保护信息安全的科学&#xff0c;它通过加密技术将可读的信息&#xff08;明文&#xff09;转换为不可读的形式&#xff08;密文&#xff09;&#xff0c;只有授权方才能解密恢复原始内容。就像给信息上了一把"数字锁"&#xff0c;只…

作者头像 李华
网站建设 2026/9/24 14:31:17

GEO实战经验分享:让AI推荐你的产品

首先GEO是啥&#xff1f;会取得什么效果&#xff1f;官方定义是指生成式引擎优化&#xff0c;核心目标是让你的工具&#xff0c;在AI生成的回答中被引用和推荐。大白话做GEO就是让豆包、deepseek这些AI收录你的工具\商铺\言论。比如你是开店卖手办的&#xff0c;你为你的店&quo…

作者头像 李华
网站建设 2026/9/24 14:30:50

计算机系统---CPU的进程与线程处理

在计算机系统中&#xff0c;CPU作为“运算核心”是通过进程与线程这两个抽象层&#xff0c;实现对海量任务的有序调度、资源隔离与高效并发。 理解CPU如何处理进程与线程&#xff0c;本质是理解操作系统如何“管理任务”与“分配算力”——这一过程覆盖了资源定义、状态流转、调…

作者头像 李华