データの整合性を守る「盾」:DAOトランザクションと鉄壁のエラーハンドリング
こんにちは。Access VBAの世界へようこそ。
マクロの記録から一歩踏み出し、「自分でコードを書いて制御したい」と思い立ったあなた。その意欲こそが、エンジニアとしての第一歩です。素晴らしいですね。
しかし、プログラムが複雑になればなるほど、避けて通れない「恐ろしい問題」に直面することになります。それは、「処理の途中でエラーが起き、データが中途半端に書き換わってしまう」という事態です。
例えば、銀行の振込処理を想像してください。「Aさんの口座から1万円引く」処理は成功したのに、その直後の「Bさんの口座に1万円足す」処理でエラーが起きて止まってしまったら……? Aさんの1万円は、世界のどこにも存在しない幽霊のようなデータになってしまいます。
これを防ぐための技術が「トランザクション」です。今日は、プロの開発者が必ず使いこなしている、DAOを用いたトランザクション処理と、絶対にデータを壊さないためのエラーハンドリングの極意を伝授します。
ここをクリアすれば、あなたのAccessアプリの信頼性は、プロの領域へと一気に引き上げられるはずですよ。
1. トランザクションとは「全か無か」の約束
トランザクションとは、一言で言えば「複数の処理を一つのまとまりとして扱うこと」です。
エンジニアの世界では、これを「原子性(Atomicity)」と呼びます。
「全ての処理が完璧に成功するか、さもなくば、最初から何もしていなかった状態に戻るか」のどちらかしかない、という極めて厳格なルールです。
- Commit(コミット): 全ての処理が成功したので、変更を確定させる。
- Rollback(ロールバック): 途中でエラーが起きたので、全ての変更を取り消して元の状態に戻す。
この「Commit」と「Rollback」を正しく制御することが、データベースの整合性を守るための唯一の手段なのです。
2. 【実践】鉄壁のトランザクション・テンプレート
それでは、実際に現場でそのまま使える「黄金のテンプレート」を見ていきましょう。
DAO(Data Access Objects)を使用して、複数のテーブルを更新するシナリオを想定しています。
Option Compare Database
Option Explicit
''' <summary>
''' 複数のテーブルを更新するトランザクション処理の例
''' </summary>
Public Sub ExecuteSafeTransaction()
' --- 1. オブジェクトの宣言 ---
Dim db As DAO.Database
Dim ws As DAO.Workspace ' トランザクション管理にはWorkspaceが必要!
' --- 2. エラーハンドリングの設定 ---
' エラーが発生したら「ErrorHandler」ラベルへジャンプするように指示
On Error GoTo ErrorHandler
' --- 3. 初期設定 ---
' DAOのデフォルトのWorkspaceを取得
Set ws = DBEngine.Workspaces(0)
Set db = CurrentDb
' --- 4. トランザクションの開始 ---
' ここから「Commit」か「Rollback」されるまでの一連の処理が「一つの塊」になります
ws.BeginTrans
Debug.Print "トランザクションを開始しました。"
' --- 5. メインの業務ロジック(ここを書き換えて使用) ---
' 例:在庫テーブルを更新
db.Execute "UPDATE T_Inventory SET StockCount = StockCount - 1 WHERE ProductID = 101", dbFailOnError
Debug.Print "在庫を減らしました。"
' (ここで何らかの予期せぬエラーが発生したと仮定...)
' 例:売上テーブルにデータを追加
db.Execute "INSERT INTO T_Sales (ProductID, SaleDate) VALUES (101, Date())", dbFailOnError
Debug.Print "売上を記録しました。"
' --- 6. 全て成功した場合の確定処理 ---
' ここまでエラーなく到達できれば、全ての変更がデータベースに書き込まれます
ws.CommitTrans
MsgBox "処理が正常に完了しました。", vbInformation, "成功"
' --- 7. 終了処理(正常終了・エラー終了共通) ---
ExitProc:
' オブジェクトの解放(メモリリークを防ぐために重要!)
Set db = Nothing
Set ws = Nothing
Exit Sub
' --- 8. エラーハンドリング(エラー発生時にジャンプ) ---
ErrorHandler:
' エラーが発生した場合、まずRollbackを試みる
' wsがセットされている(=トランザクション中である)ことを確認
If Not ws Is Nothing Then
ws.Rollback
Debug.Print "エラーが発生したため、ロールバックを実行しました。"
End If
' エラー内容をユーザーに通知(実務ではログ出力なども行う)
MsgBox "致命的なエラーが発生しました。処理を中断し、データを元に戻しました。" & vbCrLf & _
"エラー番号: " & Err.Number & vbCrLf & _
"エラー内容: " & Err.Description, vbCritical, "エラー発生"
' 終了処理へ向かう
Resume ExitProc
End Sub
3. アーキテクトの視点:なぜこのコードなのか?
ただコードを写すだけではもったいない。なぜこの書き方が「正解」なのか、その理由(重み)を理解しましょう。
① `Workspace` オブジェクトの重要性
ここが最大の落とし穴です。多くの初心者は `Database` オブジェクトに対してトランザクションをかけようとしますが、DAOにおいてトランザクションの管理権限を持っているのは `Workspace` です。
`ws.BeginTrans` と書くことで、そのWorkspaceが管理するすべてのデータベース操作が、一つのユニットとして束ねられます。
② `dbFailOnError` オプションの徹底
`db.Execute` を使う際、必ず末尾に `dbFailOnError` を付けてください。
これがないと、例えば「型が違う」「制約違反」といったエラーが起きても、VBAはエラーを吐かずに「無視して次の行へ」進んでしまいます。トランザクションの「全か無か」という哲学を貫くためには、エラーを確実にキャッチするこのオプションが必須です。
③ `On Error GoTo` と `Resume ExitProc` の構造
エラーが起きた時、いきなりプログラムを終了させてはいけません。
1. Rollback でデータを守る
2. Cleanup (Set Nothing) でメモリを掃除する
3. Exit で安全に抜ける
この「後片付けのフロー」を確立することが、システムを長時間安定して動かすためのプロの作法です。
4. よくある失敗と回避策
開発中に陥りやすい「罠」をまとめておきます。
| 失敗パターン | 起こる現象 | 回避策 |
|---|---|---|
| Rollbackを忘れる | データベースがロックされたままになり、他のユーザーが操作できなくなる。 | `ErrorHandler` 内で必ず `ws.Rollback` を実行する。 |
| エラー処理の中でさらにエラーが出る | プログラムがクラッシュし、不完全な状態で止まる。 | `ErrorHandler` 内の処理は極力シンプルにし、複雑なロジックを書かない。 |
| 変数のスコープミス | `ws` が `ErrorHandler` から見えず、Rollbackできない。 | `ws` はプロシージャの先頭で宣言し、適切な範囲(スコープ)で管理する。 |
まとめ:整合性を守れるエンジニアへ
トランザクションとエラーハンドリングをマスターすることは、単に「エラーを防ぐ」ことではありません。「ユーザーの大切なデータを、いかなる状況下でも守り抜く」という責任を果たすことなのです。
最初は少し記述量が多く感じるかもしれません。しかし、このテンプレートを自分の「型」として体に叩き込んでください。そうすれば、どれほど複雑な業務ロジックを組むことになっても、あなたは自信を持って「このシステムは安全だ」と言えるようになります。
さあ、次のステップへ進みましょう。あなたのコードが、より強固で美しいものになることを応援しています!
