オブジェクトグラフの管理に関するクラス群

EOEditingContextはオブジェクトグラフを管理するために他のオブジェクトと共同して動作します。 特に重要な役割を担うのがNSUndoManagerとEOObserverCenterです。 NSUndoManagerオブジェクトはアンドゥ履歴を提供します。 NSUndoManagerのクライアントとなるEOEditingContextは、監視するオブジェクトのすべての変更をアンドゥイベントとして登録します。

EOObserverCenterはオブジェクトを監視する通知メカニズムを提供します。 監視するオブジェクトは、他のオブジェクトの状態が変更されるのを知ることができます。 「監視される」オブジェクト(通常すべてのエンタープライズオブジェクト)は、状態が変更される前("set"メソッド)に willChange メソッドを実行する責務を負います。 オブジェクト(EOEditingContextオブジェクトなど)は自身を監視者としてEOObserverCenterに登録することができます。 監視されているオブジェクトが willChange を実行すると、登録したオブジェクトは必ず通知を受け取ります(objectWillChangeが実行されます)。

objectWillChangeメソッドはEOObservingインターフェースで定義されています。 EOEditingContextはEOObservingインターフェースを実装しています。


EOEditingContextの生成

通常EOEditingContextは何らかの操作を行うとアプリケーションによって自動的に生成されます。 例えば次のような操作を行うとEOEditingContextを含めたオブジェクトを生成します。

しかし、グラフィカルなインターフェースを使わないアプリケーションなどではEOEditingContextを手動で生成する必要があるかもしれません。 EOEditingContextを生成するには次のようにします。

  EOEditingContext editingContext = new EOEditingContext();
このようにするとデフォルトのEOObjectStoreCoordinatorに接続したEOEditingContextを生成します。 また、EOEditingContextの生成時に特定のオブジェクトストアを親オブジェクトストアとして使うように変更することもできます。 デフォルト以外のEOObjectStoreCoordinatorを使いたい場合やEOEditingContextをネストさせたい場合に有効です。 例えば次のコードは childEditingContext の親オブジェクトストアを parentEditingContext にして生成します。
    EOEditingContext parentEditingContext; // すでにあるものとして仮定
    EOEditingContext childEditingContext =
        new EOEditingContext(parentEditingContext);

手動で生成したEOEditingContextは、lockunlock メソッドを使ってスレッドセーフに使われるようにしなければなりません。

EOEditingContextのアダプタレベルのオブジェクトへのアクセス

EOEditingContextは何らかのオブジェクトストアと協調して動作しますが、通常の設定であればEOEditingContextを通してアクセスレイヤーのオブジェクトにアクセスすることができます。 EOEditingContextのアダプタレベルのオブジェクトにアクセスするには、まずEOEditingContextからEOObjectStoreCoordinatorを取得し、続いてEOObjectStoreCoordinatorからEODatabaseContext (EOAccess) を取得します。 このEODatabaseContextからアダプタレベルのオブジェクトを取得します。 次のコードは一連の処理の例です。

  EOEditingContext editingContext;  // すでにあるものとして仮定
  String entityName;                // すでにあるものとして仮定
  EOFetchSpecification fspec;
  EOObjectStoreCoordinator rootStore;
  com.webobjects.eoaccess.EODatabaseContext dbContext;
  com.webobjects.eoaccess.EOAdaptor adaptor;
  com.webobjects.eoaccess.EOAdaptorContext adContext;
  fspec = new EOFetchSpecification(entityName, null, null);
  rootStore = (EOObjectStoreCoordinator)editingContext.rootObjectStore();
  dbContext = (EODatabaseContext)rootStore.objectStoreForFetchSpecification(fspec);
  adaptor = dbContext.database().adaptor();
  adContext = dbContext.adaptorContext();

