【入門編】DAOのUpdateメソッド失敗時のロールバック:エラー発生時のデータ不整合を防ぐ定型句 – Access VBA解析バイブル

スポンサーリンク
スポンサーリンク

「半分だけ更新されたデータ」という悪夢を防ぐ。Access VBAでトランザクションを使いこなす極意

こんにちは。業務自動化の世界へようこそ。
マクロの記録から一歩踏み出し、VBAという「魔法の杖」を手に取ったあなた。素晴らしい決断です。

しかし、コードが書けるようになった先に、エンジニアを震え上がらせる恐ろしい現象が待ち受けています。それは、「データが中途半端に書き換わってしまう」という現象です。

例えば、銀行の振込処理を想像してみてください。
「Aさんの口座から1万円引く」という処理は成功した。でも、その直後の「Bさんの口座に1万円足す」という処理で、ネットワークエラーや予期せぬエラーが起きて止まってしまったら……?

Aさんの1万円は、この世から消えてしまいますよね。これが、データベースにおける「不整合」という名の悪夢です。

今日は、この悪夢を未然に防ぐための最強の武器、「トランザクション(ロールバック)」の使い方を伝授します。ここをマスターすれば、あなたのプログラムの信頼性はプロの領域へと跳ね上がりますよ。

1. 「トランザクション」とは何か?

プログラミングの世界には、「All or Nothing(すべてか、無か)」という鉄則があります。

一連の更新処理(例えば、在庫を減らす、売上を作る、履歴を残す、といった複数のステップ)を、「一つのまとまり」として扱う考え方をトランザクションと呼びます。

トランザクションには、3つの重要な状態があります。

1. BeginTrans(開始): 「今から一連の作業を始めるよ。まだ確定(保存)はしないよ」という合図。
2. CommitTrans(確定): 「すべての作業が完璧に終わった! これでデータベースに永久に保存してくれ!」という命令。
3. Rollback(取り消し): 「ああっ、エラーだ! 今までの作業は全部なかったことにして、作業開始前の状態に戻してくれ!」という命令。

この「Rollback」こそが、データ不整合を防ぐ守護神なのです。

2. DAOにおけるトランザクションの構造

Access VBAでデータを操作する際、私たちはよく `DAO.Recordset` を使います。
しかし、トランザクションの命令を出す相手は、Recordset(レコードセット)ではなく、Workspace(ワークスペース)という、データベース全体を管理する「司令塔」なのです。

図解すると、このような階層構造になっています。

[DBEngine] (データベースの心臓部)
   └── [Workspace] (作業領域:ここでトランザクションを管理!)
          └── [Database] (実際のファイル)
                 └── [Recordset] (個々のデータ)

トランザクションを制御するには、この「Workspace」を正しく操作する必要があります。

3. 【実践】エラー発生時に確実にロールバックする定型句

それでは、実戦でそのまま使えるテンプレートコードを見ていきましょう。
今回は「複数のテーブルを更新する」というシナリオを想定した、最も堅牢な書き方です。

Option Compare Database
Option Explicit

