「トランザクションって何で要るんだっけ」を、自分の言葉で腹落ちさせた学習メモです。
綴Photo は「写真に気持ちを綴る」アプリなので、1枚保存するときに DB の操作が2つ走ります。photos テーブルに写真を入れて、meanings(綴り)テーブルにその言葉を入れます。この2つを素朴に順番でやると、途中で失敗したときに困る状態が生まれます。そこを埋めるのがトランザクションだった。まとめておく。
2つの操作が、途中で失敗すると
順番に実行するだけだと、こうなり得ます。
制約違反・ストレージ不足・処理の途中でアプリが落ちるなど、2回目の INSERT が通らないことは起き得ます。「言葉を綴るためのアプリ」で、言葉のない写真だけが取り残されます。これは見た目には分かりにくい、想定していない中途半端な状態で、後から一覧やリレーションを辿ったときに効いてきます。
ひとまとまりに包む
写真と綴りの INSERT を、同じトランザクションの中に入れます。
dart
// 写真 + 綴り を1つのトランザクションで保存する
await transaction(() async {
final photoId = await _insertPhoto(...); // 写真を INSERT
await _insertMeaning(photoId, ...); // 綴りを INSERT
});学びポイント
- ユーザーから見た「1回の保存」が、内部では複数の INSERT に分かれることがあります。そういう時はトランザクションで原子性を担保します
- 写真(親)だけ残って綴り(子)がない行は、その場では気づきにくく、後から一覧・集計で写真と綴りをつないで辿ろうとしたときに効いてきます。最初から作らないのが一番安いです
- 「全部成功か、全部失敗か」をコードで保証できると、途中失敗の場合分けを自分で抱えなくて済みます
おわりに
トランザクションは名前だけ知っていて、「DBの安全機能」くらいの解像度で止まっていた。でも綴Photo の「写真+綴り=1セット」という具体に当てはめたら、何を守っているのかがはっきりした。守っているのは速さでも便利さでもなく、「綴りのない写真」みたいな中途半端な状態を存在させないことだった。
この記事は2026年6月時点の自分の理解で書いています。仕様や認識が変わってる可能性があります。