在本教程中,我们将探讨在Rust中与关系型数据库交互时使用的两个库。Diesel和SQLx。
本文将使用一个有学生的简单教室数据库来演示每种方法。我们将使用Diesel ORM和SQLx进行CRUD操作。
要跟上这个教程,你需要有Rust的工作知识,以及访问和使用Rust、Rust的构建系统和包管理器Cargo的能力,以及一个MySQL服务器实例。
什么是Diesel?
Diesel是一个ORM,支持PostgreSQL、MySQL、SQLite。ORM是对象关系映射的意思。ORM帮助面向对象的程序员抽象出关系数据库的细节。
ORM带有查询生成器,所以你不必担心编写原始SQL查询。使用ORM,你可以与关系型数据库沟通,就像它们是面向对象的一样。
对于经验不足的开发者来说,使用ORM可能会更好,因为ORM会编写优化的SQL查询。ORM也使你不容易受到SQL注入攻击。
什么是SQLx?
与Diesel不同,SQLx不是一个ORM。SQLx是一个异步的Rust SQL crate,具有编译时SQL查询检查功能。它与数据库和运行时间无关。
SQLx支持连接池、跨平台开发、嵌套池、异步通知、传输层安全和其他令人兴奋的功能。当使用SQLx时,你必须自己制作SQL查询和迁移。
在了解了表面情况之后,让我们来探讨一下如何用Diesel和SQLx与关系型数据库进行交互。
开始使用Diesel ORM
下面的步骤展示了如何用Cargo建立一个使用Diesel ORM的Rust项目。
用Diesel ORM初始化一个新项目
你的第一步是通过运行以下命令来初始化项目:
cargo new -- lib classroom_diesel
cd classroom_diesel
在上面的代码中,我们建立了项目并将其命名为classroom_diesel 。新的项目目录应该是这样的:
./
│
├── src/
│ └── lib.rs
│
├── .gitignore
└── Cargo.toml
我们还需要用我们在项目中需要的依赖项来更新Cargo.toml 文件,像这样。
[dependencies]
diesel = { version = "1.4.4", features = ["mysql"] }
dotenv = "0.15.0"
dotenv 依赖关系帮助我们管理项目中的环境变量。
安装Diesel CLI
Diesel使用一个单独的CLI工具。它是一个独立的二进制文件;我们不需要在cargo.toml 文件中把它作为一个依赖项。只需用下面的命令安装它:
cargo install diesel_cli
设置我们的Diesel环境
我们需要在我们的环境中设置一个DATABASE_URL 变量。这就是Diesel如何知道要连接到哪个MySQL数据库:
echo DATABASE_URL=mysql://<username>:<password>@localhost/<database> > .env
编辑连接字符串以匹配你的本地数据库凭证。
你的项目目录现在看起来会是这样的:
./
│
├── src/
│ └── lib.rs
│
├── .env
├── .gitignore
└── Cargo.toml
现在运行以下命令:
diesel setup
这个命令将帮助我们设置数据库,并创建一个空的migrations目录来管理数据库模式。
设置Diesel迁移
迁移可以帮助ORM跟踪数据库的操作,比如增加一个字段或者删除一个表。你可以把它们看作是数据库的一个版本控制系统。
首先,让我们使用Diesel CLI为教室应用程序创建一些迁移。理想情况下,我们应该有一个包含教室学生数据的表。
我们需要创建空的迁移文件,然后用SQL语句填充它们,以创建一个表:
diesel migration generate create_students
你的文件树将看起来与此类似:
./
│
├── migrations/
│ │
│ ├── 2022-07-04-062521_create_students/
│ │ ├── down.sql
│ │ └── up.sql
│ │
│ └── .gitkeep
│
├── src/
│ └── lib.rs
│
├── .env
├── .gitignore
├── Cargo.toml
└── diesel.toml
up.sql 文件是用来创建迁移的,而down.sql 文件是用来反转的。
用迁移的SQL来更新up.sql 文件:
sql
CREATE TABLE students (
id INTEGER AUTO_INCREMENT PRIMARY KEY,
firstname VARCHAR(255) NOT NULL,
lastname TEXT NOT NULL,
age INTEGER NOT NULL
);
用可以逆转迁移的SQL来修改down.sql 文件:
sql
DROP TABLE students;
在创建了up 和down 迁移之后,我们需要在数据库中执行SQL:
diesel migration run
我们可以开始编写Rust来对表进行查询。
用Diesel ORM创建行
让我们编写代码,使用.env 文件中设置的连接字符串建立一个与MySQL服务器的连接:
#[macro_use]
extern crate diesel;
extern crate dotenv;
pub mod models;
pub mod schema;
use diesel::prelude::*;
use dotenv::dotenv;
use std::env;
pub fn create_connection() -> MysqlConnection {
dotenv().ok();
let database_url = env::var("DATABASE_URL").expect("DATABASE_URL must be set");
MysqlConnection::establish(&database_url)
.unwrap_or_else(|_| panic!("Error connecting to {}", database_url))
}
接下来,我们必须为Students 表写一个模型。模型是发生对象-关系映射的地方。模型将生成所需的代码,将Students 表上的一行或多行转换为Rust中的 Student 结构。
cd ./src
touch model.rs
在我们刚刚创建的新的model.rs 文件中,添加以下内容:
use super::schema::students;
#[derive(Queryable)]
pub struct Student {
pub id: i32,
pub firstname: String,
pub lastname: String,
pub age: i32,
}
#[derive(Insertable)]
#[table_name = "students"]
pub struct NewStudent<'a> {
pub firstname: &'a str,
pub lastname: &'a str,
pub age: &'a i32,
}
有了这个模型,来自Students 表的信息将映射到Rust中相应的Student 结构。现在的src 文件夹应该是这样的:
src/
├── lib.rs
├── models.rs
└── schema.rs
现在,我们可以写一个脚本来添加一个学生:
cd src
mkdir bin
cd bin
touch create_students.rs
在create_students.rs 文件中,我们可以调用之前写的模型和函数来创建一个新的学生:
extern crate classroom_diesel;
extern crate diesel;
use self::classroom_diesel::*;
fn main() {
let connection = create_connection();
let firstname = "John";
let lastname = "Doe";
let age: i32 = 64;
let student = create_post(&connection, firstname, lastname, &age);
println!(
"Saved student {} with id {}",
student.firstname, student.id
);
}
项目的结构现在看起来将类似于这样:
./
│
├── migrations/
│ │
│ ├── 2022-07-04-062521_create_students/
│ │ ├── down.sql
│ │ └── up.sql
│ │
│ └── .gitkeep
│
├── src/
│ │
│ ├── bin/
│ │ └── create_students.rs
│ │
│ ├── lib.rs
│ ├── models.rs
│ └── schema.rs
│
├── .env
├── .gitignore
├── Cargo.lock
├── Cargo.toml
└── diesel.toml
使用以下命令执行新的脚本:
cargo run --bin create_students
正如你在下面的图片中看到的,John 的新学生文件已经被保存为id ,即1 。我们可以使用这个id 来查询Rust数据库,我们将在下一节中看一下:

