Hibernate - n + 1問題


つの多くの関係を持つ2つの実体があると想像しましょう:製品とproductentry.いくつかのユースケースでは、製品関連の属性のみをロードしたい場合がありますが、他のケースでは、製品をエントリと一緒に読み込む必要があります.
熱心で怠け者は、Hibernateによって提供される2つのフェッチタイプです.lazyはアクセスしたときに希望するエンティティと入れ子になった属性だけを取得します.接続のために熱心な取得タイプを設定することは初心者のための一般的なオプションです、しかし、それが彼らが必要でないとしても、すべての関係の読み込みを必要とするので、それは効率を分解します.加えて、熱心なフェッチタイプを採用すると、すべての時間を活用する力.メソッドレベルではオーバーライドできません.
次のシナリオは、熱心にフェッチする型がOKの間、パフォーマンスの必要性を満たさないので、FETType型が怠惰に戻されることに気づくときです.その後、親エンティティ(例えば、製品)のリストをループして階層的な関係を取得します.コードの立場から、それは正しいように見えます、しかし、これは「N + 1選択」問題が現れるところです.次のクエリがHibernateによって送信されます.
製品の一覧を取得するには、最初のSQLクエリを実行します.
特定の製品(製品につき質問)の製品エントリーを検索するさらなる質問.

その結果.🔵🔴


N + 1 SELECTクエリが生成されます.ここでNは製品の数です.性能がどのように影響を受けるかを見るために、N = 1000または10.000を考慮してください.単一のクエリはあなたの仕事を行うことができますが、10000クエリも行うことができます.あなたの側を選択します.

解決策はこちら


Joinフェッチを使用したカスタムクエリは、“n + 1 select”問題のHibernateソリューションの一つです.別のクエリを使用して各製品の入れ子になった製品レコードをクエリする代わりに、製品を選択して関連するエントリに結合する単一のクエリを構築することができます.Hibernateは、“join fetch”が使用されるとき、入れ子にされた実体を受け入れるように言われます.
カスタム結合フェッチクエリで注釈付きのカスタムメソッドを使用して、エンティティとスプリングリポジトリを検討します.
@Repository
public interface ProductRepository extends JpaRepository<Product, Long> {
    @Query("SELECT DISTINCT s FROM Products p LEFT JOIN FETCH p.entries pe")
    List<Product> findAllWithEntries();
}
結果のリストには、追加クエリを実行せずに入れ子になった製品エントリが設定されます.