トランザクションの管理
データベースへの接続:インデックス への移動
トランザクションはアクションのグループであり,トランザクションがコミット(確定)される前に,そのすべてのアクションがデータベース内の 1 つまたは複数のテーブルに対して正常に実行されなければなりません。グループ内のアクションが 1 つでも失敗すると,すべてのアクションはロールバックされ(取り消され)ます。トランザクションを使用すると,トランザクションを構成するアクションのうちの 1 つが完了した時点で問題が起きてもデータベースの整合性が失われることはありません。
たとえば,バンキングアプリケーションでの口座から口座への資金振り替えは,トランザクションによる保護が適している操作です。もしも,一方の口座の残高を減らした後でもう一方の口座の残高を増やすときにエラーが発生した場合,それでもデータベース全体の総額が正しく保たれるようにするには,トランザクションをロールバックします。
SQL コマンドをデータベースに直接送信してトランザクションを管理する処理はいつでも実行可能です。ほとんどのデータベースは,独自のトランザクション管理モデルを提供しています(ただし,トランザクションをまったくサポートしないものもあります)。管理モデルをサポートするサーバーでは,独自のトランザクション管理を直接コーディングして,スキーマキャッシングなど,特定のデータベースサーバーの高度なトランザクション管理機能を利用できます。
高度なトランザクション管理が不要な場合は,接続コンポーネントが提供している一連のメソッドとプロパティを使用することによって,SQL コマンドを明示的に送信しなくてもトランザクションを管理できます。このプロパティやメソッドを利用すると,トランザクションをサポートするデータベースサーバーである限り,アプリケーションをその種類ごとにカスタマイズしなくても済むという利点があります(BDE も,ローカルテーブル用に制限付きのトランザクションサポートを提供していますが,サーバートランザクションサポートは提供していません。BDE を使用しない場合,トランザクションをサポートしていないデータベースでトランザクションを始めようとすると,接続コンポーネントは例外を発生します)。
警告: データセットプロバイダコンポーネントが更新を適用すると,それは暗黙的に,更新のためのトランザクションを生成します。明示的に開始するトランザクションが,プロバイダによって生成されたトランザクションと競合しないように注意してください。
トランザクションの開始
トランザクションを開始すると,それ以降にデータベースを読み書きするすべての文は,トランザクションが明示的に終了された場合,または(オーバーラップするトランザクションなら)別のトランザクションが開始された場合を除いて,開始されたトランザクションのコンテキスト内にあるものとみなされます。それぞれの文はグループの一部とみなされます。変更がデータベースに正常にコミットされなければ,変更はすべて元に戻されます。
トランザクションの進行中は,データベーステーブルのデータの表示は,トランザクション排他レベルによって指定されます。
TADOConnection の場合,トランザクションを開始するには,BeginTrans メソッドを呼び出します。
Level := ADOConnection1.BeginTrans;
Level = ADOConnection1->BeginTrans();
BeginTrans は,開始したトランザクションのネストのレベルを返します。ネストしたトランザクションとは,別の,親トランザクションの内にネストされているもののことです。サーバーがトランザクションを開始すると,ADO 接続は OnBeginTransComplete イベントを受け取ります。
TDatabase の場合,かわりに StartTransaction メソッドを使用します。TDataBase は,ネストした,またはオーバーラップしたトランザクションをサポートしていません。別のトランザクションが進行中のときに TDatabase コンポーネントの StartTransaction メソッドを呼び出すと,例外が発生します。StartTransaction の呼び出しを避けるには,InTransaction プロパティをチェックします。
if not Database1.InTransaction then
Database1.StartTransaction;
if (!Database1->InTransaction)
Database1->StartTransaction();
TSQLConnection は StartTransaction メソッドも使用しますが,それはより制御性の高いものです。つまり,StartTransaction は,トランザクション記述子というパラメータを持っています。この記述子を使用して,複数の同時トランザクションを管理し,トランザクション排他レベルをトランザクションごとに指定することができます。複数の同時トランザクションを管理するには,トランザクション記述子の TransactionID 項目をユニークな値に設定します。TransactionID には,値がユニーク(進行中の他のトランザクションと競合しない)であれば,任意の値を設定できます。サーバーによっては,TSQLConnection で開始したトランザクションを(ADO を使用する場合のように)ネストしたりオーバーラップすることができます。
var
TD: TTransactionDesc;
begin
TD.TransactionID := 1;
TD.IsolationLevel := xilREADCOMMITTED;
SQLConnection1.StartTransaction(TD);
TTransactionDesc TD;
TD.TransactionID = 1;
TD.IsolationLevel = xilREADCOMMITTED;
SQLConnection1->StartTransaction(TD);
オーバーラップされたトランザクションの場合,デフォルトでは,2 番めのトランザクションが開始されると,最初のトランザクションは非アクティブになります。ただし,最初のトランザクションのコミットやロールバックは後で実行できます。InterBase データベースで TSQLConnection を使用している場合,IsolationLevel プロパティを設定すると,それぞれのデータセットと特定のアクティブなトランザクションをアプリケーション内で関連付けることができます。つまり 2 番めのトランザクションを開始した後でも,データセットを目的のトランザクションに関連付けることによって,2 つのトランザクションの処理を同時に継続できます。
メモ: TADOConnection とは異なり,TSQLConnection および TDatabase は,トランザクションが開始してもイベントを受け取りません。
InterBase Express では,接続コンポーネントを使ってトランザクションを開始するかわりに,別個のトランザクションコンポーネントを使用すると,TSQLConnection よりもさらに高い制御性が得られます。ただし,デフォルトのトランザクションを開始するには,TIBDatabase を使用します。
if not IBDatabase1.DefaultTransaction.InTransaction then
IBDatabase1.DefaultTransaction.StartTransaction;
if (!IBDatabase1->DefaultTransaction->InTransaction)
IBDatabase1->DefaultTransaction->StartTransaction();
2 つの別個のトランザクションコンポーネントを使用すると,トランザクションをオーバーラップさせることができます。それぞれのトランザクションコンポーネントには,トランザクションを設定するためのパラメータのセットがあります。これらにより,トランザクション排他レベル,およびトランザクションのほかのプロパティを指定することができます。
トランザクションの終了
理想的には,トランザクションは必要最小限の間だけ存続すべきです。トランザクションがアクティブな時間が長くなるほど,そのデータベースに同時にアクセスするユーザーが増え,トランザクションの存続期間中に並行して開始および終了する同時トランザクションが多くなり,変更をコミットしようとするときに別のトランザクションと競合する可能性が高くなります。
トランザクションの構成要素であるすべてのアクションが正常に実行されたら,トランザクションをコミットしてデータベースの変更を確定できます。TDatabase の場合,Commit メソッドを使ってトランザクションをコミットします。
MyOracleConnection.Commit;
MyOracleConnection->Commit();
TSQLConnection の場合も,Commit メソッドを使用できますが,StartTransaction メソッドに与えたトランザクション記述子を指定して,どのトランザクションをコミットするかを明示しなければなりません。
MyOracleConnection.Commit(TD);
MyOracleConnection->Commit(TD);
TIBDatabase の場合,Commit メソッドを使ってトランザクションオブジェクトをコミットします。
IBDatabase1.DefaultTransaction.Commit;
IBDatabase1->DefaultTransaction->Commit();
TADOConnection の場合,CommitTrans メソッドを使ってトランザクションをコミットします。
ADOConnection1.CommitTrans;
ADOConnection1->CommitTrans();
メモ: ネストされた(子)トランザクションをコミットすることは可能です。これは,親トランザクションがロールバックされた場合に,後で変更をロールバックするためです。
トランザクションが正常にコミットされると,ADO 接続コンポーネントは OnCommitTransComplete イベントを受け取ります。ほかの接続コンポーネントは,同様のイベントを受け取りません。
現在のトランザクションをコミットする呼び出しは通常,try...except 文の中で試行します。このようにして,トランザクションが正常にコミットできなければ,except ブロックを使ってエラーを処理し,操作を再試行するかトランザクションをロールバックします。
トランザクションの一部で変更を行うときや,トランザクションをコミットしようとしてエラーが発生したら,そのトランザクション内で行われたすべての変更を破棄します。これらの変更を破棄することを,トランザクションをロールバックするといいます。
TDatabase の場合,Rollback メソッドを呼び出してトランザクションをロールバックします。
MyOracleConnection.Rollback;
MyOracleConnection->Rollback();
TSQLConnection の場合も,Rollback メソッドを使用できますが,StartTransaction メソッドに与えたトランザクション記述子を指定して,どのトランザクションをロールバックするかを明示しなければなりません。
MyOracleConnection.Rollback(TD);
MyOracleConnection->Rollback(TD);
TIBDatabase の場合,Rollback メソッドを呼び出してトランザクションオブジェクトをロールバックします。
IBDatabase1.DefaultTransaction.Rollback;
IBDatabase1->DefaultTransaction->Rollback();
TADOConnection の場合,RollbackTrans メソッドを呼び出してトランザクションをロールバックします。
ADOConnection1.RollbackTrans;
ADOConnection1->RollbackTrans();
トランザクションが正常にロールバックされると,ADO 接続コンポーネントは OnRollbackTransComplete イベントを受け取ります。ほかの接続コンポーネントは,同様のイベントを受け取りません。
現在のトランザクションをロールバックする呼び出しは,通常,以下の場合に使用します。
- 例外処理コード(データベースのエラーを回復できないとき)
- ボタンまたはメニューのイベントコード(ユーザーが[キャンセル]ボタンをクリックした場合など)