この例では、最初にエンティティ名を引数にしてフェッチ仕様を生成しています。 もちらん普通にフェッチ仕様を使うならコンストラクタに null 以外の値を指定しますが、ここではEOObjectStoreCoordinatorの objectStoreForFetchSpecification メソッドを使うためにエンティティ名だけを指定しています。 次に、rootObjectStore メソッドでEOEditingContextのEOObjectStoreCoordinatorを取得します。 rootObjectStoreはEOObjectStoreCoordinatorオブジェクトを返しますが、戻り値の型はEOObjectStoreCoordinatorクラスではなくEOObjectStoreクラスです。 これはルートオブジェクトストアをカスタムオブジェクトストアで置き換えられるようにするためです。 同様にEOObjectStoreCoordinatorの objectStoreForFetchSpecification メソッドも通常EODatabaseContextオブジェクトを返しますが、戻り値の型はEOCooperatingObjectStoreになります。 これもデータベースコンテキストをカスタム協調オブジェクトストアで置き換えられるようにするためです。 もしオブジェクトストアを置き換えたならば、上記のコードもカスタムオブジェクトストアに合わせて変更する必要があります。

EOEditingContextのEOObjectStoreCoordinatorは、データベースレベル・アダプタレベルのオブジェクトのセットを複数扱うことができます。 従ってEOObjectStoreCoordinatorからEODatabaseContextを取得するには、EODatabaseContextを特定する情報を準備しておく必要があります。 上記のコードではEOFetchSpecificationと objectStoreForFetchSpecification メソッドを使っていますが、EOObjectStoreCoordinatorの次のメソッドでもオブジェクトストアを特定することができます。


メソッド 説明
cooperatingObjectStores EOObjectStoreCoordinatorの協調オブジェクトストアの配列を返します。
objectStoreForGlobalID グローバルIDを管理している協調オブジェクトストアを返します。
objectStoreForObject オブジェクトを管理している協調オブジェクトストアを返します。

EODatabaseContextを取得すると、そのオブジェクトからEOAdaptorとEOAdaptorContextも取得できます。 (EODatabaseContext, EOAdaptor, EOAdaptorContextはすべてEOAccessパッケージで定義されています)


異なる設定でのEOEditingContextの使用

EOEditingContextにとって、親EOObjectStoreはオブジェクトグラフを生成する基礎となる関係にあります。 EOObjectStoreはEOEditingContextが管理するオブジェクトの入出力について定義している抽象クラスです。 EOObjectStoreはオブジェクトを生成、登録、フォールトを解決するほかにEOEditingContextのオブジェクトグラフの変更をコミットする責務を負っています。

EOEditingContextと親EOObjectStoreは様々な方法で基本的な設定を拡張することができます。 例えば複数のEOEditingContext間でEOObjectStoreを共有したり、1つのEOEditingContextが複数のEOObjectStoreに作用するように設定することもできますす。 次のセクションでは最も多用されるパターンを解説します。


対等なEOEditingContexts

複数のEOEditingContextで1つのEOObjectStoreを共有することができます。 各EOEditingContextは個別にオブジェクトグラフを管理するので、 例えば従業員テーブルのデータベース行に対応するオブジェクトは各EOEditingContextごとに生成されることになります。 あるEOEditingContextが管理するオブジェクトに変更を加えても、共有されているEOObjectStoreに変更をコミットしない限り、他のEOEditingContext内のオブジェクトには影響ありません。 変更をコミットすると、すべてのEOEditingContextのオブジェクトが同期されます。 ユーザが複数の「ドキュメント」を編集するようなアプリケーションに有用です。

ネストされたEOEditingContexts

EOEditingContextはEOObjectStoreのサブクラスであり、EOEditingContextオブジェクトが他のEOEditingContextオブジェクトをEOObjectStoreとして扱えるようになっています。 言い換えるとEOEditingContextをネストすることによって、ユーザに一方のEOEditingContextでオブジェクトを編集させ、その変更を破棄したりもう一方のEOEditingContextに変更をコミットすることができます(次にそのEOEditingContextが変更を外部ストアにコミットします)。 これは「OK(コミット)」と「キャンセル(ロールバック)」で変更を操作できるネストされたダイアログのような、「ドリルダウン」式のインターフェースで有用です。

ネストされたEOEditingContexts

