Diesel

Diesel是Rust的安全,可扩展的ORM和查询生成器 Diesel是与Rust中数据库交互的最有效方式,因为它对查询的安全和可组合抽象。

为何创造Diesel

1:防止运行时错误

我们不想浪费时间追踪运行时错误。 我们通过让Diesel消除编译时不正确的数据库交互的可能性来实现这一目标。

2:专为性能而打造

Diesel提供了一个高级查询构建器,让您可以考虑Rust中的问题,而不是SQL。 我们专注于零成本抽象,使Diesel能够以比C更快的速度运行查询并加载数据。

3:富有成效和可扩展性

与Active Record和其他ORM不同,Diesel旨在被抽象化。 Diesel使您能够编写可重用的代码并根据问题域而不是SQL进行思考。

示例

简单的查询

  1. 简单的查询是一件轻而易举的事。 从数据库加载所有用户:

rust代码:

  1. users::table.load(&connection)

生成SQL:

  1. SELECT * FROM users;
  1. 加载用户的所有帖子:

rust代码:

  1. Post::belonging_to(user).load(&connection)

生成SQL:

  1. SELECT * FROM posts WHERE user_id = 1;

复杂查询

Diesel强大的查询构建器可帮助您以0的成本构建简单或复杂的查询。

rust代码:

  1. let versions = Version::belonging_to(krate)
  2. .select(id)
  3. .order(num.desc())
  4. .limit(5);
  5. let downloads = try!(version_downloads
  6. .filter(date.gt(now - 90.days()))
  7. .filter(version_id.eq(any(versions)))
  8. .order(date)
  9. .load::<Download>(&conn));

生成SQL:

  1. SELECT version_downloads.*
  2. WHERE date > (NOW() - '90 days')
  3. AND version_id = ANY(
  4. SELECT id FROM versions
  5. WHERE crate_id = 1
  6. ORDER BY num DESC
  7. LIMIT 5
  8. )
  9. ORDER BY date

更少样板

Diesel codegen为您生成样板。 它使您可以专注于业务逻辑,而不是映射到SQL行和从SQL行映射。

这意味着你可以这样写:

  1. #[derive(Queryable)]
  2. pub struct Download {
  3. id: i32,
  4. version_id: i32,
  5. downloads: i32,
  6. counted: i32,
  7. date: SystemTime,
  8. }

而不用这样写

  1. pub struct Download {
  2. id: i32,
  3. version_id: i32,
  4. downloads: i32,
  5. counted: i32,
  6. date: SystemTime,
  7. }
  8. impl Download {
  9. fn from_row(row: &Row) -> Download {
  10. Download {
  11. id: row.get("id"),
  12. version_id: row.get("version_id"),
  13. downloads: row.get("downloads"),
  14. counted: row.get("counted"),
  15. date: row.get("date"),
  16. }
  17. }
  18. }

插入数据

  1. 这不仅仅是阅读数据。 diesel使结构易于使用新记录。

rust代码:

  1. #[derive(Insertable)]
  2. #[table_name="users"]
  3. struct NewUser<'a> {
  4. name: &'a str,
  5. hair_color: Option<&'a str>,
  6. }
  7. let new_users = vec![
  8. NewUser { name: "Sean", hair_color: Some("Black") },
  9. NewUser { name: "Gordon", hair_color: None },
  10. ];
  11. insert_into(users)
  12. .values(&new_users)
  13. .execute(&connection);

生成SQL:

  1. INSERT INTO users (name, hair_color) VALUES
  2. ('Sean', 'Black'),
  3. ('Gordon', DEFAULT)
  1. 如果您需要你插入行的数据,只需将execute更改为get_resultget_results。 diesel将负责其余的工作。

rust代码:

  1. let new_users = vec![
  2. NewUser { name: "Sean", hair_color: Some("Black") },
  3. NewUser { name: "Gordon", hair_color: None },
  4. ];
  5. let inserted_users = insert_into(users)
  6. .values(&new_users)
  7. .get_results::<User>(&connection);

生成SQL:

  1. INSERT INTO users (name, hair_color) VALUES
  2. ('Sean', 'Black'),
  3. ('Gordon', DEFAULT)
  4. RETURNING *

更新数据

Diesel的codegen可以生成多种更新行的方法,让您以对应用程序有意义的方式封装逻辑。

修改结构

  1. post.published = true;
  2. post.save_changes(&connection);

一次性批量更改SQL

  1. update(users.filter(email.like("%@spammer.com")))
  2. .set(banned.eq(true))
  3. .execute(&connection)

使用结构进行封装SQL

  1. update(Settings::belonging_to(current_user))
  2. .set(&settings_form)
  3. .execute(&connection)

符合人体工程学的原始SQL

总会有某些查询更容易编写为原始SQL,或者无法使用查询构建器表示。 即使在这些情况下,Diesel也提供了一个易于使用的API来编写原始SQL。

  1. #[derive(QueryableByName)]
  2. #[table_name = "users"]
  3. struct User {
  4. id: i32,
  5. name: String,
  6. organization_id: i32,
  7. }
  8. // Using `include_str!` allows us to keep the SQL in a
  9. // separate file, where our editor can give us SQL specific
  10. // syntax highlighting.
  11. sql_query(include_str!("complex_users_by_organization.sql"))
  12. .bind::<Integer, _>(organization_id)
  13. .bind::<BigInt, _>(offset)
  14. .bind::<BigInt, _>(limit)
  15. .load::<User>(conn)?;