0Pricing
Learn Rust Coding · レッスン

CRUD操作

クエリと実行

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

sqlxによるCRUD

CRUDはCreate、Read、Update、Deleteを意味します。sqlxでは、これらを2種類の実行方法に分けています。

  • 行を返すクエリにはfetch_*
  • データを変更する文にはexecute

Create(INSERT)

バインドパラメーターを使って行を挿入し、executeで実行します。生成されたIDを取得するにはRETURNINGを使います。

use sqlx::{Pool, Postgres};

async fn create(pool: &Pool<Postgres>, name: &str) -> i32 {
    let rec = sqlx::query!(
        "INSERT INTO users (name) VALUES ($1) RETURNING id",
        name
    ).fetch_one(pool).await.unwrap();
    rec.id
}

1行を読み取る

fetch_oneは行がちょうど1つ返ることを想定し、それ以外の場合はエラーになります。行が存在しない可能性がある場合はfetch_optionalを使います。

use sqlx::{Pool, Postgres};

async fn find(pool: &Pool<Postgres>, id: i32) -> Option<String> {
    let rec = sqlx::query!("SELECT name FROM users WHERE id = $1", id)
        .fetch_optional(pool)
        .await
        .unwrap();
    rec.map(|r| r.name)
}

複数行を読み取る

fetch_allは、条件に一致するすべての行をVecとして返します。

use sqlx::{Pool, Postgres};

async fn all_names(pool: &Pool<Postgres>) -> Vec<String> {
    let rows = sqlx::query!("SELECT name FROM users ORDER BY id")
        .fetch_all(pool)
        .await
        .unwrap();
    rows.into_iter().map(|r| r.name).collect()
}

Update

UPDATE文ではexecuteを使います。executeは、影響を受けた行数を含む結果を返します。

use sqlx::{Pool, Postgres};

async fn rename(pool: &Pool<Postgres>, id: i32, name: &str) -> u64 {
    let res = sqlx::query!("UPDATE users SET name = $1 WHERE id = $2", name, id)
        .execute(pool)
        .await
        .unwrap();
    res.rows_affected()
}

Delete

DELETEでもexecuteを使います。rows_affected()を確認すると、実際に削除されたかどうかが分かります。

use sqlx::{Pool, Postgres};

async fn delete(pool: &Pool<Postgres>, id: i32) -> bool {
    let res = sqlx::query!("DELETE FROM users WHERE id = $1", id)
        .execute(pool)
        .await
        .unwrap();
    res.rows_affected() > 0
}

RustだけでCRUDをモデル化する

メモリ上のマップに対するCRUDは、sqlxがテーブルに対して行う同じ操作を再現します。

use std::collections::HashMap;

fn main() {
    let mut users: HashMap<i32, String> = HashMap::new();
    users.insert(1, "Alice".to_string());      // create
    println!("{:?}", users.get(&1));            // read
    users.insert(1, "Alicia".to_string());      // update
    println!("{:?}", users.get(&1));
    users.remove(&1);                            // delete
    println!("{:?}", users.get(&1));
}

トランザクション

トランザクションを使うと、複数の文をアトミックにまとめられます。commitですべて成功するか、どれも適用されないかのどちらかになります。

use sqlx::{Pool, Postgres};

async fn transfer(pool: &Pool<Postgres>) {
    let mut tx = pool.begin().await.unwrap();
    sqlx::query!("UPDATE acct SET bal = bal - 10 WHERE id = 1")
        .execute(&mut *tx).await.unwrap();
    sqlx::query!("UPDATE acct SET bal = bal + 10 WHERE id = 2")
        .execute(&mut *tx).await.unwrap();
    tx.commit().await.unwrap();
}

ロールバック

トランザクションガードがcommitなしで破棄されると、sqlxは自動的にロールバックします。rollbackを明示的に呼び出すこともできます。

use sqlx::{Pool, Postgres};

async fn maybe(pool: &Pool<Postgres>, ok: bool) {
    let mut tx = pool.begin().await.unwrap();
    sqlx::query!("INSERT INTO log (msg) VALUES ('x')").execute(&mut *tx).await.unwrap();
    if ok { tx.commit().await.unwrap(); } else { tx.rollback().await.unwrap(); }
}

大量の読み取りをストリーミングする

非常に大きな結果セットでは、fetchがストリームを返します。すべてをメモリに読み込まずに行を処理できます。

use sqlx::{Pool, Postgres};
use futures::TryStreamExt;

async fn stream(pool: &Pool<Postgres>) {
    let mut rows = sqlx::query!("SELECT id FROM big_table").fetch(pool);
    while let Some(row) = rows.try_next().await.unwrap() {
        println!("id {}", row.id);
    }
}

エラー処理

データベース呼び出しはResultを返します。Result<_, sqlx::Error>を返す関数では、アンラップする代わりに?でエラーを伝播させます。

use sqlx::{Pool, Postgres};

async fn count(pool: &Pool<Postgres>) -> Result<i64, sqlx::Error> {
    let rec = sqlx::query!("SELECT COUNT(*) as n FROM users")
        .fetch_one(pool)
        .await?;
    Ok(rec.n.unwrap_or(0))
}

理解度チェック

CRUD操作についての理解度を確認しましょう。

まとめ

sqlxによるCRUDについて学びました。

  • fetch_one、fetch_optional、fetch_allで行を読み取ります
  • executeでデータを変更し、rows_affected()で影響を受けた行数を確認します
  • RETURNINGで生成された値を取得します
  • begin/commit/rollbackによるトランザクションでアトミック性を保証します

次は、スキーマを発展させるマイグレーションです。

よくある質問

「CRUD操作」レッスンは無料ですか?

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

「CRUD操作」で何を学びますか?

クエリと実行 ブラウザで直接実行するハンズオンコードでLearn Rust Codingを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

Learn Rust Codingを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのLearn Rust Codingは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン3/4です。

「CRUD操作」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このLearn Rust Codingレッスンでコードを書いて実行できますか?

はい。すべてのLearn Rust Codingレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. データベースへの接続
  2. コンパイル時に検査されるクエリ
  3. CRUD操作
  4. マイグレーション
← Learn Rust Codingに戻る