概要
「送金処理」を例に考える: 口座 A から 1000 円引いて、口座 B に 1000 円足す。これは 2 つの UPDATE で実現するが、1 つ目だけ成功して 2 つ目で失敗したら 1000 円が消える。「全部成功するか・全部失敗するか」を DB に宣言できる仕組みが トランザクション だ。
PHP では $pdo->beginTransaction() で「ここから先は一塊」と宣言し、すべて問題なければ $pdo->commit() で確定、途中で例外が出たら $pdo->rollBack() で すべて巻き戻す。try { begin → SQL → commit } catch { rollBack → throw } のテンプレが定石で、exception トピックで学んだ例外処理と完全にセットで使う。
保証される性質は ACID で、特に重要なのが A (Atomicity) = 「全部か無か」の原子性。送金・複数テーブル一括 INSERT・複数行 UPDATE のように 論理的に 1 つの操作 をやるときは必ずトランザクションで括る。逆に SELECT だけや単一行更新ならトランザクション不要 (DB が 1 文単位で原子的に処理する)。
学習目標
beginTransaction()で「ここから先は一塊」と宣言できるcommit()で確定、rollBack()で取り消し- 途中で例外が出たら 全部巻き戻す ことでデータの一貫性を守れる
- 「複数の更新を全部成功させたい」ときに使う理由を説明できる
ドリル
| no | 内容 |
|---|---|
| 01 | 2 件 INSERT を 1 トランザクションで実行 → commit → SELECT 確認 |
| 02 | 1 件目成功 → 2 件目で例外 → rollBack → 何も追加されていない |
📝 演習: ドリル 01 — 2 件 INSERT を commit
問題
accounts テーブルに次の 2 件を 1 つのトランザクションで INSERT してから commit し、
最後に "commit" と出力し、続けて id 昇順で "{id}: {balance}" を全件出してください。
追加する 2 件:
id = 10, balance = 1000id = 11, balance = 2000
初期データは空 (テーブルだけある状態)。
期待される出力:
commit
10: 1000
11: 2000ヒント
$pdo->beginTransaction();でトランザクション開始$pdo->exec("INSERT INTO accounts (id, balance) VALUES (10, 1000)");を 2 回$pdo->commit();で確定echo "commit\n";を出す- 最後に
SELECT id, balance FROM accounts ORDER BY idを foreach で出力
📝 演習: ドリル 02 — エラーで rollBack
問題
accounts テーブルに 2 件 INSERT しようとして、2 件目で意図的に失敗 させ、
rollBack で全部巻き戻したい。最後に SELECT したとき何も追加されていないことを示す。
INSERT 内容:
- 1 件目:
id = 10, balance = 1000→ 成功するはず - 2 件目:
id = 10, balance = 2000→ PK 重複 で例外発生
try ... catch (PDOException $e) で例外を捕まえて rollBack を呼び、
そのとき "rollback" と出力してください。
その後、SELECT で残った行を id 昇順で "{id}: {balance}" 形式で出す。
初期データは空。 rollBack されたので 1 件目も取り消され、SELECT は 0 件。
期待される出力:
rollbackヒント
$pdo->beginTransaction();で開始try { ... $pdo->commit(); } catch (PDOException $e) { $pdo->rollBack(); echo "rollback\n"; }- 2 件目の INSERT で同じ
id = 10を入れれば PK 重複で例外 - catch を抜けた後に SELECT で残りを foreach 出力 (0 件なので何も出ない)