''' <summary>
''' データの更新処理を行うメインルーチン
''' </summary>
Public Sub ExecuteSafeUpdate()
    ' --- 1. 変数の宣言 ---
    Dim ws As DAO.Workspace
    Dim db As DAO.Database
    Dim rsStock As DAO.Recordset
    Dim rsSales As DAO.Recordset
    
    ' エラーハンドリングの設定
    ' エラーが発生したら "Err_Handler:" というラベルへジャンプするように指示
    On Error GoTo Err_Handler

    ' --- 2. オブジェクトのセットアップ ---
    ' トランザクションを管理するのは Workspace です
    Set ws = DBEngine(0) 
    Set db = CurrentDb

    ' --- 3. トランザクションの開始 ---
    ' ここから先の一連の処理は「仮の状態」になります
    ws.BeginTrans
    Debug.Print "トランザクションを開始しました。"

    ' --- 4. メインの処理(ここが重要!) ---
    
    ' 例1:在庫テーブルの更新
    Set rsStock = db.OpenRecordset("T_Inventory", dbOpenDynaset)
    rsStock.FindFirst "ProductID = 1"
    If Not rsStock.EOF Then
        rsStock.Edit
        rsStock.Fields("StockCount").Value = rsStock.Fields("StockCount").Value - 1
        rsStock.Update ' ここで更新されるが、Commitされるまでは確定しない
    End If
    rsStock.Close

    ' --- (ここでエラーが起きると仮定しましょう) ---
    ' 例:あえてエラーを発生させるテスト用コード
    ' Err.Raise vbObjectError + 1, "Test", "強制的なエラー発生!"

    ' 例2:売上履歴テーブルへの追加
    Set rsSales = db.OpenRecordset("T_SalesHistory", dbOpenDynaset)
    rsSales.AddNew
    rsSales.Fields("ProductID").Value = 1
    rsSales.Fields("SaleDate").Value = Now()
    rsSales.Update
    rsSales.Close

    ' --- 5. すべて成功した場合のみ、確定(コミット)する ---
    ws.CommitTrans
    Debug.Print "すべての処理が成功しました。データを確定しました。"
    
    MsgBox "更新が完了しました。", vbInformation

Exit_Sub:
    ' --- 6. 後片付け(クリーンアップ) ---
    ' エラーが起きても起きなくても、開いたオブジェクトは必ず閉じる
    On Error Resume Next ' 後片付け中のエラーで止まらないように
    If Not rsStock Is Nothing Then rsStock.Close
    If Not rsSales Is Nothing Then rsSales.Close
    Set rsStock = Nothing
    Set rsSales = Nothing
    Set db = Nothing
    Set ws = Nothing
    Exit Sub

Err_Handler:
    ' --- 7. エラー発生時の処理(ロールバック) ---
    ' エラーが起きたら、BeginTrans 以前の状態にすべて巻き戻す!
    If Not ws Is Nothing Then
        ws.Rollback
        Debug.Print "エラーが発生したため、ロールバックを実行しました。"
    End If

    ' エラー内容をユーザーに通知
    MsgBox "エラーが発生したため、処理を中断し、データを元の状態に戻しました。" & vbCrLf & _
           "エラー内容: " & Err.Description, vbCritical, "更新失敗"
    
    Resume Exit_Sub
End Sub

4. ここがプロの分かれ道!陥りやすい落とし穴

このコードを読み解く上で、初心者が躓きやすいポイントを3つお伝えします。ここをクリアすれば、あなたはもう「ただのマクロ使い」ではありません。

① `Update` メソッドと `CommitTrans` の違い

「`rs.Update` を実行したんだから、もう保存されたんじゃないの?」と思うかもしれません。
いいえ、違います。`rs.Update` は「このレコードの変更内容を確定させる」という意味ですが、トランザクションの中では「仮の確定」に過ぎません。`ws.CommitTrans` が呼ばれるまでは、他のユーザーからも、データベース本体からも、その変更は見えない(あるいは変更中として扱われる)のです。

② エラーハンドリングの「場所」

`On Error GoTo Err_Handler` は、必ず `BeginTrans` よりも前に書いてください。
もし `BeginTrans` の直後にエラーが起きた場合、エラーハンドラーが準備できていないと、プログラムはそのまま強制終了してしまいます。そうなると、トランザクションが「開きっぱなし」になり、データベースがロックされて他の人が操作できなくなる……という最悪の事態を招きます。

③ 後片付け(Cleanup)の徹底

`Exit Sub` の直前に、必ずオブジェクトを `Nothing` にしたり `Close` したりする処理を入れてください。これを怠ると、メモリを無駄に消費したり、予期せぬタイミングでファイルがロックされたりします。プロのコードは、「入り口よりも出口(後片付け)に魂を込める」のです。

まとめ:信頼されるシステムを作るために

いかがでしたか?
「トランザクション」と「ロールバック」という概念は、最初は少し難しく感じるかもしれません。しかし、一度この仕組みを体に叩き込んでしまえば、あなたは「データが壊れる心配のない、信頼されるシステム」を作れるようになります。

「エラーが起きることを前提に、どうやって元に戻すか」

この考え方こそが、エンジニアリングの本質です。
ぜひ、今日学んだテンプレートを自分のプロジェクトに組み込んでみてください。一歩ずつ、確実に、最強のエンジニアへの道を歩んでいきましょう。

応援していますよ!

タイトルとURLをコピーしました