ネストされたEOEditingContextでフェッチされたオブジェクトには、親EOEditingContext内の未保存の変更も反映されます。 例えば次のような状況があるとします。 あるパネルに従業員リストが表示されており、給与額を自由に変更できます。 このパネルにはネストしたパネルを表示するためのボタンがあり、そのパネルでは詳細を編集することができます。 親パネルで給与額を編集すると、その給与額がネストしたパネルにも反映されます。 ネストしたパネルだけデータベースから取得したデータのまま、ということにはなりません。 このように、ネストされたEOEditingContextは親EOEditingContextを通じてフェッチを行います。

EOEditingContextは次のEOObjectStoreのメソッドをオーバーライドしています。

基本的にこれらのメソッドは他のEOEditingContextに対し、EOObjectStoreとして振る舞うために使われます。


複数のソースからデータを取得する

EOEditingContextのオブジェクトグラフは複数の外部ストアから得たオブジェクトを保持することができます。 その場合のルートオブジェクトストアはEOObjectStoreCoordinatorであり、処理を各EOCooperatingObjectStoreに転送することでオブジェクトストアを抽象化しています。

実際のアプリケーション開発では、上記で触れた様々なシナリオを組み合わせることになるでしょう。


オブジェクトのフェッチ

Enterprise Objects Frameworkアプリケーションで外部ストアからオブジェクトをフェッチする一般的な方法は、EOEditingContextの objectsWithFetchSpecification メソッドを使うことです。 このメソッドはフェッチ仕様を引数にとり、オブジェクトの配列を返します。 フェッチ仕様はフェッチするオブジェクトのエンティティ名、フェッチ条件(クエリ)、返すオブジェクトのソート順序を含みます。


アプリケーション内の変更の管理

EOEditingContextにはアプリケーション内のオブジェクトの変更を管理する様々なメソッドがあります。 これらのメソッドは変更のあるオブジェクトについて調べたり、特定の変更をアンドゥ・リドゥする他に、データベースに保存する前にすべての変更を破棄するなどの用途に使います。 以降のセクションでは、これらのメソッドについて解説します。


変更されたオブジェクトの調査

EOEditingContextは、オブジェクトグラフ中のオブジェクトへの三種類の変更(挿入、削除、更新)を管理します。 データベースに保存する前であれば、これらの変更はそれぞれ insertedObjects, deletedObjects, updatedObjects メソッドで調べられます。 各メソッドはそれぞれ挿入待ち、削除待ち、更新待ちのオブジェクトを配列で返します。 hasChanges メソッドはオブジェクトグラフに挿入待ち、削除待ち、更新待ちのいずれかのオブジェクトがあるかどうかを truefalse で返します。


アンドゥとリドゥ

EOEditingContextにはオブジェクトグラフ中のオブジェクトの変更を管理する undo, redo, revert メソッドが定義されています。 undo はEOEditingContextのNSUndoManagerにオブジェクトグラフを最後に変更された状態に戻すよう要求します。 redo は同じくNSUndoManagerに最後のアンドゥを取り消すよう要求します。 revert はアンドゥ履歴を空にし、すべての挿入待ちと削除待ちの変更を破棄し、オブジェクトを最後に保存された状態に戻します。

EOEditingContextのアンドゥ回数に制限はありません。 オブジェクトを最初にフェッチ、生成された状態まで繰り返しアンドゥできます。 また、変更を保存した後でもアンドゥできます。 アンドゥ機能を使うとNSUndoManagerは大量のメモリを使います。

例えば、リレーションシップ先のオブジェクトが削除されると、EOEditingContextはリレーションシップ元のオブジェクトのスナップショットを生成します。 このスナップショットは削除されたオブジェクトを参照しており、EOEditingContextとNSUndoManagerから参照されます。 EOEditingContextは変更が保存されるとスナップショットを参照しなくなりますが、NSUndoManagerは引き続き参照します。 NSUndoManagerは削除されたオブジェクトをアンドゥできるようにスナップショットを保管します。

アプリケーションを普通に使っていても頻繁に変更が行われる場合、アンドゥ機能で使われるメモリの量を制限したくなるかもしれません。 EOEditingContextが保存処理を行うときならUndoManagerのアンドゥ履歴を片づけることができます。 これは単純にNSUndoManagerの removeAllActions メソッド(または removeAllActionsWithTarget メソッドを引数にEOEditingContextを指定して)を実行するだけです。 アンドゥ機能が必要なければ、EOEditingContextの setUndoManager メソッドの引数を null にして実行すると、アンドゥ機能の負荷をなくすことができます。


