Access VBAの暗黒面を制御せよ:DAOとADOのトランザクション、その「排他制御」の本質
現場で「なぜかレコードが更新できない」「頻繁にデッドロックが発生する」というトラブルに直面したことはないだろうか。Access開発において、DAOとADOのトランザクション挙動を混同したまま設計することは、時限爆弾を抱えてコードを書いているに等しい。
今日は、業務自動化の最前線で戦う諸君に向けて、DAOとADOのトランザクションにおける「ロックの哲学」と、実務で絶対にバグらせないためのアーキテクチャを伝授する。
—
1. DAOとADO、その「トランザクション」の根本的な違い
まず認識すべきは、DAOが「Accessのネイティブな血統」であり、ADOが「汎用的なデータアクセスの巨人」であるという点だ。
- DAO (Data Access Objects): Accessのデータベースエンジン(ACE/Jet)に対して直接命令を下す。トランザクションは「ワークスペース」レベルで管理され、ローカルデータベース(`.accdb`)に対する操作には圧倒的に高速かつ親和性が高い。
- ADO (ActiveX Data Objects): 接続(Connection)単位でトランザクションが完結する。外部SQL Server等との連携を前提としており、接続文字列による制御や、より高度なロックレベルの指定が可能だが、その分オーバーヘッドも大きい。
なぜ「排他制御」で躓くのか
マルチユーザー環境において、DAOはページ単位でのロックを好む傾向があり、ADOは接続先(プロバイダ)の仕様に依存する。トランザクション開始からコミットまでの間、レコードを「占有」しているという意識が希薄だと、他者が触れようとした瞬間に競合が発生する。
—
2. 堅牢な設計のための「トランザクション実装」の極意
トランザクションは「短く、太く」が鉄則だ。長時間開いたままにすれば、それだけデッドロックの確率は指数関数的に上昇する。
DAOによる安全なトランザクション実装(ローカル特化)
DAOでトランザクションを行う際は、必ずエラーハンドラで `Rollback` を呼び出すこと。これを行わないと、ロックが掴まれたままになり、アプリがフリーズしたような状態(ゴーストロック)を引き起こす。
Public Sub ExecuteDAOTransaction()
Dim wrk As DAO.Workspace
Dim db As DAO.Database
Set wrk = DBEngine.Workspaces(0)
Set db = CurrentDb
On Error GoTo ErrorHandler
wrk.BeginTrans ‘ トランザクション開始
‘ ここで複数の更新処理を行う
db.Execute “UPDATE T_Sales SET Status = 1 WHERE Processed = 0”, dbFailOnError
db.Execute “INSERT INTO T_AuditLog (LogDate) VALUES (Now())”, dbFailOnError
wrk.CommitTrans ‘ 成功時に確定
Exit Sub
ErrorHandler:
wrk.Rollback ‘ 失敗時は即座に解放
MsgBox “更新失敗: ” & Err.Description, vbCritical
End Sub
—
3. ADOによるマルチユーザー環境の最適化
SQL Server等と連携するならADO一択だが、ここで重要なのは `LockType` の指定だ。デフォルトの `adLockReadOnly` ではなく、排他制御を考慮した `adLockOptimistic` を正しく選ぶ必要がある。
ADOによるトランザクション(汎用・堅牢)
ADOでは接続の `BeginTrans` を使用する。
Public Sub ExecuteADOTransaction(strConn As String)
Dim conn As Object
Set conn = CreateObject(“ADODB.Connection”)
conn.Open strConn
On Error GoTo ErrorHandler
conn.BeginTrans
‘ レコードセット操作を行う際はLockTypeを指定して競合を防ぐ
Dim rs As Object
Set rs = CreateObject(“ADODB.Recordset”)
rs.Open “SELECT FROM T_Inventory”, conn, 1, 3 ‘ adOpenKeyset, adLockOptimistic
‘ … データ更新処理 …
conn.CommitTrans
Exit Sub
ErrorHandler:
If Not conn Is Nothing Then conn.Rollback
MsgBox “ADOトランザクション異常終了”, vbCritical
End Sub
—
4. デッドロックを回避する「黄金の掟」
現場のリーダーとして、以下の3点を部下に徹底させてほしい。これが守れないコードは、本番環境に出してはならない。
1. ロックの順序を統一せよ: 複数のテーブルを更新する際、常にテーブルA→テーブルBの順でアクセスする。この順序がバラバラだと、デッドロックが確実に発生する。
2. トランザクション内でUI操作を挟むな: `MsgBox` やユーザー入力をトランザクションの真っ只中に入れるのは自殺行為だ。ユーザーが席を外した瞬間にシステム全体がロックされる。
3. 「読み取り」と「書き込み」の分離: 可能であれば、参照系の処理と更新系の処理を分け、更新時のみトランザクションを張る設計に倒す。
終わりに:アーキテクトとしての提言
DAOは「Accessの庭」を管理するには最強のツールだが、拡張性という点ではADOに軍配が上がる。どちらを使うか迷ったときは、「そのデータは将来的にSQL Serverへ移行する可能性があるか?」を自問してほしい。
技術は手段であり、目的は「止まらないシステム」を作ることだ。今日紹介したコードは、あくまで「最低限の防波堤」。これをベースに、貴殿の現場の要件に合わせて、さらに堅牢な例外処理(ログ出力やリトライロジック)を組み込んでいってほしい。
健闘を祈る。
