0Pricing
Learn Rust Coding · 课时

数据库集成(SQLx/Diesel)

使用 SQLx 或 Diesel 等常用对象关系映射工具或查询构建器,将 Rust Web 服务接入数据库。

数据库集成(SQLx/Diesel) 是 CoddyKit 上的免费 Learn Rust Coding 课时。 这是第 2 节课,共 3 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Learn Rust Coding 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Learn Rust Coding 课程共包含 3 节课。

为什么 Web 应用需要数据库?

Web 服务通常需要持久化存储和检索数据。想一想用户资料、产品目录或博客文章——这些数据需要存放在程序内存之外的某个地方。

数据库提供了一种结构化的数据管理方式,确保数据安全、一致且可访问。对于大多数实际应用程序而言,将 Rust Web 服务与数据库集成是一项核心任务。

SQLx:您的异步数据库助手

在 Rust 中,我们有许多优秀的数据库交互工具。两个常用的选择是 SQLx 和 Diesel。

  • SQLx:一个异步的纯 Rust SQL crate,其查询会在编译时进行检查。对于使用 Tokio 等框架构建的异步 Web 服务,它通常是首选。
  • Diesel:一个功能强大的 ORM(对象关系映射器),提供了更抽象的数据库交互方式,可以将 Rust 结构体直接映射到数据库表。它通常是同步的。

在本课程中,我们将重点介绍 SQLx,因为它对异步操作提供了强有力的支持,非常符合现代 Rust Web 服务的开发方式。

SQLx 设置:依赖项

在编写代码之前,您需要将 sqlx 和 tokio 添加到项目的 Cargo.toml 中。tokio 提供了 sqlx 所需的异步运行时。

例如,要使用 SQLite(一个适合本地开发的基于文件的简易数据库)和 SQLx,您的 Cargo.toml 可能如下所示:

[dependencies] sqlx = { version = "0.7", features = ["runtime-tokio-rustls", "sqlite"] } tokio = { version = "1", features = ["full"] }

此外,您通常还会使用 DATABASE_URL 环境变量来配置数据库连接字符串。

连接到您的第一个数据库

让我们连接到一个内存中的 SQLite 数据库。SQLx 使用连接池,高效地管理多个数据库连接,以处理 Web 服务中的并发请求。

请尝试运行以下示例:

use sqlx::SqlitePool;
use tokio;

#[tokio::main]
async fn main() -> Result<(), sqlx::Error> {
    // Use an in-memory SQLite database for simplicity
    let database_url = "sqlite::memory:"; 
    let pool = SqlitePool::connect(&database_url).await?;
    println!("Successfully connected to the database!");

    // In a real web app, 'pool' would be passed around or stored in app state.

    Ok(())
}

使用表组织数据

在存储数据之前,我们需要使用 DDL(数据定义语言)定义数据库架构。让我们创建一个简单的 tasks 表。

sqlx::query! 宏允许您直接将 SQL 嵌入 Rust 代码中。它甚至会在编译时检查您的 SQL!

use sqlx::{SqlitePool, query};
use tokio;

#[tokio::main]
async fn main() -> Result<(), sqlx::Error> {
    let database_url = "sqlite::memory:";
    let pool = SqlitePool::connect(&database_url).await?;

    // Create a 'tasks' table if it doesn't exist
    query!(
        "CREATE TABLE IF NOT EXISTS tasks (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            description TEXT NOT NULL,
            completed BOOLEAN NOT NULL DEFAULT FALSE
        )"
    ).execute(&pool).await?;

    println!("Table 'tasks' created successfully!");
    Ok(())
}

添加新记录

现在我们已经有了一个表,让我们添加一些数据!我们将结合参数绑定使用 INSERT 语句。这可以防止 SQL 注入,让您的查询更加安全。

请注意,参数使用 ? 占位符,值则传递给 .bind()。

use sqlx::{SqlitePool, query};
use tokio;

#[tokio::main]
async fn main() -> Result<(), sqlx::Error> {
    let database_url = "sqlite::memory:";
    let pool = SqlitePool::connect(&database_url).await?;

    query!(
        "CREATE TABLE IF NOT EXISTS tasks (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            description TEXT NOT NULL,
            completed BOOLEAN NOT NULL DEFAULT FALSE
        )"
    ).execute(&pool).await?;

    // Insert a new task
    let description = "Learn SQLx";
    let completed = false;
    query!(
        "INSERT INTO tasks (description, completed) VALUES (?, ?)",
        description,
        completed
    ).execute(&pool).await?;

    println!("Task '{}' inserted successfully!", description);
    Ok(())
}

使用 SELECT 获取数据

要从 tasks 表中获取数据,我们使用 SELECT 语句。SQLx 提供了 .fetch_all() 和 .fetch_one() 等方法来获取结果。

您可以遍历返回的行,并通过名称或索引访问列。

use sqlx::{SqlitePool, query};
use tokio;

