6 releases (3 breaking)
Uses new Rust 2024
| new 0.4.2 | Dec 25, 2025 |
|---|---|
| 0.4.1 | Dec 24, 2025 |
| 0.3.0 | Dec 21, 2025 |
| 0.2.0 | Dec 20, 2025 |
| 0.1.1 | Dec 18, 2025 |
#332 in Database interfaces
140KB
3K
SLoC
uorm
Rust 下的轻量级 ORM 框架,借鉴 Java MyBatis 的设计理念,强调 SQL 与业务逻辑分离:用 XML 管理 SQL,通过 sql_id 调用并把结果自动反序列化到 Rust 类型;同时支持直接执行原生 SQL,原生支持 async/await。
特性
- 🚀 MyBatis 风格:支持熟悉的 XML Mapper 语法,通过
namespace.id唯一定位 SQL。 - 🎯 动态 SQL:支持
<if>、<foreach>、<include>等标签,轻松构建复杂 SQL。 - 📦 类型安全:利用 Rust 强大的类型系统,通过
serde自动处理参数绑定和结果反序列化。 - ⚡ 异步优先:基于
tokio运行时,全程支持async/await,适配高并发场景。 - 🔧 灵活配置:支持多数据源管理、连接池优化、超时设置及事务控制。
- 🛠️ 过程宏增强:提供
#[sql]、#[transaction]及mapper_assets!等宏,极大简化开发工作。 - 🗄️ 多数据库支持:原生支持 SQLite 和 MySQL,架构易于扩展至其他 UDBC 驱动。
- 📝 详细日志:集成
logcrate,提供 SQL 执行、耗时及参数详情,便于调试。
安装
在 Cargo.toml 中添加依赖:
[dependencies]
uorm = "0.4.1"
特性开关 (Features)
sqlite(默认开启):支持 SQLite 数据库。mysql:支持 MySQL 数据库。
[dependencies]
# 仅启用 MySQL 支持
uorm = { version = "0.4.1", default-features = false, features = ["mysql"] }
快速开始
1) 注册数据库驱动
通过 U 全局单例注册驱动。SqliteDriver 和 MysqlDriver 均采用 Builder 模式。
use uorm::driver_manager::U;
use uorm::udbc::sqlite::pool::SqliteDriver;
#[tokio::main]
async fn main() -> Result<(), uorm::error::DbError> {
// 创建驱动并指定名称(默认为 "default")
let driver = SqliteDriver::new("sqlite::memory:")
.build()?;
// 注册到全局管理器
U.register(driver)?;
Ok(())
}
2) 加载 Mapper XML
uorm 提供两种方式加载 XML 资源:
方式一:编译期内嵌(推荐)
使用 mapper_assets! 宏,在编译时将 XML 文件内容嵌入二进制中,程序启动时自动注册。
use uorm::mapper_assets;
// 自动扫描路径下的所有 XML 文件并内嵌
mapper_assets!["src/resources/**/*.xml"];
方式二:运行时加载
在程序启动后手动扫描文件系统加载 XML。
use uorm::driver_manager::U;
fn init_mappers() -> Result<(), uorm::error::DbError> {
// 运行时扫描并解析 XML
U.assets("src/resources/**/*.xml")?;
Ok(())
}
3) 执行 Mapper 调用
use serde::{Deserialize, Serialize};
use uorm::driver_manager::U;
#[derive(Debug, Deserialize)]
struct User {
id: i64,
name: String,
age: i64,
}
#[derive(Serialize)]
struct IdArg {
id: i64,
}
pub async fn get_user_by_id(user_id: i64) -> Result<User, uorm::error::DbError> {
let mapper = U.mapper().expect("Driver not found");
// execute 会根据 XML 定义的标签(select/insert/update/delete)自动执行。
// 对于 select,如果结果只有一行且返回类型是结构体而非 Vec,会自动解包(Unwrap)。
mapper.execute("user.get_by_id", &IdArg { id: user_id }).await
}
SQL 过程宏 (#[sql])
使用 #[sql] 宏可以像定义 DAO 接口一样操作数据库,代码更加优雅。
use serde::{Deserialize, Serialize};
use uorm::{exec, sql};
#[derive(Debug, Serialize, Deserialize)]
struct User {
id: i64,
name: String,
}
#[sql("user")] // 指定 XML 的 namespace
struct UserDao;
impl UserDao {
#[sql("get_by_id")] // 对应 user.get_by_id
pub async fn get(id: i64) -> Result<User, uorm::error::DbError> {
// exec!() 是由 #[sql] 宏在函数内部注入的局部宏
// 它会自动捕获函数参数、namespace 和 id 并执行调用
exec!()
}
#[sql(id = "list_all", database = "other_db")] // 可指定特定的数据库名称
pub async fn list_all() -> Result<Vec<User>, uorm::error::DbError> {
exec!()
}
}
直接执行 SQL (Session)
如果不想使用 XML,也可以通过 Session 直接执行带有命名参数的 SQL。uorm 内部集成了轻量级模板引擎。
use serde::Serialize;
use uorm::driver_manager::U;
#[derive(Serialize)]
struct UserParam<'a> {
name: &'a str,
age: i32,
}
pub async fn add_user() -> Result<u64, uorm::error::DbError> {
let session = U.session().expect("Default driver not found");
// 支持 #{field} 语法绑定参数,内部会自动处理 SQL 注入防护
let affected = session.execute(
"INSERT INTO users(name, age) VALUES (#{name}, #{age})",
&UserParam { name: "Alice", age: 18 }
).await?;
Ok(affected)
}
事务管理
自动事务宏 (#[transaction])
使用 #[transaction] 宏可以简化事务代码:它会在执行函数体前调用 session.begin().await,当函数返回 Ok(_) 时提交事务(commit()),返回 Err(_) 时回滚事务(rollback())。
该宏要求被标注的函数返回 Result<T, E>,并且 E 能从 uorm::error::DbError 转换(即满足 E: From<DbError>),以便将 begin/commit 的错误向外返回。
use serde::Serialize;
use uorm::driver_manager::U;
use uorm::executor::session::Session;
use uorm::error::DbError;
#[derive(Serialize)]
struct MyData {
id: i64,
name: String,
}
#[uorm::transaction]
async fn transfer_data(session: &Session, data: &MyData) -> Result<(), DbError> {
session
.execute("INSERT INTO t(id, name) VALUES (#{id}, #{name})", data)
.await?;
session
.execute("UPDATE t SET name = #{name} WHERE id = #{id}", data)
.await?;
Ok(())
}
#[derive(Serialize)]
struct IdArg {
id: i64,
}
#[uorm::transaction("s")]
async fn custom_session_name(s: &Session) -> Result<(), DbError> {
s.execute("DELETE FROM t WHERE id = #{id}", &IdArg { id: 1 })
.await?;
Ok(())
}
pub async fn run() -> Result<(), DbError> {
let session = U.session().expect("Default driver not found");
transfer_data(
&session,
&MyData {
id: 1,
name: "Alice".to_string(),
},
)
.await?;
custom_session_name(&session).await?;
Ok(())
}
手动管理事务
uorm 使用线程局部存储(Thread Local Storage)管理事务上下文,确保在同一线程内的操作共享同一个事务连接。
async fn manual_transaction() -> Result<(), uorm::error::DbError> {
let session = U.session().expect("Default driver not found");
session.begin().await?;
match do_work(&session).await {
Ok(_) => session.commit().await?,
Err(e) => {
session.rollback().await?;
return Err(e);
}
}
Ok(())
}
XML Mapper 示例
<mapper namespace="user">
<!-- 基本查询 -->
<select id="get_by_id">
SELECT id, name, age FROM users WHERE id = #{id}
</select>
<!-- 动态 SQL:if 标签 -->
<select id="search">
SELECT * FROM users
<where>
<if test="name != null">
AND name LIKE #{name}
</if>
<if test="min_age != null">
AND age >= #{min_age}
</if>
</where>
</select>
<!-- 动态 SQL:foreach 标签 -->
<select id="list_by_ids">
SELECT * FROM users
WHERE id IN
<foreach item="id" collection="ids" open="(" separator="," close=")">
#{id}
</foreach>
</select>
<!-- 插入并获取自增 ID -->
<!-- 当 useGeneratedKeys 为 true 时,execute 将返回最后插入的 ID -->
<insert id="insert_user" useGeneratedKeys="true" keyColumn="id">
INSERT INTO users(name, age) VALUES (#{name}, #{age})
</insert>
</mapper>
高级配置
连接池与超时
use uorm::udbc::ConnectionOptions;
use uorm::udbc::mysql::pool::MysqlDriver;
fn build_mysql_driver() -> Result<MysqlDriver, uorm::error::DbError> {
let options = ConnectionOptions {
max_open_conns: 20,
max_idle_conns: 5,
max_lifetime: 3600,
timeout: 5, // 连接获取超时(秒)
};
MysqlDriver::new("mysql://user:pass@localhost/db")
.options(options)
.build()
}
SQLite 特殊说明
- 并发性:SQLite 驱动默认开启了
WAL模式(Write-Ahead Logging)和foreign_keys支持,显著提升并发读写性能。 - 内存数据库:使用
sqlite::memory:或sqlite://:memory:。注意:当前 SQLite 驱动每次acquire()都会创建新连接;对:memory:而言,这意味着每次都是全新的空库。需要共享状态时建议使用文件数据库(例如sqlite:./app.db)。
日志监控
uorm 使用 log crate 输出详细的执行日志。
fn init_logging() {
// 建议在开发环境下开启 Debug 级别
// 输出示例:
// DEBUG [uorm] Query: sql=SELECT ... WHERE id = ?, params=[("id", I64(1))], elapsed=2ms, rows=1
env_logger::Builder::new().filter_level(log::LevelFilter::Debug).init();
}
License
Apache-2.0
Dependencies
~8–29MB
~379K SLoC