章: 第2章: データベース応用
振込処理の途中でエラーが起きたら?
Aさんの口座から1000円引いたあと、Bさんへの加算処理でエラーが発生した——。処理をそのまま放置すると「Aさんの残高だけ減って、Bさんには届かない」という最悪の状態になります。
トランザクションを使えば「一連の処理をまとめて成功か失敗」にでき、途中で止まっても自動的に元の状態に戻る(ロールバック)ことを保証できます。
トランザクションの基本実装
<?php
$pdo->beginTransaction();
try {
$pdo->exec("UPDATE accounts SET balance = balance - 1000 WHERE id = 1");
$pdo->exec("UPDATE accounts SET balance = balance + 1000 WHERE id = 2");
$pdo->commit();
} catch (Exception $e) {
$pdo->rollBack();
echo '処理を取り消しました: ' . $e->getMessage();
}
ACID特性とトランザクション
| 特性 | 意味 | トランザクションでの保証 |
| Atomicity(原子性) | 全部成功か全部失敗 | commit / rollBack |
| Consistency(一貫性) | 制約を満たした状態を維持 | DBの制約違反で自動ロールバック |
| Isolation(分離性) | 並列処理でも干渉しない | トランザクション分離レベルで制御 |
| Durability(永続性) | commitした内容は失われない | DBのディスク保存 |
チェックポイント: 「複数のSQL文がセット」で意味をなす処理は必ずトランザクションで囲みましょう。注文処理・在庫更新・決済はその代表例です。
ネストしたトランザクションとセーブポイント
<?php
$pdo->beginTransaction();
try {
$pdo->exec("INSERT INTO orders (user_id, total) VALUES (1, 5000)");
// セーブポイントで部分ロールバックも可能
$pdo->exec("SAVEPOINT before_items");
$pdo->exec("INSERT INTO order_items (order_id, product_id) VALUES (1, 42)");
$pdo->commit();
} catch (Exception $e) {
$pdo->rollBack();
throw $e;
}
チェックポイント:
rollBack()を呼んでも例外を再スローしないと、上位の呼び出し元がエラーを検知できません。必ずthrow $eを忘れずに。
まとめ & 次のステップ
- トランザクションで「複数SQL処理の原子性」を保証する
- エラー発生時は
rollBack()で自動的に元の状態に戻る catchブロックでは必ずロールバック後に例外を再スローする
次回はN+1問題と対策を学びます。ループ内で発生する大量クエリを、JOINで根本的に解決する方法です。