テクニカル ノート 68: Microsoft Access 7 ODBC ドライバーでのトランザクションの実行
Note
次のテクニカル ノートは、最初にオンライン ドキュメントの一部とされてから更新されていません。 結果として、一部のプロシージャおよびトピックが最新でないか、不正になります。 最新の情報について、オンライン ドキュメントのキーワードで関係のあるトピックを検索することをお勧めします。
このテクニカル ノートでは、MFC ODBC データベース クラスおよび Microsoft ODBC Desktop Driver Pack バージョン3.0 に含まれている Microsoft Access 7.0 ODBC ドライバーを使用する場合のトランザクションの実行方法について説明します。
概要
データベース アプリケーションでトランザクションを実行する場合は、アプリケーションで正しい順序で CDatabase::BeginTrans
および CRecordset::Open
を呼び出すように注意する必要があります。 Microsoft Access 7.0 ドライバーは Microsoft Jet データベース エンジンを使用します。Jet は、アプリケーションに、開いているカーソルがあるデータベースでトランザクションを開始しないように要求します。 MFC ODBC データベース クラスでは、開いているカーソルは開いている CRecordset
オブジェクトに相当します。
BeginTrans
を呼び出す前にレコードセットを開くと、エラー メッセージが表示されない場合があります。 ただし、CRecordset::Update
を呼び出した後に、アプリケーションが行うレコードセットの更新は永続的になり、更新プログラムは Rollback
を呼び出すことによってロール バックされません。 この問題を回避するには、まず BeginTrans
を呼び出し、次にレコードセットを開く必要があります。
MFC は、カーソルのコミットとロールバックの動作について、ドライバーの機能を確認します。 クラス CDatabase
には、GetCursorCommitBehavior
と GetCursorRollbackBehavior
の 2 つのメンバー関数が用意されており、開いている CRecordset
オブジェクトに対するトランザクションの影響を判定します。 Microsoft Access 7.0 ODBC ドライバーでは、Access ドライバーはカーソルの保持をサポートしていないため、これらのメンバー関数は SQL_CB_CLOSE
を返します。 したがって、CommitTrans
または Rollback
の操作の後に CRecordset::Requery
を呼び出す必要があります。
複数のトランザクションを 1 つずつ実行する必要がある場合、最初のトランザクションの後に Requery
を呼び出して、次のトランザクションを開始することはできません。 Jet の要件を満たすために、次の BeginTrans
への呼び出しの前にレコードセットを閉じる必要があります。 このテクニカル ノートでは、この状況を処理する 2 つの方法について説明します。
CommitTrans
またはRollback
の各操作の後にレコードセットを閉じます。ODBC API 関数
SQLFreeStmt
を使用します。
各 CommitTrans または Rollback 操作の後にレコードセットを閉じる
トランザクションを開始する前に、レコードセット オブジェクトが閉じていることを確認します。 BeginTrans
を呼び出した後、レコードセットの Open
メンバー関数を呼び出します。 CommitTrans
または Rollback
を呼び出した直後に、レコードセットを閉じます。 レコードセットを繰り返し開いたり閉じたりすると、アプリケーションのパフォーマンスが低下する可能性があることに注意してください。
SQLFreeStmt の使用
また、ODBC API 関数 SQLFreeStmt
を使用して、トランザクションの終了後にカーソルを明示的に閉じることもできます。 別のトランザクションを開始するには、BeginTrans
を呼び出した後に CRecordset::Requery
を呼び出します。 SQLFreeStmt
を呼び出すときは、レコードセットの HSTMT を最初のパラメーターとして指定し、SQL_CLOSE を 2 番目のパラメーターとして指定する必要があります。 このメソッドは、トランザクションの開始時ごとにレコードセットを閉じて開くよりも高速です。 この手法を実装する方法を次のコード例に示します。
CMyDatabase db;
db.Open("MYDATASOURCE");
CMyRecordset rs(&db);
// start transaction 1 and
// open the recordset
db.BeginTrans();
rs.Open();
// manipulate data
// end transaction 1
db.CommitTrans(); // or Rollback()
// close the cursor
::SQLFreeStmt(rs.m_hstmt, SQL_CLOSE);
// start transaction 2
db.BeginTrans();
// now get the result set
rs.Requery();
// manipulate data
// end transaction 2
db.CommitTrans();
rs.Close();
db.Close();
この手法を実装するもう 1 つの方法は、新しい関数 RequeryWithBeginTrans
を記述することです。これを呼び出すと、最初のトランザクションをコミットまたはロールバックした後に、次のトランザクションを開始できます。 このような関数を記述するには、次の手順を実行します。
CRecordset::Requery( )
のコードを新しい関数にコピーします。SQLFreeStmt
の呼び出しの直後に、次の行を追加します。m_pDatabase->BeginTrans( );
これで、この関数をトランザクションの各ペア間で呼び出せるようになりました。
// start transaction 1 and
// open the recordset
db.BeginTrans();
rs.Open();
// manipulate data
// end transaction 1
db.CommitTrans(); // or Rollback()
// close the cursor, start new transaction,
// and get the result set
rs.RequeryWithBeginTrans();
// manipulate data
// end transaction 2
db.CommitTrans(); // or Rollback()
Note
トランザクション間でレコードセット メンバー変数 m_strFilter または m_strSort を変更する必要がある場合は、この手法を使用しないでください。 その場合は、CommitTrans
または Rollback
操作の後にレコードセットを閉じる必要があります。