プライマリ・コンテンツに移動
Oracle® Data Provider for .NET開発者ガイド
ODAC 12c リリース4 (12.1.0.2) for Microsoft Windows
E72575-01
  目次へ移動
目次
索引へ移動
索引

前
 
次
 

論理破損を防ぐためのトランザクション・ガードの使用

トランザクション・ガードを使用すると、計画済および計画外の停止時や送信が繰り返された場合に、管理対象および管理対象外ODP.NETアプリケーションで実行を1回以下にすることができます。トランザクション・ガードを使用しないと、アプリケーションが停止の後に操作を実行しようとして、トランザクションが重複してコミットされ論理破損が発生する可能性があります。

停止後にアプリケーションをリカバリする際の従来の問題の1つは、継続性のないコミット・メッセージがクライアントに戻されることでした。クライアントとサーバーの間が中断されると、クライアントには通信が失敗したことを示す(リカバリ可能エラーとも呼ばれる)エラー・メッセージが示されます。このエラーは、送信でコミット操作が実行されたかどうかや、すべての予測されるコミットの実行中に完了まで手続きコールを実行したかどうかをアプリケーションに通知しません。エラーはセッション・ステート変更または断続的な障害も示しません。トランザクションがコミットされたかどうか、完全に完了したかどうか、クライアントは確認できないままです。

このリカバリ可能エラーでは、重複したトランザクションの送信またはその他の論理破損の形式で、エンド・ユーザーやアプリケーションがリプレイを試すことが必要です。トランザクションは、非トランザクション状態が不正確な場合、またはそのトランザクションがコミットされている場合は、有効に再送信できません。コミットされているが完了していないコールの処理を続けると、誤った状態のデータベース・セッションがアプリケーションで使用されることになります。

ODP.NETおよびトランザクション・ガード

トランザクション・ガードでは、ODP.NET管理対象ドライバおよびODP.NET管理対象外ドライバによって自動的かつ透過的に基準化された方法で、トランザクションの重複を解消します。

ノード、ネットワーク、データベースなどで障害が発生した場合、ODP.NETアプリケーションはデータベース・サービスが起動されていればステータスを問い合せることで、そのトランザクションがコミットされたかどうかを判断できます。これらの1つが失敗した後でも、Oracleは自動的にトランザクション・ステータスを保持します。

ODAC 12cリリース4では、トランザクション・ガード・アプリケーション開発の使用は合理化され、トランザクション結果を決定するために必要なアプリケーション・ロジックが削減されました。さらに、これらの利点は管理対象ODP.NETと管理対象外ODP.NETの両方に有効です。

トランザクション・ガードが有効なデータベース・サービスで、データベース・コミットまたはコミットをコールした可能性のあるSQLかPL/SQLの実行でリカバリ可能エラーが発生した場合、ODP.NET OracleExceptionOracleLogicalTransactionインスタンス付きで作成されます。データベースでは、管理者によって指定された保存期間、論理トランザクションの結果が維持されます。ODP.NETは、リカバリ可能なエラーが発生した際、アプリケーションのために自動でデータベースに問い合せます。これにより、OracleExceptionオブジェクトのOracleLogicalTransactionオブジェクト・インスタンスは、トランザクションがコミットされているかどうか、およびユーザー・コールが完了しているかどうかを示すことができます。

ステータスがコミット済の場合、トランザクションは正常に完了しています。管理者が他の操作を行う必要はありません。

コミットされていない場合、ODP.NETアプリケーションは現在のトランザクション状態が、リカバリ可能かどうか、OracleLogicalTransactionを使用して再試行可能かどうかを確認します。エラーがリカバリ可能な場合、トランザクションは安全に再送信できます。エラーがリカバリ不可の場合、アプリケーションは別のメカニズムを使用してトランザクション結果を判断する必要があります。


注意:

トランザクション・ガードは、ローカル・トランザクションのみをサポートしています。分散トランザクションはサポートしていません。

