0Pricing
Learn Rust Coding · レッスン

データベース連携(SQLx/Diesel)

SQLxやDieselなどの一般的なORMまたはクエリビルダーを使い、Rustのウェブサービスをデータベースと連携させます。

「データベース連携(SQLx/Diesel)」はCoddyKit上の無料Learn Rust Codingレッスンです。 これはレッスン2/3です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはLearn Rust Coding学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Learn Rust Codingコースには全3レッスンが含まれています。

Webサービスにデータベースを使う理由

Webサービスでは、データを永続的に保存し、取得する必要がよくあります。ユーザープロファイル、商品カタログ、ブログ記事などを考えてみてください。これらのデータは、プログラムのメモリ以外の場所に保存する必要があります。

データベースは、このようなデータを構造化して管理する方法を提供し、安全性、一貫性、アクセス性を確保します。RustのWebサービスをデータベースと統合することは、現実の多くのアプリケーションで中核となる作業です。

SQLx:非同期DBの頼れる味方

Rustには、データベース操作に使える優れたツールがあります。代表的な選択肢はSQLxとDieselです。

  • SQLx:コンパイル時にクエリをチェックできる、非同期対応の純粋なRust製SQLクレートです。Tokioのようなフレームワークで構築する非同期Webサービスでよく選ばれます。
  • Diesel:データベースをより抽象的に操作できる強力なORM(Object-Relational Mapper)です。Rustの構造体をデータベースのテーブルへ直接マッピングできます。通常は同期的に動作します。

このレッスンでは、現代的なRustのWebサービス開発と相性のよい、強力な非同期サポートを備えたSQLxに焦点を当てます。

SQLxのセットアップ:依存関係

コードを書く前に、プロジェクトのCargo.tomlへsqlxとtokioを追加する必要があります。tokioは、sqlxに必要な非同期ランタイムを提供します。

たとえば、SQLxでSQLite(ローカル開発に適したファイルベースのデータベース)を使う場合、Cargo.tomlは次のようになります。

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

また通常は、データベース接続文字列を設定するためにDATABASE_URL環境変数も使用します。

最初のDBに接続する

インメモリ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(Data Definition Language)を使ってデータベーススキーマを定義する必要があります。単純な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(())
}

DBスキルをテスト

RustのWebサービスにおけるSQLxについて、次の文のうち正しいものはどれですか。

DB統合:重要なポイント

データベース統合を学び、堅牢なRust Webサービスの構築に向けて大きく前進しました。

  • 永続性:データベースはアプリケーションデータの保存に不可欠です。
  • SQLx:非同期対応でコンパイル時にチェックされるSQLクレートであり、Webサービスに適しています。
  • コネクションプール:効率的で並行性の高いデータベースアクセスに不可欠です。
  • CRUD:データの作成(INSERT)、取得(SELECT)、更新(UPDATE)、削除(DELETE)を扱いました。
  • 型安全性:query_as!と#[derive(FromRow)]によって、安全で使いやすいデータマッピングを実現できます。

次は、これらのデータベース操作を実際のWebルートへ統合してみましょう。

よくある質問

「データベース連携(SQLx/Diesel)」レッスンは無料ですか?

はい。「データベース連携(SQLx/Diesel)」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Learn Rust Codingコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Learn Rust Codingコースには全3レッスンが含まれています。

「データベース連携(SQLx/Diesel)」で何を学びますか?

SQLxやDieselなどの一般的なORMまたはクエリビルダーを使い、Rustのウェブサービスをデータベースと連携させます。 ブラウザで直接実行するハンズオンコードでLearn Rust Codingを演習し、24時間対応の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に戻る