ただし、すべてがそれほど単純ではありません。 遅延初期化は、JDK Dynamic ProxyまたはCGLIBライブラリを使用してプロキシオブジェクトを作成することにより実現されます。 どちらの場合も、適切なgetメソッドのプロキシは、必要なデータを取得するためにHibernateセッションにアクセスすることになります。 後者は、Hibernateセッションがある場合にのみ、オブジェクトの遅延プロパティへのアクセスが可能になることを意味します。 それ以外の場合、オブジェクトプロパティを取得しようとすると、忘れられない例外「LazyInitializationException:プロキシを初期化できませんでした-所有するセッションが閉じられました」が発生します。
開いているセッションを手元に置くことは常に可能なこととはほど遠いことは明らかであり、これは不便をもたらします。 したがって、たとえば、MVCアプリケーションテンプレートで遅延初期化でドメインを使用するには、「 OpenSessionInView 」メソッドに頼らなければなりません。その本質は、リクエスト処理の開始時にセッションを開き、終了時に閉じることを保証するフィルターを作成することです
しかし、アップロードされたデータを含むドメインをHibernateセッション外で使用する必要がある場合はどうでしょうか? たとえば、クライアント/サーバーアーキテクチャの場合、サーバーはいつドメインをクライアントに転送する必要がありますか? もちろん、一般的な場合にデータベースについて何も知らないという理由だけで、クライアント側でセッションを開くという話はありません。 私の意見では、状況から抜け出す唯一の方法は、ドメインオブジェクトを「ドロップ」し、サーバーからクライアントに転送する前に必要なデータでそれらを初期化することです。
アプリケーションのサーバー側が3つのレイヤーで構成されていると想像してください。
- サービス層(クライアントが動作するオブジェクト);
- ビジネスロジックの層(アプリケーションのビジネスロジックを実装するオブジェクト)。
- 永続層(休止状態、ドメイン)。
ドメインクラス自体は、サーバーとクライアントの両方で使用できます(一般に論理的です)。
このシナリオでは、ビジネスロジックレイヤーはHibernateセッション内でドメインプロキシを簡単に使用できます。 この場合のサービス層の役割は、クライアントのビジネスロジック層とデータ構成から必要なデータを取得すること、つまり特定の深さと詳細をコピーすることによりドメインクラスに基づいてDTOオブジェクトを作成することに限定されます。
問題は、この「デプロキシ」を実行する方法だけです。 原則として、これはHibernate自体を使用して実行できます。
public static <T> T initializeAndUnproxy(T entity) {
if (entity == null ) {
throw new
NullPointerException( "Entity passed for initialization is null" );
}
Hibernate.initialize(entity);
if (entity instanceof HibernateProxy) {
entity = (T) ((HibernateProxy) entity).getHibernateLazyInitializer()
.getImplementation();
}
return entity;
}
* This source code was highlighted with Source Code Highlighter .
このようなアプローチは、特定のオブジェクトのすべてのコレクションを初期化し、プロキシから「取得」します。 ただし、いくつかの欠点があります。行内のすべてのコレクションが初期化され、親オブジェクトのみが非プロキシ化されます(少なくとも、非プロキシ化時にオブジェクトの接続グラフをどの程度下げる必要があるかが不明です)。
この状況での解決策は、同じクラスの新しいオブジェクトを作成し、Hibernate基本クラスに対応するプロパティを初期化することにより、ドメインのプロキシ解除を実行する小さなユーティリティクラスを作成することです。 オブジェクトの他のすべてのプロパティは、必要に応じてサービス層によって初期化されます。
私は、このアプローチが最適である、または唯一の正しいアプローチであるとは言いません。 それ以外の問題を解決する方法について何か提案があれば、私はそれらを聞いてうれしいです。