更新のキャッシング(FireDAC)

提供: RAD Studio
移動先: 案内検索

データの編集(FireDAC) への移動

概要

キャッシュ更新("キャッシュされた更新" の意味)モードでは、Post/Delete メソッドを呼び出すのではなく、データベースへの更新のポストを先送りすることができます。これにより、単一バッチ(オプションでトランザクション内に含めることも可能)で複数の更新をポストできるようになります。

データセットをキャッシュ更新モードに設定するには、CachedUpdates プロパティを True に設定します。その場合、データセットでは、このプロパティを直近に True に設定してから、あるいは CancelUpdates/CommitUpdates を直近に呼び出してから行われた変更をすべて追跡します。これらの変更は変更ジャーナルに格納されます。そこでは、すべての変更が変更時刻の順に並べられています。FireDAC では、同じレコードの複数のバージョンを追跡しません。直近の変更により、それ以前の変更が無効になり、レコードが変更順序の最後に移動します。

FireDAC では、以下の分散キャッシュ更新モードと一元キャッシュ更新モードをサポートしています。

  • 分散キャッシュ更新モード -- 各データセットでは、他のデータセットとは無関係に変更を追跡します。これが従来の方式で、デフォルト モードになっています。
  • 一元キャッシュ更新モード -- 複数のデータセットで単一の変更ログを共有し、その中で変更が時間順に並べられます。

一元キャッシュ更新

アプリケーションで複数のデータセットにログインし、それらの変更を時間順に適用する必要がある場合は、一元キャッシュ更新("一元的にキャッシュされた更新" の意味)を使用できます。それには、単一の TFDSchemaAdapter インスタンスをデータセットの SchemaAdapter プロパティに割り当てる必要があります。TFDSchemaAdapter は、行と、複数のデータセットに対するそれらの行の変更内容を一元的に格納する場所となります。

まず、一元キャッシュ更新はマスタ/詳細関係で役に立ちます。この関係では、自動インクリメント フィールド値も含め、マスタ データセットから変更が連鎖的に詳細データセットに伝播します。 詳細データセットへの伝播を有効にするには、FetchOptions.DetailCascadeTrue に設定する必要があります。 これにより以下が可能になります。

  • マスタ/詳細の変更の同期。その結果、マスタ データセットと詳細データセットの変更が時間順に記録され適用されるようになります。たとえば、まず、マスタ レコードが挿入されると、次に、それに対応する詳細レコードが挿入されます。
  • マスタ ID 列値の詳細データセットへの伝播。たとえば、詳細データセットがマスタ データセットの ID 列にリンクしている場合に、更新を適用すると、対応する詳細レコードに実際のマスタ ID 列値が設定される必要があります。
  • マスタ レコード削除時の詳細レコードの連鎖的削除。たとえば、マスタ レコードが削除されると、対応するすべての詳細レコードも削除され、それが変更ログに記録されます。
  • 詳細データセットへのマスタ フィールド変更の連鎖的伝播。

伝播は、以下の条件を満たしている場合に機能します。

また、FireDAC でクライアント側の連鎖的変更がデータベースにポストされるかどうかを、DetailServerCascade を使用して制御することもできます。その場合、DetailServerCascadeFetchOptions.DetailCascade と一緒に使用します。

伝播を伴う一元キャッシュ更新を有効にするには、以下の手順を実行します(その前にまず、範囲ベースの M/Dをセットアップし、CachedUpdates プロパティを True に設定します)。

  1. フォームに TFDSchemaAdapter をドロップします。
  2. マスタ データセットの SchemaAdapter プロパティを TFDSchemaAdapter に設定します。
  3. 詳細データセットの SchemaAdapter プロパティを TFDSchemaAdapter に設定します。
  4. 詳細データセットの FetchOptions.DetailCascadeTrue に設定します。

同時に、これで、詳細データセットのインメモリ参照制約も有効になります。これは以下の SQL コマンドと似ています。

ALTER TABLE <detail> ADD CONSTRAINT
FOREIGN KEY (<detail fields>)
REFERENCES <master> (<master fields>)
ON DELETE CASCADE
ON UPDATE CASCADE

アプリケーションで更新を適用するには、データセットの ApplyUpdates メソッドではなく、TFDSchemaAdapter.ApplyUpdates メソッドを使用しなければなりません。 エラーを調停するには、データセットの Reconcile メソッドではなく、TFDSchemaAdapter.Reconcile.Reconcile メソッドを使用します。

詳細については、Centralized Cached Updates サンプルを参照してください。

更新の追跡

アプリケーションがキャッシュ更新モードで動作している場合は、変更を追跡でき、オプションで、データセットごとに変更を元に戻すこともできます。変更を追跡するには、以下のプロパティを使用します。

  • UpdatesPending -- 変更ログが空でない場合、True を返します。
  • ChangeCount -- 変更の総数を返します。
  • UpdateStatus -- 現在のレコードの変更の種類を返します。
  • FilterChanges -- レコードを変更の種類でフィルタリングできるようにします。

既存の変更を元に戻すには、以下のプロパティとメソッドを使用します。

  • SavePoint – 現在の変更ログの状態を設定/取得します。
  • RevertRecord – 現在のレコードを、その前の(元の)状態に戻します。
  • UndoLastChange – 最終変更レコードにジャンプし、それをその前の(元の)状態に戻します。
  • CancelUpdates – 変更ログのすべてのレコードを元に戻します。