トランザクション・ガードの機能は、Oracleサービスレベル構成のCOMMIT_OUTCOME設定によって有効または無効にします。デフォルトでは無効です。この設定はデータベースを停止せずに変更できます。新しい設定は、このサービスに対して作成された新規接続にのみ適用されます。

次に、SRVCTLを使用してCOMMIT_OUTCOMEを設定する例を示します。

srvctl modify service -d orcl -s GOLD -commit_outcome TRUE

注意:

トランザクションのステータスを取得するデータベース・ユーザーには、DBMS_APP_CONTパッケージのEXECUTE権限を付与する必要があります。
GRANT EXECUTE ON DBMS_APP_CONT TO <user name> ;

次に、ODP.NETトランザクション・ガードのアプリケーション・シナリオの例を示します。

ODP.NETアプリケーションは、高速アプリケーション通知(FAN)ダウン・イベントまたはエラーを受信します。FANは、中断されたセッションを自動的に強制終了し、アプリケーションはOracleExceptionを受信します。エラーを透過的に処理するために作成されたトランザクション・ガード・アプリケーションは、次のことを実行します。

  1. OracleException.OracleLogicalTransactionプロパティの値を確認します。値がOracleLogicalTransactionオブジェクトの場合(つまりnull以外)、エラーはリカバリ可能です。プロパティの値がnullの場合、エラーはリカバリ可能ではなく、トランザクション・ガードは有効化されていません。

  2. リカバリ可能なエラーでは、OracleLogicalTransaction.Committedプロパティを確認します。trueの場合、トランザクションはコミットされています。falseの場合、トランザクションは送信されていませんが、安全に再送信できるようになりました。

  3. リカバリ可能なエラーでは、コミット操作以外のトランザクションの状態が重要な場合、OracleLogicalTransaction.UserCallCompletedプロパティを確認します。CommittedおよびUserCallCompletedの値が何を意味するかについては、次の表を参照してください。

Committedの値 UserCallCompletedの値 結果
True True トランザクションは正常に完了しました。結果をアプリケーションに戻すことができます。
False False トランザクションは正常に完了していません。そのトランザクションをアプリケーションで再送信することができます。
True False トランザクションはコミットされましたが、アプリケーションで期待どおりに続行されていない未完了の状態(行数やネスト済のPL/SQLロジックなど)が含まれている可能性があります。

サンプル・コード

using System;
using Oracle.DataAccess.Client;
//alternatively can use using Oracle.ManagedDataAccess.Client;
 
class TransactionGuardSample
{
    static void Main()
    {
        bool bReadyToCommit = false;
 
        string constr = "user id=hr;password=hr;data source=oracle";
        OracleConnection con = new OracleConnection(constr);
        OracleTransaction txn = null;
        OracleCommand cmd = null;
 
        try
        {
            string sql = " update employees set salary=10000 where employee_id=103";
            con.Open();
            txn = con.BeginTransaction();
            cmd = new OracleCommand(con, sql);
            cmd.ExecuteNonQuery();
            bReadyToCommit = true;
        }
        catch (Exception ex)
        {
            // rollback here as the SQL execution is unsuccessful
            txn.Rollback();  
            Console.WriteLine(ex.ToString());
        }
 
        try
        {
            if (bReadyToCommit)
                txn.Commit();
        }
        catch (Exception ex)
        {
            if (ex is OracleException)
            {
                //  It's safe to re-submit the work if the error is recoverable and the transaction has not been committed
                if (ex.IsRecoverable && ex.OracleLogicalTransaction != null && !ex.OracleLogicalTransaction.Committed) 
                {
                    // safe to re-submit work
                }
                else
                {
                    // do not re-submit work
                }
            }
        }
        finally
        {
            // dispose all objects
            txn.Dispose();
            cmd.Dispose();
            con.Dispose(); // place the connection back to the connection pool
        }
    }
}      cmd.Dispose();
      con.Dispose();
    }
  }
}

関連項目: