拓展Rust编程边界:Diesel——安全且可扩展的ORM和查询构建器
Diesel是Rust语言中一款卓越的ORM(对象关系映射)框架和查询构建器,旨在消除数据库交互的冗余代码,并在不牺牲性能的同时,通过充分利用Rust强大的类型系统,提供一种“Rust范”的低开销查询体验。
Diesel支持以下三种数据库:
- PostgreSQL
- MySQL
- SQLite
只需在Cargo.toml
中配置相应的后端特性即可:
[dependencies]
diesel = { version = "<version>", features = ["<postgres|mysql|sqlite>"] }
开始探索
详细的入门教程可在Diesel官网找到。关于更多特定功能的指南也将陆续发布。
获取帮助
如果你遇到问题,Diesel的Gitter聊天室非常活跃。你可以在这里寻求即时帮助:gitter.im/diesel-rs/diesel。对于更深入的问题讨论或Diesel的未来发展建议,欢迎在GitHub Discussions上开启讨论。
使用示例
简单查询
执行简单的数据库查询变得轻而易举。如需加载所有用户:
users::table.load(&mut connection)
对应的SQL语句为:
SELECT * FROM users;
查询指定用户的帖子:
Post::belonging_to(user).load(&mut connection)
对应的SQL语句为:
SELECT * FROM posts WHERE user_id = 1;
复杂查询
Diesel强大的查询构造器允许你在无需额外成本的情况下创建任意复杂的查询。
let versions = Version::belonging_to(krate)
.select(id)
.order(num.desc())
.limit(5);
let downloads = version_downloads
.filter(date.gt(now - 90.days()))
.filter(version_id.eq_any(versions))
.order(date)
.load::<Download>(&mut conn)?;
对应的SQL语句为:
SELECT version_downloads.*
WHERE date > (NOW() - '90 days')
AND version_id = ANY(
SELECT id FROM versions
WHERE crate_id = 1
ORDER BY num DESC
LIMIT 5
)
ORDER BY date
减少样板代码
Diesel的代码生成工具能为你自动生成大量的样板代码,使你专注于业务逻辑而非与SQL行的转换。
这意味着你可以这样编写:
#[derive(Queryable, Selectable)]
#[diesel(table_name = downloads)]
pub struct Download {
id: i32,
version_id: i32,
downloads: i32,
counted: i32,
date: SystemTime,
}
而不是没有Diesel时的这样:
pub struct Download {
id: i32,
version_id: i32,
downloads: i32,
counted: i32,
date: SystemTime,
}
impl Download {
fn from_row(row: &Row) -> Download {
Download {
id: row.get("id"),
version_id: row.get("version_id"),
downloads: row.get("downloads"),
counted: row.get("counted"),
date: row.get("date"),
}
}
}
插入数据
Diesel使得使用结构体创建新记录变得简单。
#[derive(Insertable)]
#[diesel(table_name = users)]
struct NewUser<'a> {
name: &'a str,
hair_color: Option<&'a str>,
}
let new_users = vec![
NewUser { name: "Sean", hair_color: Some("Black") },
NewUser { name: "Gordon", hair_color: None },
];
insert_into(users)
.values(&new_users)
.execute(&mut connection);
对应的SQL语句为:
INSERT INTO users (name, hair_color) VALUES
('Sean', 'Black'),
('Gordon', DEFAULT)
如果需要获取插入的数据,只需要将execute
改为get_result
或get_results
,其他工作就交给Diesel处理。
let new_users = vec![
NewUser { name: "Sean", hair_color: Some("Black") },
NewUser { name: "Gordon", hair_color: None },
];
let inserted_users = insert_into(users)
.values(&new_users)
.get_results::<User>(&mut connection);
对应的SQL语句为:
INSERT INTO users (name, hair_color) VALUES
('Sean', 'Black'),
('Gordon', DEFAULT)
RETURNING *
更新数据
Diesel的代码生成工具可以生成多种更新行的方法,让你可以根据应用逻辑来封装你的代码。
通过修改结构体:
post.published = true;
post.save_changes(&mut connection);
一次性批量更改:
update(users.filter(email.like("%@spammer.com")))
.set(banned.eq(true))
.execute(&mut connection)
利用结构体进行封装:
update(Settings::belonging_to(current_user))
.set(&settings_form)
.execute(&mut connection)
原生SQL
有些查询更适合直接使用原生SQL编写,或者无法用查询构建器表达。即使在这种情况下,Diesel也提供了一个方便使用的API用于编写原生SQL。
#[derive(QueryableByName)]
#[diesel(table_name = users)]
struct User {
id: i32,
name: String,
organization_id: i32,
}
// 将SQL放在单独文件中,让编辑器提供SQL特定的语法高亮。
sql_query(include_str!("complex_users_by_organization.sql"))
.bind::<Integer, _>(organization_id)
.bind::<BigInt, _>(offset)
.bind::<BigInt, _>(limit)
.load::<User>(&mut conn)?;
行为准则
所有与Diesel互动的人,无论是在哪个平台,都必须遵守我们的行为准则。
许可证
Diesel遵循以下许可之一:
- Apache许可证,版本2.0,(LICENSE-APACHE或https://www.apache.org/licenses/LICENSE-2.0)
- MIT许可证 (LICENSE-MIT或https://opensource.org/licenses/MIT)
贡献代码
在贡献之前,请阅读贡献者指南,了解有关如何在本地设置Diesel、编码风格以及常用缩写的有用信息。
除非您明确表示,否则您有意提交的任何贡献(适用于Apache-2.0许可定义的工作),都将按照上述条款双授权,而不附加任何其他条款或条件。