変更の保存

saveChanges メソッドはオブジェクトグラフ中にある変更のあったオブジェクトを外部ストアへ保存します。 変更を保存すると、EOEditingContextの挿入待ち、更新待ち、削除待ちのリストが空になります。

保存処理が成功すると、EOEditingContextの親EOObjectStoreは ObjectsChangedInStoreNotification をポストします。 保存されたEOEditingContextと対等の関係にあるEOEditingContextはこの通知を受け取り、保存されたデータで管理するオブジェクトを同期します。


オブジェクトグラフを管理するメソッド

EOEditingContextにはオブジェクトグラフ中のエンタープライズオブジェクトを管理するメソッドがあります。 このセクションではこれらのメソッドとオブジェクトグラフを管理する様々な方法を解説します。

アプリケーションの様々な個所で、次のような処理を行いたいことがあるでしょう。

以上のシナリオを以降のセクションで検証します。


エンタープライズオブジェクトの変更を破棄する

エンタープライズオブジェクトの変更を破棄するには様々な方法があります。 次にその方法を示します。

この他の方法は invalidate... メソッドを使うことです。 これについては「メモリにキャッシュされているオブジェクトの破棄」で解説します。


オブジェクトのリフレッシュ

オブジェクトグラフの特徴の一つは、アプリケーション中のデータに一意性を持たせることです。 デフォルトの設定でデータを再フェッチしたとき、Enterprise Objects Frameworkはデータベース行に変更があってもオブジェクトのデータを上書きしないようにして、オブジェクトグラフの整合性を保ちます。 しかしその変更を調べたい場合はどうすればいいのでしょうか? このような場合にはEOFetchSpecificationの setRefreshesRefetchedObjects メソッドを使います。 setRefreshesRefetchedObjects を、引数を true にして実行しておくと、フェッチ済みのオブジェクトを新しくフェッチしたデータで上書きすることができます。 もしくはEODatabaseContextのデリゲートメソッドである databaseContextShouldUpdateCurrentSnapshot を使っても同じことができます。

オブジェクトのリフレッシュを行うようにEOFetchSpecificationの setRefreshesRefetchedObjects で設定すると、以降にフェッチしたオブジェクトだけがリフレッシュされます。 例えば従業員オブジェクトをリフレッシュしても、従業員オブジェクトのリレーションシップ先にある部署オブジェクトまでは再フェッチされません。 しかしリレーションシップ先を事前にフェッチするようEOFetchSpecificationで設定しておくと、再フェッチ時に、setPrefetchingRelationshipKeyPaths で指定したすべてのリレーションシップ先のオブジェクトも再フェッチされます。

再フェッチしたオブジェクトのリフレッシュは、指定したオブジェクトにのみ有効です。 オブジェクトグラフ全体を再フェッチしたい場合は、次に解説する EOEditingContextの invalidate... メソッドを使います。


メモリにキャッシュされているオブジェクトの破棄

このセクションでは、undorevert で特定のエンタープライズオブジェクトの変更を破棄できることについて解説します。 これらのメソッドを使うと、データベースから最初にフェッチしたデータのキャッシュを維持することができます。 よくあるケースとして、メモリ中のすべてのオブジェクトを消したい場合や誰かがデータベースに加えた変更を調べたい場合はどうすればいいでしょうか? そのような場合は invalidateAllObjects メソッドか invalidateObjectsWithGlobalIDs メソッドを使ってエンタープライズオブジェクトを無効にすることができます。 また、EOFetchSpecificationの setRefreshesRefetchedObjects メソッドを true に設定しておくと、invalidate... メソッドはオブジェクトグラフ全体を再フェッチします。

