87

私はSpringフレームワークと組み合わせてHibernateを使用する傾向があり、それは宣言型のトランザクション境界機能(@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でごめんなさい)

4

3 に答える 3

26

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

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

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

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

于 2011-02-17T09:44:45.753 に答える
7

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

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

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

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

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

于 2011-02-17T14:45:57.950 に答える
3

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

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

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

于 2011-02-17T09:28:36.313 に答える