次の明らかに文書化されていない問題が発生しました。
- 私は何か悪いことをした
- 誰かが同じ問題に遭遇しましたか?
- 本当にどこにも文書化されていませんか?または私は何かを逃しましたか?
動作はこれです 次のマッピングを想定します
<class name="org.sample.Foo" table="foo">
...
<many-to-one name="bar" class="org.sample.Bar"/>
</class>
<class name="org.sample.Bar" table="bar" lazy="false">
...
</class>
まず、背景として、多対 1 のリレーションのfetch属性の Hibernate のデフォルト値は「 select」である必要があります。これは少なくとも文書化されているものです (見つけたらここにリンクを追加します)。
ただし、これは、参照されるクラスが lazy="true" である場合にのみ当てはまります。
したがって、明らかに上記のマッピングは次のように変換されます (Bar は lazy="false" であるため):
<class name="org.sample.Foo" table="foo">
...
<many-to-one name="bar" class="org.sample.Bar" *fetch="join" />
</class>
<class name="org.sample.Bar" table="bar" lazy="false">
...
</class>
では、なぜそれが問題になるのでしょうか。2 つの選択の代わりに、Hibernate は非遅延参照をその「親」を使用して単一の選択でロードします (単一の選択で Bar を使用して Foo をロードします)。
オブジェクトは遅延していないので、これは実際に理にかなっています。ロードしないのはなぜですか?
答えはこうです: Bar が第 2 レベルのキャッシュにある場合はどうなりますか?
<class name="org.sample.Foo" table="foo">
...
<many-to-one name="bar" class="org.sample.Bar" *fetch="join" />
</class>
<class name="org.sample.Bar" table="bar" lazy="false">
<cache usage="transactional" />
...
</class>
その答えは、何も変わらないということです。
どうやら、Hibernate はこのタイプのオブジェクトをロードしてはならないことを理解できるほどスマートであると想定するでしょうが、デフォルトのフェッチが select から join に変更されたため、Hibernate には選択の余地がありません (実際のテーブルを第 2 レベルのキャッシュ、まだ)
そのため、Hibernate は指示されたとおりに実行し、結合を使用して、既に第 2 レベルのキャッシュにあるデータベースからオブジェクトをフェッチします。
私が見つけた解決策は、文字通りマッピングを fetch="select" に変更することです
Bar の 2 番目の選択が実行されようとすると、Hibernate はそれがデータベースに送信されるべきではないことを認識し、キャッシュからフェッチします。1つのクエリのみが実行されます(ウォームアップ後)