invalidateAllObjects メソッドの効果は使い方次第です。 例えばEOEditingContextの invalidateAllObjects メソッドを実行すると、登録されているすべてのオブジェクトのグローバルIDに対して、親EOObjectStoreの invalidateObjectsWithGlobalIDs メソッドを実行します。 EOEditingContextがネストされている場合、親EOObjectStoreはもう一つのEOEditingContextかEOObjectStoreCoordinatorになります。 とにかく、オブジェクトストアの階層構造をたどってメソッドを連鎖することになります。 EOObjectStoreCoordinatorまでたどりつくと、今度はEODatabaseContextがメソッドを実行します。 EODatabaseContextはグローバルIDに対応するデータベース行のスナップショットを破棄し、続けて ObjectsChangedInStoreNotification をポストします。 その結果、オブジェクトグラフ中のすべてのエンタープライズオブジェクトがフォールトに戻されます。 次にオブジェクトにアクセスしたとき、そのオブジェクトが再びデータベースからフェッチされます。

invalidateAllObjects メソッドは実行したEOEditingContextのみで管理されているオブジェクトのみを対象にするのではなく、他のEOEditingContextでも共通して管理されている(グローバルIDに対応する)オブジェクトを対象とします。 例えば editingContext1objectAobjectB を管理しており、 editingContext2objectAobjectBobjectC を管理しているとします。 editingContext1invalidateAllObjects を実行すると、 editingContext1editingContext2 の両方が管理する objectAobjectB が再度フォールトに戻されます。 しかし editingContext2 が管理する objectC は、 editingContext1objectC を管理するようにならない限りそのままの状態が保たれます。

EOObjectStoreCoordinatorの invalidateAllObjects を直接実行すると、EOObjectStoreCoordinatorが管理するEODatabaseContextすべての invalidateAllObjects が実行されます。 アプリケーション中のスナップショットはすべて破棄され、すべてのEOEditingContextが管理するオブジェクトがフォールトに戻されます。

invalidate... メソッドは変更を保存せずにデータベースのロックを解除する唯一の手段です。

invalidateAllObjectsinvalidateObjectsWithGlobalIDs を頻繁に実行すると、パフォーマンスに大きな影響を及ぼす可能性があります。 EOEditingContext中のオブジェクトが必要なくなった場合は、次のようにすると簡単です。

    ec.dispose();
    ec = new EOEditingContext(); // 新しくオブジェクトをフェッチ、フォールトする

オブジェクトを複数のEOEditingContext間に渡って使う

「異なる設定でのEOEditingContextの使用」でも触れているように、アプリケーションは常に複数のEOEditingContextを使用しています。 あるEOEditingContextから他のEOEditingContextが管理するオブジェクトにアクセスしなければならないことも多いでしょう。

一見すると、最も適当な方法は1番目のEOEditingContextに2番目のEOEditingContextで取得したオブジェクトを登録し、直接オブジェクトを編集してしまうことのように見えます。 しかし、この方法では各EOEditingContextのオブジェクトグラフの整合性を保つ基本的な規則が崩れてしまいます。 2番目のEOEditingContextのオブジェクトを編集するのではなく、1番目のEOEditingContextはオブジェクトのコピーを取得する必要があります。 こうすると元のオブジェクトに影響を与えることなくコピーを編集できます。 1番目のEOEditingContextが変更を保存すると、オブジェクトストアの階層をたどって元のオブジェクトに変更が伝わります。 EOEditingContextに他のEOEditingContextが管理しているオブジェクトのコピーを取得させるメソッドは faultForGlobalID です。

例えばEOEditingContextをネストしていて、親EOEditingContextが持つオブジェクトのリストを表示するしているものとします。 ユーザはこのリストからオブジェクトを選択でき、さらに子EOEditingContextが使われる「詳細画面」でオブジェクトの詳細を確認したり編集することができます。 このとき子EOEditingContextが選択されたオブジェクトのコピーを取得するには次のようにします。

    EOEditingContext childEC, parentEC; // すでにあるものとして仮定
    Object origObject;                  // すでにあるものとして仮定
    Object newObject;
    newObject = childEC.faultForGlobalID(parentEC.globalIDForObject(origObject),
                                         childEC);
  

origObject はリストからユーザが選択したオブジェクトです。