使用Diesel ORM查询Rust数据库
在上一节中,我们回顾了如何使用Diesel ORM在Rust中写进数据库。了解查询或阅读的工作方式也是非常重要的。
让我们写一个脚本来查询一个学生,他的id 是1 。首先,创建一个query_students.rs 文件:
cd bin
touch query_students.rs
然后,在我们刚刚创建的query_students.rs 文件中,添加以下内容:
extern crate classroom_diesel;
extern crate diesel;
use self::models::*;
use classroom_diesel::*;
use diesel::prelude::*;
fn main() {
use self::schema::students::dsl::*;
let connection = create_connection();
let result = students
.filter(id.eq(1))
.load::<Student>(&connection)
.expect("Error loading students");
println!(
"Student: {} {} {} years",
result[0].firstname, result[0].lastname, result[0].age
);
}
执行该脚本:
cargo run --bin query_students
正如你在下面的图片中所看到的,结果是一个打印行,包含了我们从数据库中查询的学生文件的名、姓和年龄。

开始使用SQLx
现在我们知道了如何在Rust中创建一个使用Diesel ORM与数据库交互的项目,让我们来看看如何创建一个使用SQLx的项目来代替。
用SQLx初始化一个新项目
首先运行下面的命令:
cargo new classroom_sqlx --bin
然后,在cargo.toml 文件中添加所需的依赖项:
[dependencies]
sqlx = { version = "0.5", features = [ "runtime-async-std-native-tls", "mysql" ] }
async-std = { version = "1", features = [ "attributes" ] }
这就是你在设置方面所需要的。很简单,对吗?
要使用SQLx与Rust中的数据库进行交互,我们所要做的就是编写一些SQL查询和Rust代码。在Diesel ORM部分,我们创建并读取了一条学生记录;在本节中,我们将编写查询来更新和删除一条记录。
使用SQLx和Rust来更新或删除数据库记录
首先,我们需要写一些Rust代码来连接SQLx和MySQL服务器:
//main.rs
use sqlx::mysql::MySqlPoolOptions;
#[async_std::main]
async fn main() -> Result<(), sqlx::Error> {
let pool = MySqlPoolOptions::new()
.max_connections(7)
.connect("mysql://root:@localhost/classroom_diesel")
.await?;
Ok(())
}
SQLx支持有准备和无准备的SQL查询。准备好的SQL查询是对SQL注入的厌恶。
让我们看看如何更新一个主键为1的记录的名和姓:
use sqlx::mysql::MySqlPoolOptions;
#[async_std::main]
async fn main() -> Result<(), sqlx::Error> {
let pool = MySqlPoolOptions::new()
.max_connections(5)
.connect("mysql://root:@localhost/classroom_diesel")
.await?;
sqlx::query("UPDATE students SET firstname=?, lastname=? WHERE id=?")
.bind("Richard")
.bind("Roe")
.bind(1)
.execute(&pool)
.await?;
Ok(())
}
用下面的命令执行该脚本:
cargo run
删除记录的方式也很相似,唯一的区别是SQL查询:
use sqlx::mysql::MySqlPoolOptions;
#[async_std::main]
async fn main() -> Result<(), sqlx::Error> {
let pool = MySqlPoolOptions::new()
.max_connections(5)
.connect("mysql://root:@localhost/classroom_diesel")
.await?;
sqlx::query("DELETE FROM students WHERE id=?")
.bind(1)
.execute(&pool)
.await?;
Ok(())
}
用下面的命令执行该脚本。
cargo run
现在你可以使用Diesel或SQLx与Rust中的数据库进行交互。
总结
像Diesel这样的ORM是足够的;它们可以帮助你生成一些你需要的SQL。大多数时候,你的应用程序中只需要足够的东西。
然而,在更广泛的应用中,可能需要更多的 "魔法"--换句话说,你的时间和精力--来使ORM正确工作并生成高性能的SQL查询。
如果需要创建更复杂的查询,并有高容量和低延迟的要求,使用SQLx等库来执行原始SQL查询可能会更好。