データベース連携(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フィードバックを取得できます。ローカル設定は不要です。
このコースのすべてのレッスン
- Actix-web/RocketでREST APIを構築する
- データベース連携(SQLx/Diesel)
- 認証と認可