子EOEditingContextは親オブジェクトストアの origObject に影響を及ぼすことなく newObject に変更を加えることができます。 子EOEditingContextが変更を保存すると、 origObject も同時に更新されます。


親EOObjectStoreからの更新

EOObjectStoreの変更の保存が成功すると、ObjectsChangedInStoreNotificationがポストされます。 EOEditingContextはこの通知を受け取ると、EOObjectStoreで更新されたデータがあればオブジェクトをフォールトに戻すことでオブジェクトを同期します。 たたじデフォルトの設定では、EOEditingContextは未保存の変更を再度オブジェクトに適用し、オブジェクトグラフ内の未保存の変更を維持します。 このように更新後も未保存の変更はそのままですが、スナップショットはEOObjectStoreに合わせて更新されます。

以上の処理は2つのデリゲートメソッドを実装すると処理内容を変更することができます。 デリゲートメソッド editingContextShouldMergeChangesForObject は、EOObjectStoreで更新されたが未保存の変更もある各オブジェクトに対して実行されます。 このメソッドで true を返すと、未保存の変更が更新後のデータにマージされます(デフォルトの処理)。 false を返すと未保存の変更を保たず、オブジェクトを無効にして再度フォールトに戻します。 デリゲートは特殊なマージ方法を実装するためにオブジェクトに関する情報(グローバルIDやスナップショットなど)をキャッシュしておくこともできます。 その場合、無効になる可能性のあるオブジェクトには変更を加えるべきではありません。 一方、デリゲートメソッド editingContextDidMergeChangesObjectsChangedInStoreNotification により未保存の変更がすべてマージされた後に実行されます。 このメソッドはデフォルトでは何もしませんが、editingContextShouldMergeChangesForObject でキャッシュした情報を元にして独自のマージ方法を実装することができます。


オブジェクトグラフ管理の一般的な指針

オブジェクトをフェッチすると、データベースのデータから生成されたオブジェクトのグラフが生成されます。 ここで注目すべき点は、アプリケーションがデータベースではなくオブジェクトグラフを中心にして動作することです。 この違いはEnterprise Objects Frameworkを理解するのに重要な手がかりになります。


データベースについて気にする必要はありません…

Enterprise Objects Frameworを使う主なメリットの一つは、データベースの詳細を心配しなくてもすむようになることです。 データベースとエンタープライズオブジェクトを一旦モデルファイルに定義してしまえば、外部キーを生成したり、オブジェクトの削除方法を管理したり、オブジェクトグラフをデータベースに反映するなどの作業は必要なくなります。

誰もが考える、オブジェクトがリレーションシップを持つ場合について明らかにしておきましょう。 例えば、従業員オブジェクトが部署オブジェクトへのリレーションシップを持つと仮定します。 このリレーションシップがオブジェクトグラフでどう表されるのかというと、従業員オブジェクトがインスタンス変数に部署オブジェクトを持つという単純な関係になります。 また、部署オブジェクトはインスタンス変数に従業員オブジェクトの配列を持ちます。 オブジェクトグラフ内のリレーションシップを操作すると(従業員オブジェクトを別の部署オブジェクトに移すなど)、Enterprise Objects Frameworkはリレーションシップを適切に変更します。 例えば従業員オブジェクトを他の部署オブジェクトに変更するということは、その従業員オブジェクトのインスタンス変数である部署オブジェクトを変更し、移動先の部署オブジェクトに従業員オブジェクトを追加することになります。 データベースに変更を保存する際、Enterprise Objects Frameworkはこれらのオブジェクトグラフの操作を適切にデータベースの操作に変換します。


…しかしオブジェクトグラフについては気にかけておく必要があります

上記のように、普通はオブジェクトグラフ内の変更がどうデータベースに保存されるか気にする必要はありません。 しかし、オブジェクトグラフの管理については気にする必要があります*1。 オブジェクトグラフはアプリケーション中のデータに一意性を与えるので、その一意性を第一に管理すべきです。 例えば従業員オブジェクトがプロジェクトオブジェクトとマネージャオブジェクトへのリレーションシップを持つとします。 新しい従業員オブジェクトを生成する際は、そのオブジェクトのプロジェクトオブジェクトとマネージャオブジェクトへのリレーションシップが適切かどうか確認しなければなりません。

