Accessの真髄:DAOトランザクションによる「不可分な整合性」の鉄則
Access VBAにおける開発において、`DoCmd.RunSQL`や安易な`Recordset.Update`の羅列は、システムを「壊れるべくして壊れる状態」に追い込む最大の要因だ。
特に、受注データと在庫データのように、複数のテーブルを跨いで整合性を保つ必要がある処理において、トランザクション管理を怠ることは、プロフェッショナルとしてあってはならない。今回は、DAO(Data Access Objects)のトランザクションを極限まで制御し、堅牢なシステムを構築するためのアーキテクチャを解説する。
—
1. トランザクション管理の基本原則:Workspaceオブジェクト
Access VBAでトランザクションを扱う際、`CurrentDb`のメソッドを直接叩くのではなく、明示的に`Workspace`オブジェクトを制御すべきだ。これにより、データベースエンジンレベルでの一貫性が保証される。
以下に、実戦でそのまま使える、エラーハンドリングを完備したテンプレートを提示する。
実装コード:堅牢なトランザクションパターン
Public Sub ExecuteAtomicUpdate()
Dim ws As DAO.Workspace
Dim db As DAO.Database
Dim rsOrder As DAO.Recordset
Dim rsStock As DAO.Recordset
‘ デフォルトのワークスペースを取得
Set ws = DBEngine.Workspaces(0)
Set db = CurrentDb
‘ トランザクション開始
ws.BeginTrans
On Error GoTo RollbackHandler
‘ 1. 受注テーブル更新
Set rsOrder = db.OpenRecordset(“T_Orders”, dbOpenDynaset)
rsOrder.AddNew
rsOrder!OrderID = 101
rsOrder.Update
‘ 2. 在庫テーブル更新
Set rsStock = db.OpenRecordset(“T_Stock”, dbOpenDynaset)
rsStock.Edit
rsStock!Quantity = rsStock!Quantity – 1
rsStock.Update
‘ 全て成功したらコミット
ws.CommitTrans
GoTo Cleanup
RollbackHandler:
‘ 致命的なエラー時は即座にロールバック
ws.Rollback
MsgBox “更新に失敗しました。データベースは更新前の状態に復元されました。” & vbCrLf & _
“Error: ” & Err.Description, vbCritical
Cleanup:
‘ オブジェクトの明示的解放(メモリリークの防止)
If Not rsOrder Is Nothing Then rsOrder.Close: Set rsOrder = Nothing
If Not rsStock Is Nothing Then rsStock.Close: Set rsStock = Nothing
Set db = Nothing
Set ws = Nothing
End Sub
—
2. シニアエンジニアが知るべき「見えない負荷」
このコードを見て「`Set db = Nothing` は不要ではないか?」と思った者は、まだ甘い。VBAにおいてオブジェクトの破棄をランタイムのGC(ガベージコレクション)に任せるのは、メモリリークを放置するのと同義だ。
メモリと参照のライフサイクル
Accessのランタイムは、特に大規模なレコードセットを扱う際、メモリを食い潰す傾向がある。`Recordset`をクローズし、かつ`Nothing`を代入することで、COM参照カウントを確実にデクリメントしなければならない。
Windows APIとの連携:パフォーマンスの極致
もし、トランザクション中に重い計算や外部API連携を挟む場合、Accessの画面描画や操作を受け付けない「フリーズ状態」になることがある。これを避けるには、`DoEvents`を適切に配置するのも手だが、さらに高度に制御するなら、Windows APIの `Sleep` を用いてCPUサイクルを意図的に開放し、OS側のレスポンスを維持する設計が求められる。
‘ 宣言部
Private Declare PtrSafe Sub Sleep Lib “kernel32” (ByVal dwMilliseconds As Long)
‘ ループ処理の合間に
Sleep 10 ‘ 10msの解放でOSの応答を維持
—
3. レガシー環境での注意点:ロックの階層
DAOのトランザクションは、裏で「ページロック」や「行ロック」を発生させる。マルチユーザー環境のAccess(特にファイル共有ベースのMDE/ACCDE)では、トランザクションの範囲が長すぎると、他のユーザーを完全にロックアウトしてしまう。
- 鉄則1:トランザクションは可能な限り短く保つ。
- 外部API通信や、時間のかかる計算は、`BeginTrans`の外で行い、DB書き込みのみを`BeginTrans`と`CommitTrans`で囲むこと。
- 鉄則2:デッドロックのリスクを考慮する。
- 複数のテーブルを更新する場合、常に「テーブルをロックする順番」を全モジュールで統一せよ。Aテーブル→Bテーブルと更新するコードと、Bテーブル→Aテーブルと更新するコードが混在すると、デッドロックの温床となる。
—
結論:コードは「意図」を語るべきだ
Access VBAは「手軽なツール」ではない。適切に設計されたVBAコードは、エンタープライズなシステムと同様の信頼性を担保できる。今回紹介した`Workspace`による管理と、明示的なオブジェクト解放は、大規模システムを運用する上での「最低限の作法」だ。
次にコードを書くとき、あなたはただ「動くコード」を書くのか、それとも「何があっても整合性を守り抜くシステム」を構築するのか。その選択が、エンジニアとしての価値を決定づける。