たとえば、簡単な取り消し機能を実装するには、actUndo アクションを作成し、以下のイベント ハンドラを付加することもできます。

procedure TForm1.actUndoUpdate(Sender: TObject);
begin
  actUndo.Enabled := FDQuery1.UpdatesPending;
end;

procedure TForm1.actUndoExecute(Sender: TObject);
begin
  FDQuery1.UndoLastChange(True);
end;

他の例としては、一連の変更を取り消すことが可能なインメモリ トランザクションを実装するには、以下のようにコーディングできます。


FDQuery1.CachedUpdates := True;
iSavePoint := FDQuery1.SavePoint;
try
  FDQuery1.Append;
  ...
  FDQuery1.Post;
  FDQuery1.Append;
  ...
  FDQuery1.Post;
  FDQuery1.Append;
  ...
  FDQuery1.Post;
except
  FDQuery.SavePoint := iSavePoint;
end;

メモ: キャッシュ更新モードでは、以下のメソッドとプロパティで更新ジャーナルが操作されます。

  • Data プロパティには、すべてのレコード(削除されたものも含む)とそれらの変更が格納されています。
  • Delta プロパティは、更新ジャーナル内のレコードのうち、削除されたもの、挿入されたもの、更新されたものを返します。
  • CopyRecord メソッドと CopyDataSet メソッドは変更を新しく作成し、変更ジャーナルをコピーしません。
  • LoadFromStreamLoadFromFileSaveToStreamSaveToFile はデータ(更新ジャーナルを含む)の読み込み/保存を行います。

キャッシュ更新モードでは、更新ジャーナルに変更が含まれている場合、一部のメソッドやプロパティ設定で例外が発生します。変更は、コミットするかキャンセルする必要があります。このようなメソッドやプロパティ設定は以下のとおりです。

  • Refresh
  • CachedUpdatesFalse に設定した場合


更新の適用

更新をデータベースに適用するには、ApplyUpdates メソッドを使用します。レコードの適用時に例外が発生した場合、その例外はそのレコードに関連付けられます。ApplyUpdates メソッドには以下の特徴があります。

  • 例外を発生させない代わりに、発生した例外の数を返します。
  • トランザクション内への更新の適用はラップしていません。それはアプリケーション自身で行うことができます。
  • 即時更新と同じ更新ポスト ロジックを採用しています。

更新の適用後も、変更されたレコードは変更ログに残ります。それらを変更ログから削除し、変更されていないものとするには、CommitUpdates メソッドを呼び出します。以下に例を示します。

FDQuery1.CachedUpdates := True;
FDQuery1.Append;
...
FDQuery1.Post;
FDQuery1.Append;
...
FDQuery1.Post;
FDQuery1.Append;
...
FDQuery1.Post;
FDConnection1.StartTransaction;
iErrors := FDQuery1.ApplyUpdates;
if iErrors = 0 then begin
  FDQuery1.CommitUpdates;
  FDConnection1.Commit;
end
else
  FDConnection1.Rollback;

メモ: この状況は、AutoCommitUpdatesFalse に設定されている場合の動作に該当します。

  • AutoCommitUpdatesTrue に設定している場合は、CommitUpdates を明示的に呼び出す必要はありません。ApplyUpdates を呼び出して適用し、更新が成功したレコードはすべて、変更されていないものと自動的に設定されるためです。

エラーのレビュー

ApplyUpdates 呼び出しの中でエラーが発生した場合、ApplyUpdates は、そのエラーを内部のデータ レコード構造に記録し、エラーの数が AMaxErrors 以上になるまで、引き続き更新を処理します。ApplyUpdates は例外を発生させません。ApplyUpdates の呼び出し後に、エラーのあるレコードをすべて処理するには、調停プロセスを使用するか、エラーのあるレコードをフィルタリングします。

レコードを調停するには、OnReconcileError イベント ハンドラを割り当て、Reconcile メソッドを呼び出します。OnReconcileError イベント ハンドラでは、エラーの分析や現在のレコード フィールド値の読み取り/変更が可能です。終了時に、このイベント ハンドラでは、エラーのある現在のレコードに対して FireDAC コードで実行すべきアクションを割り当てなければなりません。Reconcile メソッドの呼び出し後、ApplyUpdates を再度呼び出して、エラーのあったレコード変更内容のポストを試みることができます。

エラーのあるレコードをフィルタリングするには、rtHasErrorsFilterChanges に含めます。その後で、データセット内を移動して RowError プロパティを読み取り、現在のレコードに関連付けられている例外オブジェクトを取得します。以下に例を示します。

var
  oErr: EFDException;
...
if FDQuery1.ApplyUpdates > 0 then begin
  FDQuery1.FilterChanges := [rtModified, rtInserted, rtDeleted, rtHasErrors];
  try
    FDQuery1.First;
    while not FDQuery1.Eof do begin
      oErr := FDQuery1.RowError;
      if oErr <> nil then begin
        // process exception object
        ...
      end;
      FDQuery1.Next;
    end;
  finally
    FDQuery1.FilterChanges := [rtUnmodified, rtModified, rtInserted];
  end;
end;

関連項目

サンプル