在本教程中,我们将探讨在 Rust 中与关系数据库交互时使用的两个库:Diesel 和 SQLx。
本文将使用一个简单的课堂数据库与学生一起演示每种方法。我们将使用 Diesel ORM 和 SQLx 执行 CRUD 操作。
我们将涵盖的内容:
-
什么是柴油?
-
什么是 SQLx?
-
柴油 ORM 入门
-
使用 Diesel ORM 初始化新项目
-
安装 Diesel CLI
-
设置我们的 Diesel 环境
-
设置 Diesel 迁移
-
使用 Diesel ORM 创建行
-
使用 Diesel ORM 查询 Rust 数据库
-
-
SQLx 入门
-
使用 SQLx 初始化新项目
-
使用 SQLx 和 Rust 更新或删除数据库记录
-
要学习本教程,您需要具备Rust 的工作知识以及访问和使用 Rust、Rust 的构建系统和包管理器 Cargo以及 MySQL 服务器实例的能力。
什么是柴油?
Diesel 是一个支持 PostgreSQL、MySQL、SQLite 的 ORM。ORM 代表对象-关系映射。ORM 帮助面向对象的程序员抽象出关系数据库的细节。
ORM 附带查询构建器,因此您不必担心编写原始 SQL 查询。使用 ORM,您可以与关系数据库进行通信,就好像它们是面向对象的一样。
对于经验不足的开发人员,使用 ORM 可能会更好,因为 ORM 会优化 SQL 查询。ORM 还使您更不容易受到 SQL 注入攻击。
什么是 SQLx?
与 Diesel 不同,SQLx 不是 ORM。SQLx 是一个异步 Rust SQL crate,具有编译时 SQL 查询检查功能。它与数据库和运行时无关。
SQLx 支持连接池、跨平台开发、嵌套池、异步通知、传输层安全性和其他令人兴奋的特性。使用 SQLx 时,您必须自己制作 SQL 查询和迁移。
触及表面之后,让我们探索如何使用 Diesel 和 SQLx 与关系数据库进行交互。
柴油 ORM 入门
以下步骤演示了如何使用使用 Diesel ORM 的 Cargo 设置 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
该命令将帮助我们设置数据库并创建一个空的迁移目录来管理数据库模式。
超过 20 万开发人员使用 LogRocket 来创造更好的数字体验了解更多 →
设置 Diesel 迁移
迁移帮助 ORM 跟踪数据库操作,量子速写(xiezuocat.com),写作猫AI文章生成器,原创文章撰写神器!例如添加字段或删除表。您可以将它们视为数据库的版本控制系统。
首先,让我们使用 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文件用于反转它。
up.sql使用 SQL更新文件以进行迁移:
sql CREATE TABLE students ( id INTEGER AUTO_INCREMENT PRIMARY KEY, firstname VARCHAR(255) NOT NULL, lastname TEXT NOT NULL, age INTEGER NOT NULL );
down.sql用可以反向迁移的 SQL修改文件:
sql DROP TABLE students;
创建up和down迁移后,我们需要在数据库上执行 SQL:
diesel migration run
我们可以开始编写 Rust 来对表执行查询。
使用 Diesel ORM 创建行
让我们编写代码以使用文件中设置的连接字符串建立与 MySQL 服务器的连接.env。
#[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该模型将生成将表上的一行或多行转换为Student Rust 中的结构所需的代码。
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 中写入数据库。了解查询或阅读的工作原理也很重要。
来自 LogRocket 的更多精彩文章:
-
不要错过来自 LogRocket 的精选时事通讯The Replay
-
了解LogRocket 的 Galileo 如何消除噪音以主动解决应用程序中的问题
-
使用 React 的 useEffect优化应用程序的性能
-
在多个 Node 版本之间切换
-
了解如何使用 AnimXYZ 为您的 React 应用程序制作动画
-
探索 Tauri,一个用于构建二进制文件的新框架
-
比较NestJS 与 Express.js
让我们编写一个脚本来查询一个学生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 入门
现在我们知道如何创建一个使用 Diesel ORM 与 Rust 中的数据库交互的项目,让我们看看如何创建一个使用 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 查询。