Hibernateの遅延ロードアプリケーションの設計


87

私はHibernateSpringフレームワークと組み合わせて使用する傾向があり、それは宣言型のトランザクション境界機能(@Transactionalなど)です。

誰もが知っているように、休止状態はできるだけ非侵襲的透明性保つように努めていますが、関係を採用する場合これは少し難しいことがわかりますlazy-loaded


透明度のレベルが異なるデザインの選択肢がいくつかあります。

  1. 関係を遅延ロードしないようにします(例: fetchType=FetchType.EAGER)
    • これは、遅延読み込みのアイデア全体をバイオライトします。
  2. を使用してコレクションを初期化する Hibernate.initialize(proxyObj);
    • これは、DAOへの比較的高い結合を意味します
    • でインターフェースを定義することはできますがinitialize、他の実装が同等のものを提供することは保証されていません。
  3. 永続Modelオブジェクト自体にトランザクション動作を追加します(動的プロキシまたはを使用@Transactional
    • @Transactionalが永続オブジェクト自体で機能するようには見えませんでしたが、動的プロキシアプローチを試したことはありません。おそらくその休止状態が原因で、プロキシでの操作が行われています。
    • トランザクションが実際に行われているときの制御の喪失
  4. 両方の怠惰/非怠惰なAPIを提供し、例えば、loadData()およびloadDataWithDeps()
    • アプリケーションに、どのルーチンをいつ使用するかを強制的に認識させます。これも密結合です。
    • メソッドオーバーフローloadDataWithA()、、 ....、loadDataWithX()
  5. byId()操作 のみを提供するなどして、依存関係のルックアップを強制します
    • 非オブジェクト指向のルーチン、例えば、の多くを必要とfindZzzById(zid)し、その後getYyyIds(zid)の代わりに、z.getY()
    • トランザクション間に大きな処理オーバーヘッドがある場合は、コレクション内の各オブジェクトを1つずつフェッチすると便利です。
  6. DAOだけでなく、アプリケーションの一部を@Transactionalにします。
    • ネストされたトランザクションに関する考えられる考慮事項
    • トランザクション管理に適合したルーチンが必要です(例:十分に小さい)
    • プログラムによる影響は小さいが、トランザクションが大きくなる可能性がある
  7. DAOに動的フェッチプロファイルを提供しますloadData(id, fetchProfile);
    • アプリケーションは、いつ使用するプロファイルを知っている必要があります
  8. AoPタイプのトランザクション。たとえば、操作をインターセプトし、必要に応じてトランザクションを実行します。
    • バイトコード操作またはプロキシの使用が必要
    • トランザクションが実行されるときの制御の喪失
    • いつものように、黒魔術:)

オプションを逃しましたか?


lazy-loadedアプリケーション設計における関係の影響を最小限に抑えるために、どのアプローチをお勧めしますか?

(ああ、WoTでごめんなさい)


オプション2と5の例:m-hewedy.blogspot.ch/2010/03/…–
Adrien Be

オプション4の例を教えてください。
degreesightdc

回答:


26

誰もが知っているように、休止状態はできるだけ非侵襲的で透明性を保つように努めています

最初の仮定は間違っていると思います。アプリケーションは常にエンティティのライフサイクルとロードされるオブジェクトグラフのサイズを処理する必要があるため、透過的な永続性は神話です。

Hibernateは思考を読み取ることができないため、特定の操作に特定の依存関係のセットが必要であることがわかっている場合は、何らかの方法でHibernateへの意図を表現する必要があります。

この観点から、これらの意図を明示的に表現するソリューション(つまり、2、4、および7)は合理的に見え、透明性の欠如に悩まされることはありません。


もちろん、あなたは正しいです、可能な限り透明なものは今のところしか機能しません。それらはあなたが行ったいくつかの素晴らしい選択です。
ヨハン・シェーベルイ

私見:完全に正解です。確かに、それは神話です。ところで:私の投票はオプション4と7になります(またはORMから完全に離れます)
G. Demecki 2015年

7

どの問題(怠惰が原因)を示唆しているのかわかりませんが、私にとって最大の問題は、自分のアプリケーションキャッシュでセッションコンテキストが失われないようにすることです。典型的なケース:

  • オブジェクトfooがロードされ、マップに配置されます。
  • 別のスレッドがマップからこのオブジェクトを取得して呼び出しますfoo.getBar()(これまで呼び出されたことはなく、遅延評価されるもの)。
  • ブーム!

したがって、これに対処するために、いくつかのルールがあります。

  • セッションを可能な限り透過的にラップします(例:OpenSessionInViewFilterwebapps)。
  • スレッド/スレッドプール用の共通APIがあり、dbセッションのバインド/アンバインドが階層の上位(ラップされているtry/finally)で実行されるため、サブクラスはそれについて考える必要がありません。
  • スレッド間でオブジェクトを渡すときは、オブジェクト自体ではなくIDを渡します。受信スレッドは、必要に応じてオブジェクトをロードできます。
  • オブジェクトをキャッシュするときは、オブジェクトではなくIDをキャッシュしてください。IDがわかっている場合は、DAOまたはマネージャークラスに抽象メソッドを設定して、第2レベルのHibernateキャッシュからオブジェクトをロードします。第2レベルのHibernateキャッシュからオブジェクトを取得するコストは、DBに移動するよりもはるかに安価です。

ご覧のとおり、これは非侵襲的で透明性に近いものではありません。しかし、熱心な読み込みに支払わなければならない価格と比較すると、コストはまだ耐えられます。後者の問題は、エンティティのコレクションはもちろん、単一の参照オブジェクトをロードするときにバタフライ効果が発生する場合があることです。控えめに言っても、メモリ消費量、CPU使用率、レイテンシーもはるかに悪いので、私はそれで生きることができると思います。


お返事をありがとうございます。の損失はtransparency、アプリケーションに遅延オブジェクトの読み込みを強制することによるものです。すべてが熱心にフェッチされた場合、アプリケーションはオブジェクトがデータベースに永続化されているかどうかを完全に認識しない可能性がありますFoo.getBar()。これは常に成功するためです。> when passing objects between threads, pass IDs、はい、これは#5に対応します。
ヨハン・シェーベルイ

3

非常に一般的なパターンは、Webアプリケーションを構築している場合にOpenEntityManagerInViewFilterを使用することです。

サービスを構築している場合、メソッドが複数のエンティティを取得または更新する必要があることが非常に多いため、DAOではなくサービスのパブリックメソッドでTXを開きます。

これにより、「遅延読み込みの例外」が解決されます。パフォーマンスチューニングのためにより高度なものが必要な場合は、フェッチプロファイルが最適な方法だと思います。


1
私はあなたが言いたいことを推測:A非常に一般的なアンチパターンを...。TXをサービスレベルで開くことに同意しますが、の使用OSIVは依然としてアンチパターンであり、例外に適切に対処できない、パフォーマンスの低下など、非常に深刻な問題につながります。要約すると、IMHO OSIVは簡単なソリューションですが、おもちゃのプロジェクトにのみ適しています。
G. Demecki 2015年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.