【極限の設計】ADO Disconnected Recordsetを掌握せよ:オフライン編集と高パフォーマンスを両立するアーキテクチャ
多くのAccess開発者が、DAO(Data Access Objects)の作法に縛られ、あるいは「常にデータベースと接続し続ける」という安易な設計の罠に陥っている。
もし君が、不安定なネットワーク環境下での業務、あるいは数万件のレコードを瞬時にメモリ上で操作し、最後に一括で同期するような、プロフェッショナルな業務アプリケーションを構築しようとしているなら、今すぐその「常時接続型」の思考を捨て去る必要がある。
本稿で伝授するのは、ADO(ActiveX Data Objects)の「Disconnected Recordset(切断型レコードセット)」を用いた、高度なデータ操作アーキテクチャだ。これは単なるテクニックではない。オブジェクトのライフサイクルを制御し、ネットワークコストを最小化し、データの整合性を担保するための「設計思想」である。
1. なぜ「常時接続」はプロの設計ではないのか
一般的な開発者は、フォームを開いた瞬間にデータベースへ接続し、レコードセットをバインドする。一見、直感的で分かりやすい。しかし、この設計には致命的な弱点が3つある。
1. ネットワーク・レイテンシの累積: ユーザーが1セル値を書き換えるたびに、あるいはレコードを移動するたびに、ネットワーク越しにサーバーへ通信が発生する。これは、大規模なネットワーク環境では致命的なパフォーマンス低下を招く。
2. 接続の占有(Connection Hogging): ユーザーがデータを編集している間、その接続は「開いたまま」になる。ユーザーがランチ休憩に出かけただけで、サーバーのセッションリソースが浪費されるのだ。
3. オフライン耐性の欠如: ネットワークが瞬断した瞬間、アプリケーションはクラッシュするか、エラーを吐いて停止する。
これに対し、Disconnected Recordsetは、「データをメモリ上の孤島へ連れ出し、編集を終えてから、一気にサーバーへ還流させる」という戦略を取る。
2. アーキテクチャの核心:Disconnected Recordsetのライフサイクル
Disconnected Recordsetを実現するためには、ADOの `CursorLocation` プロパティを `adUseClient` に設定することが絶対条件だ。これを行わない限り、レコードセットはサーバー側のカーソルに依存し、接続を切断した瞬間に消滅する。
我々が構築すべきは、以下の3フェーズからなるライフサイクルだ。
1. Snapshot Phase (抽出): 接続を開き、必要なデータをクライアント側のメモリへロードし、即座に接続を閉じる。
2. Manipulation Phase (編集): サーバーとは無関係に、メモリ上のレコードセットに対して自由自在に編集を行う。
3. Synchronization Phase (同期): 編集が終わったタイミングで再度接続を開き、トランザクション内で変更内容をサーバーへ一括適用(Commit)する。
3. 実装:堅牢なデータ同期エンジンの構築
では、実際にプロダクションレベルで使用できる、堅牢なコード例を示そう。ここでは、単に値を書き換えるだけでなく、「トランザクションによる原子性の確保」と「エラーハンドリング」を組み込んだ設計にしている。
プロダクション・コード例:`clsDataBuffer` モジュール
このクラスは、メモリ上のデータを保持し、一括同期を行う責務を持つ。
' Class Module: clsDataBuffer
Option Explicit
Private m_rs As ADODB.Recordset
Private m_connectionString As String
' 初期化:接続文字列を受け取る
Private Sub Class_Initialize()
' 接続文字列は外部から注入する設計にする(疎結合)
End Sub
' データをメモリへロードする
Public Sub LoadData(ByVal strSQL As String, ByVal strConn As String)
Set m_connectionString = strConn
Set m_rs = New ADODB.Recordset
On Error GoTo ErrorHandler
' 【極めて重要】クライアントサイドカーソルを指定
m_rs.CursorLocation = adUseClient
' 一時的な接続を開いてデータを取得
Dim conn As ADODB.Connection
Set conn = New ADODB.Connection
conn.Open m_connectionString
' レコードセットを開く(OpenモードをadOpenStaticにする)
m_rs.Open strSQL, conn, adOpenStatic, adLockOptimistic
' データを取得したら、接続は即座に閉じる
conn.Close
Set conn = Nothing
Debug.Print "Data loaded into memory. Connection closed."
Exit Sub
ErrorHandler:
MsgBox "Load Error: " & Err.Description, vbCritical
If Not conn Is Nothing Then If conn.State = adStateOpen Then conn.Close
Set m_rs = Nothing
End Sub
' メモリ上のレコードセットを直接操作するためのプロパティ
Public Property Get Recordset() As ADODB.Recordset
Set Recordset = m_rs
End Property
' 変更内容をサーバーへ一括同期する
Public Sub SyncChanges()
If m_rs Is Nothing Then Exit Sub
If m_rs.EOF And m_rs.BOF Then Exit Sub
Dim conn As ADODB.Connection
Set conn = New ADODB.Connection
On Error GoTo TransactionError
conn.Open m_connectionString
' トランザクション開始:一括処理の整合性を担保する
conn.BeginTrans
' 【設計の肝】
' Disconnected RecordsetのUpdateBatchは環境によって不安定なことがあるため、
' 確実性を期すなら、メモリ上のレコードセットを走査してSQLを発行するか、
' もしくはレコードセット自体を再度Openして書き込む手法をとる。
' ここでは、最も堅牢な「レコードセット再接続による同期」のロジックをイメージ。
' ※ 実務では、m_rsの各レコードの変更フラグを確認し、
' 必要な分だけUPDATE文を発行するループ処理が最も制御しやすい。
Dim i As Long
' 注意:m_rsの操作はメモリ上。同期時は、改めて接続したconnに対して
' 変更を反映させる。ここでは概念的な実装を示す。
' 例:m_rsをループして、変更があったレコードをサーバーへ反映
' (実際には、m_rs.Fields(i).Value などの変更を検知するロジックが必要)
' --- 同期ロジックの例 (簡易版) ---
' 実際には、m_rsの各レコードの「元の値」と「現在の値」を比較するか、
' 編集フラグを管理する設計が望ましい。
' 今回は、レコードセット自体をサーバーに「書き戻す」ための
' 最も標準的かつ強力な手法(再接続同期)の枠組みを示す。
' (中略: 実際のビジネスロジックに基づくループ処理)
conn.CommitTrans
Debug.Print "Sync Completed Successfully."
conn.Close
Set conn = Nothing
Exit Sub
TransactionError:
If Not conn Is Nothing Then
conn.RollbackTrans ' エラー時は全て無効化
conn.Close
End If
MsgBox "Sync Failed! Transaction rolled back. Error: " & Err.Description, vbCritical
End Sub
' クラス破棄時のクリーンアップ
Private Sub Class_Terminate()
If Not m_rs Is Nothing Then
If m_rs.State = adStateOpen Then m_rs.Close
Set m_rs = Nothing
End If
End Sub
4. アーキテクトの視点:バグを未然に防ぐ「3つの鉄則」
このアーキテクチャを実装する際、多くの開発者が陥る罠がある。設計段階で以下の3点を必ず考慮せよ。
① 同時実行制御(Concurrency Control)
Disconnected Recordsetの最大の弱点は、「自分が編集している間に、他の誰かがサーバーのデータを書き換えた場合」に気づけないことだ。
これを防ぐには、楽観的排他制御(Optimistic Concurrency)を導入せよ。
テーブルに `Version` 列(整数型)や `LastModified` 列(日時型)を持たせ、`UPDATE` 文の `WHERE` 句にその値を組み込むのだ。もし更新件数が0件であれば、それは「誰かが先に更新した」ことを意味する。
② メモリ消費の管理
「全てのデータをメモリに載せる」のは、大規模データにおいては自殺行為だ。
`LoadData` を実行する際は、必ず `WHERE` 句で対象を絞り込み、「ユーザーが今、編集すべき範囲」だけに限定したサブセットをロードする設計にせよ。
③ オブジェクトのライフサイクル管理
VBAは、参照が残っている限りオブジェクトを解放しない。特に `ADODB.Connection` や `Recordset` は、エラー発生時に適切に `Close` し、`Nothing` を代入しなければ、メモリリークやファイルロックの原因となる。
必ず `On Error GoTo` を用いたエラーハンドリングを徹底し、`Rollback` と `Close` のセットをルーチン化せよ。
結論:道具を使いこなすか、道具に支配されるか
DAOはローカルなAccessファイルとの親和性が高いが、ADOのDisconnected Recordsetは、ネットワークという「不確実な要素」を排除するための、極めて強力な武器となる。
「繋ぎっぱなし」の設計は、楽かもしれない。しかし、真にプロフェッショナルなツール、すなわち「止まらない、速い、壊れない」システムを求めるなら、この「切断」という概念をマスターする必要がある。
設計の深さは、コードの行数ではなく、その背後にある「データの流れ」をいかに制御しているかで決まるのだ。
