Rust 中使用原生 SQL 与 SQLx
当谈到使用 SQL 时,Rust 生态系统让我们有太多的选择。 SQLx 是一个纯粹的异步、与运行时无关的 Rust SQL 包,它允许您在没有 DSL 的情况下使用编译时类型检查的查询。作为 Rust 中使用 SQL 的最流行方法之一,它具有以下优点:
- 它与您最喜欢的所有 SQL 风格(MySQL、SQLite、Postgres)兼容
- 编译时检查查询确保类型和查询的有效性
- 支持 Postgres 监听/通知等额外功能
- 构建和使用查询的多种不同方法
- 您还可以使用 SQLx 创建自己的查询生成器!
让我们看看 SQLx 的实际应用!
入门
首先,您需要将 sqlx
添加到 Rust 程序中:
cargo add sqlx
您还需要安装官方 SQLx CLI sqlx-cli
,它可以帮助您更轻松地管理迁移等。您可以通过运行以下命令来安装它:
cargo install sqlx-cli
迁移
第一步:迁移。如果您愿意,您可以自己手动创建表 - 但这会花费大量时间和精力…并且您需要记住您做了什么!值得庆幸的是,我们可以编写 .sql
文件来表示我们的迁移,然后通过 sqlx-cli
或使用 sqlx::execute
命令将它们迁移到我们正在使用的任何数据库。一个简单的 SQL 模式可能如下所示:
-- this only creates a table if it doesn't exist, avoiding the issue of tables being wiped
CREATE TABLE IF NOT EXISTS foo (
id SERIAL PRIMARY KEY,
message TEXT
);
只要它是有效的 SQL,无论您决定使用哪种方法都会成功,并将在数据库中创建一个 _sqlx_migrations
表,其中包含已应用的迁移的列表。
应用内迁移命令可能如下所示:
pool.execute(include_str!("../schema.sql"))
.await
.context("Failed to initialize DB")?;
作为个人建议,我使用 sqlx-cli
并使用 sqlx migrate -r add <filename>
。此命令本质上添加了一个新的迁移,但是 -r
标志允许您在出现问题时随时恢复迁移。如果在将新的迁移部署到生产环境后出现任何问题,这是一种能够恢复事物的便捷方法。
查询
默认情况下,您可以通过快速运行查询然后使用连接池执行它来使用原始 SQL 查询:
let query = sqlx::query("SELECT * FROM TABLE")
.execute(&pool)
.await
.unwrap();
默认情况下,SQLx 提倡使用绑定参数,这对于防止 SQL 注入非常重要 - 您只需将它们添加到查询中即可做到这一点(在此处找到有关此内容的更多信息):
sqlx::query("INSERT INTO TABLE (foo) VALUES ($1)")
.bind("bar".to_string())
.execute(&pool)
.await
.unwrap();
现在假设您正在编写一个返回某些内容的查询。当您从该查询中获取行时,您很可能必须单独获取每个值 - 返回数量少时很好,但是当您使用 fetch_all
时,您必须制作一个迭代器从每一行获取您需要的内容。方便的是,SQLx 知道这一点,并且幸运地为我们提供了一个宏,以便我们能够从 SQL 行向量中提取结构体向量 - 您可以使用 query_as
将返回结果绑定到使用 #[derive(Sqlx::FromRow)]
。
你会像这样使用它:
#[derive(sqlx::FromRow)]
struct Foo {
id: i32,
message: String
}
async fn foo(pool: PgPool) -> Vec<Foo> {
let res = sqlx::query_as::<_, Foo>("SELECT * FROM FOO")
.fetch_all(&pool)