19个不稳定版本 (7个破坏性更新)
新功能 0.11.0 | 2024年8月18日 |
---|---|
0.10.0 | 2024年5月14日 |
0.9.0 | 2023年11月16日 |
0.8.0 | 2023年10月25日 |
0.0.0 | 2023年10月9日 |
654 在 数据库接口
每月147次 下载
38KB
232 行
Exemplar
rusqlite的样板代码消除器。
入门指南
一窥你可以做什么
#[derive(Debug, PartialEq, Model)]
#[table("users")]
#[check("../tests/schema.sql")]
struct User {
username: String,
#[bind(bind_path)]
#[extr(extr_path)]
home_dir: PathBuf,
#[column("pwd")]
password: Vec<u8>,
}
fn main() -> Result<()> {
let conn = Connection::open_in_memory()?;
conn.execute_batch(
include_str!("../tests/schema.sql")
)?;
let alice = User {
username: "Alice".to_owned(),
home_dir: "/var/home/alice".into(),
password: b"hunter2".to_vec()
};
let bob = User {
username: "Bob".to_owned(),
home_dir: "/var/home/robert".into(),
password: b"password".to_vec()
};
alice.insert(&conn)?;
bob.insert(&conn)?;
let mut stmt = conn.prepare("
SELECT * FROM users ORDER BY username ASC
")?;
let mut iter = stmt.query_and_then([], User::from_row)?;
assert_eq!(alice, iter.next().unwrap()?);
assert_eq!(bob, iter.next().unwrap()?);
Ok(())
}
功能
- 与原始SQL一起工作,而不是与之对抗。
- 轻量级,零成本API。
- Exemplar的大部分内容围绕
Model
特质展开,该特质在运行时被内联和单态化。生成的代码大致是你手动使用纯rusqlite
时编写的。 - 设计为即插即用;尽可能重用
rusqlite
的现有类型,包括其Result
类型别名。 - 支持任何可以
Deref
到rusqlite::Connection
的类型,例如事务或池连接。
- Exemplar的大部分内容围绕
- 可选测试推导,以防止数据库模式和Rust模型类型之间的漂移。
- 用于处理与SQL兼容的
enum
和映射到特定查询的"匿名"记录类型的宏。 - 在运行时对
dyn Model
进行反射/工作的某些能力。
如果你只需要使用sqlite
进行一些Rust数据的CRUD操作,而不需要完整的ORM或企业级DBMS,那么Exemplar适合你!
常见问题解答
"Exemplar不能做什么?"
一些关键事项
- 模式生成和管理。示例程序明确不是ORM,且在没有陷入ORM领域的情况下,很难表示外键和迁移等概念。
- 如果您“必须”使用它,请查看
diesel
或sqlx
/seaorm
,它们都支持SQLite。
- 如果您“必须”使用它,请查看
- 查询生成(不包括
INSERT
。) - 接口可移植性。仅支持
rusqlite
。
"它是否非常快?"
是的。在我的机器上(根据这些基准测试),Exemplar可以
- 在~600纳秒内插入一个非平凡的模型类型(每秒1.6百万行)
- 查询并重建相同的类型在~9微秒内(每秒111,000行,使用
SELECT * LIMIT 1
)
显然,这种速度的功劳应归于SQLite和rusqlite
的开发者,但我可以自信地说,我没有减慢事情的速度!
"这与serde-rusqlite
相比如何?"
serde_rusqlite
是一个巧妙的技巧,但它仍然涉及太多的扭曲和样板代码——这就是为什么我创建了Exemplar。
我试图解决的一些痛点
- 需要分配和操作一个
String
列名切片,以有效地反序列化行——这可能是由于serde
的限制?- Exemplar静态地知道应期望哪些列,因此
from_row
不需要额外的输入,也不进行任何多余的分配。
- Exemplar静态地知道应期望哪些列,因此
- 无字段的
enum
的设计选择——它们被无效率地序列化为TEXT
而不是INTEGER
。这对于调试来说很方便,但我认为更快的选项应该是Exemplar的默认设置。 to_params_named(&row1).unwrap().to_slice().as_slice()
- 等同于在Exemplar中
row1.insert(&conn)
或row1.insert_with(&stmt)
。
- 等同于在Exemplar中
- 编译和运行时都会出现一般的
serde
开销。- 基准测试显示,与Exemplar相比,
serde_rusqlite
在插入操作上慢了约25%。 - 检索操作同样快速,可能是因为最终转换步骤与查询计算和I/O相比微不足道。
- 基准测试显示,与Exemplar相比,
致谢
rusqlite
,为这个库提供了基础。- David Tolnay,为他各种各样的proc宏
咒语crate。
依赖关系
~23MB
~434K SLoC