EOEditingContextのオブジェクトグラフの一意性を管理する必要がある際は、オブジェクトを他のEOEditingContextで編集してはいけません。 オブジェクトの同期に重大な問題が発生する可能性があります。 もし複数のEOEditingContextで共通するオブジェクトを扱う必要があるならば、「オブジェクトを複数のEOEditingContext間に渡って使う」で触れたように faultForGlobalID を使います。 このメソッドを使うと元のオブジェクトに影響することなく、他のEOEditingContextでオブジェクトのコピーを編集できるようになります。 このオブジェクトの変更を保存すれば、元のオブジェクトも同時に更新されます。

オブジェクトグラフの一意性を管理するのに必要な習慣の一つは、エンタープライズオブジェクトを決してコピーしないことです(プロパティはコピー可能)。コピーをするとオブジェクトの一意性が崩れてしまいます。 EOEditingContextは元のオブジェクトか、そのオブジェクトのコピーを1つだけ管理することを保証します。 同様に、エンタープライズオブジェクトの equals メソッドをオーバーライドしてはいけません。 Enterprise Objects Frameworkはこのメソッドのインスタンス同士を比較する仕様に依存しています。


Web Objects Frameworkのカスタムオブジェクトのアーカイブ

As of WebObjects 5, the WebObjects Frameworks use Java Serialization to archive objects in a persistent platform independent manner. For general information about Serialization see the java.io package and Object Serialization

Many of the classes provided by the Foundation and EOControl frameworks implement the java.io.Serializable interface. You can archive and unarchive these classes with java.io.ObjectInputStream and ObjectOutputStream objects like any other Serializable objects.

JavaClient and its EODistribution framework as well as some legacy applications still use the NSCoding style archiving. This is essentially deprecated since WebObjects 5. New applications should all use Java Serialization whenever possible.

Serializing EOEnterpriseObjects

Both EOEnterpriseObject and EOEditingContext implement Serializable. Archiving an EOEditingContext saves the context's current state, any pending updates to its enterprise objects, and finally serializes all the enterprise objects managed by that EOEditingContext. An EOEditingContext stores only as much information about its enterprise objects as necessary to reconstitute the object graph later. For example, unmodified objects are stored as simple references (by EOGlobalID) that will allow the context to recreate the object from the database. Thus, the application can store state very efficiently by letting an EOEditingContext archive the enterprise objects. Essentially enterprise objects are only archived and unarchived as faults whenever possible. Updated objects have the delta from their last committed state archived, and inserted objects are archived in their entirety.

EOEditingContext includes several additional methods that affect the archiving and unarchiving of objects: setUsesContextRelativeEncoding and usesContextRelativeEncoding. This is true by default. If usesContextRelativeEncoding returns false, serializing an enterprise object will archive all the properties and all of the state for that object, not just a EOGlobalID, even if it is unmodified. This creates a great deal more data, but can be useful for creating a database independent archive. Such an archive can be passed to others without access to your database, or used to transition between databases.

It also has the methods setSubstitutionEditingContext and substitutionEditingContext. These control which EOEditingContext deserialized enterprise objects are placed into. Unless you have set an explicit substitution context, the EOEnterpriseObjects will be deserialized into the EOEditingContext in the serialized stream (i.e. a transitory EOEditingContext for which you do not have a reference). It can be useful within a WOSession to do:

setSubstitutionEditingContext(defaultEditingContext());
Object results = myObjectInputStream.readObject();
setSubstitutionEditingContext(null);
This places all the unarchived enterprise objects in the context associated with your current session.

Both the substitution editing context and the context relative encoding flag are global settings. It is important to ensure multiple threads do not try to change either setting while a separate thread is archiving or unarchiving EOEnterpriseObjects or EOEditingContexts. If you intend to have several threads potentially Serializing enterprise objects simultaneously, you should create a separate lock object, such as an NSLock, to manage potential contention.


*1 原文は "However, you do not need to concern yourself with managing the object graph itself." 。 4.5のドキュメントでは "you do need" であり、明らかに間違っているので修正した。