#[tokio::main]
async fn main() -> Result<(), sqlx::Error> {
    let database_url = "sqlite::memory:";
    let pool = SqlitePool::connect(&database_url).await?;

    query!(
        "CREATE TABLE IF NOT EXISTS tasks (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            description TEXT NOT NULL,
            completed BOOLEAN NOT NULL DEFAULT FALSE
        )"
    ).execute(&pool).await?;

    query!(
        "INSERT INTO tasks (description, completed) VALUES (?, ?)",
        "Learn SQLx", false
    ).execute(&pool).await?;
    query!(
        "INSERT INTO tasks (description, completed) VALUES (?, ?)",
        "Build a web app", false
    ).execute(&pool).await?;

    // Fetch and print all tasks
    println!("\n--- All Tasks ---");
    let rows = query!("SELECT id, description, completed FROM tasks")
        .fetch_all(&pool).await?;

    for row in rows {
        println!("ID: {}, Desc: {}, Done: {}", 
            row.id, row.description, row.completed);
    }
    Ok(())
}

使用结构体实现类型安全的数据处理

手动从行中提取数据容易出错。SQLx 的 query_as! 宏结合 #[derive(FromRow)],可以将查询结果直接映射到 Rust 结构体中!

这能提供强大的类型安全性,并让代码更加整洁、易于使用。

use sqlx::{FromRow, SqlitePool, query, query_as};
use tokio;

#[derive(Debug, FromRow)]
struct Task {
    id: i64,
    description: String,
    completed: bool,
}

#[tokio::main]
async fn main() -> Result<(), sqlx::Error> {
    let database_url = "sqlite::memory:";
    let pool = SqlitePool::connect(&database_url).await?;

    query!(
        "CREATE TABLE IF NOT EXISTS tasks (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            description TEXT NOT NULL,
            completed BOOLEAN NOT NULL DEFAULT FALSE
        )"
    ).execute(&pool).await?;

    query!(
        "INSERT INTO tasks (description, completed) VALUES (?, ?)",
        "Learn SQLx", false
    ).execute(&pool).await?;

    // Fetch tasks directly into our Task struct
    println!("\n--- Tasks as Structs ---");
    let tasks = query_as::<_, Task>("SELECT id, description, completed FROM tasks")
        .fetch_all(&pool).await?;

    for task in tasks {
        println!("{:?}", task);
    }
    Ok(())
}

修改与删除记录

更新和删除数据的方式与插入类似。您需要结合参数绑定使用 UPDATE 和 DELETE SQL 语句。

  • UPDATE:根据 WHERE 子句修改现有行。
  • DELETE:根据 WHERE 子句删除行。

使用 UPDATE 和 DELETE 时,请务必谨慎编写 WHERE 子句,以免意外丢失数据!

use sqlx::{SqlitePool, query};
use tokio;

#[tokio::main]
async fn main() -> Result<(), sqlx::Error> {
    let database_url = "sqlite::memory:";
    let pool = SqlitePool::connect(&database_url).await?;

    query!(
        "CREATE TABLE IF NOT EXISTS tasks (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            description TEXT NOT NULL,
            completed BOOLEAN NOT NULL DEFAULT FALSE
        )"
    ).execute(&pool).await?;

    query!(
        "INSERT INTO tasks (description, completed) VALUES (?, ?)",
        "Finish lesson", false
    ).execute(&pool).await?;
    let task_id_to_update = 1;

    // Update a task
    query!(
        "UPDATE tasks SET completed = ? WHERE id = ?",
        true,
        task_id_to_update
    ).execute(&pool).await?;
    println!("Task {} updated.", task_id_to_update);

    // Delete a task (uncomment to run, careful with IDs after update)
    // let task_id_to_delete = 1;
    // query!(
    //     "DELETE FROM tasks WHERE id = ?",
    //     task_id_to_delete
    // ).execute(&pool).await?;
    // println!("Task {} deleted.", task_id_to_delete);

    Ok(())
}

检验您的数据库技能

以下哪些陈述是关于 Rust Web 服务中 SQLx 的 TRUE 说法?

数据库集成:要点回顾

通过学习数据库集成,您在构建稳健的 Rust Web 服务方面迈出了一大步!

  • 持久化:数据库对于存储应用程序数据至关重要。
  • SQLx:一个异步且在编译时进行检查的 SQL crate,非常适合 Web 服务。
  • 连接池:对于高效并发地访问数据库至关重要。
  • CRUD:我们介绍了创建(INSERT)、获取(SELECT)、更新(UPDATE)和删除(DELETE)数据。
  • 类型安全:query_as! 和 #[derive(FromRow)] 可以实现安全且便捷的数据映射。

接下来,您可以探索如何将这些数据库操作集成到实际的 Web 路由中!

常见问题解答

「数据库集成(SQLx/Diesel)」课时是免费的吗?

是的 — 「数据库集成(SQLx/Diesel)」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Learn Rust Coding 课程的其余内容,请升级到 CoddyKit PRO。 Learn Rust Coding 课程共包含 3 节课。

「数据库集成(SQLx/Diesel)」这节课中我会学到什么?

使用 SQLx 或 Diesel 等常用对象关系映射工具或查询构建器,将 Rust Web 服务接入数据库。 你通过在浏览器中直接运行的动手代码来练习 Learn Rust Coding,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 Learn Rust Coding 需要有经验吗?

无需任何先前经验。CoddyKit 上的 Learn Rust Coding 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 2 节课,共 3 节。

「数据库集成(SQLx/Diesel)」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 Learn Rust Coding 课中编写并运行代码吗?

能。每节 Learn Rust Coding 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. 使用 Actix-web/Rocket 构建 REST API
  2. 数据库集成(SQLx/Diesel)
  3. 身份验证与授权
← 返回 Learn Rust Coding