JPAとHibernateを使用する場合のJOINとJOIN FETCHの違いは何ですか


183

通常のJOINを使用する場所とJOIN FETCHをどこで使用するかを教えてください。

たとえば、次の2つのクエリがある場合

FROM Employee emp
JOIN emp.department dep

そして

FROM Employee emp
JOIN FETCH emp.department dep

それらの間に違いはありますか?はいの場合、どちらを使用しますか?


2
あなたはここでそれを見つけることができるリンク読ん14.3。協会と入会
Angga 2013

4
私はそのdocumententationを通過しましたが、JOINをどこで使用すればよいか、JOIN FETCHをどこで使用すべきかわかりません。
abbas 2013

2
@oneToOneマッピングをFetchType.LAZYに設定していて、2番目のクエリを使用する場合(DepartmentオブジェクトをEmployeeオブジェクトの一部としてロードする必要があるため)、Hibernateが行うことは、個々のEmployeeオブジェクトごとにDepartmentオブジェクトをフェッチするクエリを発行することです。 DBからフェッチします。コードの後半で、EmployeeからDepartmentへの単一値の関連付けを介してDepartmentオブジェクトにアクセスする場合があり、Hibernateは指定されたEmployeeのDepartmentオブジェクトをフェッチするクエリを発行しません。Hibernateがフェッチした従業員の数に等しいクエリを発行することを忘れないでください。
Bunti

ドキュメントハントを支援するために〜フェッチ戦略
Eddie B

1
@ShameeraAnurangaその場合、LEFT OUTER JOINが必要になると思います。
abbas

回答:


180

この2つのクエリでは、JOINを使用して、少なくとも1つの部門が関連付けられているすべての従業員をクエリしています。

しかし、違いは次のとおりです。最初のクエリでは、HibernateのEmployesのみが返されます。2番目のクエリでは、従業員関連するすべての部門を返しています。

したがって、2番目のクエリを使用する場合、データベースを再度ヒットして各従業員の部門を表示するために新しいクエリを実行する必要はありません。

各従業員の部門が必要であることが確実な場合は、2番目のクエリを使用できます。部門が必要ない場合は、最初のクエリを使用します。

WHERE条件(おそらく必要になるもの)を適用する必要がある場合は、このリンクをお読みになることをお勧めします。JPQLの "join fetch"を "where"句でJPA 2 CriteriaQueryとして適切に表現するにはどうすればよいですか。

更新

使用せずfetch、Departmentsが引き続き返される場合は、EmployeeとDepartment(a @OneToMany)の間のマッピングがで設定されているためFetchType.EAGERです。この場合、すべてのHQL(with fetchまたはnot)クエリFROM Employeeはすべての部門をもたらします。すべてのマッピング* ToOne(@ManyToOneおよび@OneToOne)は、デフォルトでEAGERであることに注意してください。


1
フェッチせずにステートメントを実行して結果を取得した場合の動作は次のとおりです。次に、セッション内で部署に扱いますか?
gstackoverflow 2015年

1
はい、@gstackoverflow
Dherik

関係の両側でレイジーフェッチを使用したネイティブクエリを使用していますが、子関係の階層をロードします。
バダムチ

fetch(例を使用して)Department属性で注文したい場合に使用する必要があることに言及する価値があります。それ以外の場合、(少なくともPGで有効)あなたは得るかもしれませんERROR: for SELECT DISTINCT, ORDER BY expressions must appear in select list
長い

60

私が前にコメントで述べたこのリンクで、この部分を読んでください:

「フェッチ」結合を使用すると、値の関連付けまたはコレクションを、単一の選択を使用して親オブジェクトとともに初期化できます。これは、コレクションの場合に特に役立ちます。これは、関連付けとコレクションのマッピングファイルの外部結合と遅延宣言を効果的にオーバーライドします

この「JOIN FETCH」は、エンティティ内のコレクションの(fetch = FetchType.LAZY)プロパティがある場合に効果があります(以下の例)。

そしてそれは「クエリがいつ起こるべきか」という方法にのみ影響します。そして、あなたもこれを知っている必要があります:

hibernateには2つの直交する概念があります。関連付けがいつフェッチされ、どのようにフェッチされるかです。それらを混同しないようにすることが重要です。フェッチを使用してパフォーマンスを調整します。lazyを使用して、特定のクラスのデタッチされたインスタンスで常に使用可能なデータのコントラクトを定義できます。

関連付けがフェッチされるのはいつですか->「フェッチ」タイプ

フェッチ方法->結合/選択/副選択/バッチ

あなたの場合、エンティティ内に次のようなEmployee内のセットとして部門がある場合にのみ、FETCHが効果を発揮します。

@OneToMany(fetch = FetchType.LAZY)
private Set<Department> department;

あなたが使うとき

FROM Employee emp
JOIN FETCH emp.department dep

あなたが取得empemp.depます。フェッチを使用しなかっemp.depた場合でも取得できますが、Hibernateはデータベースに対して別の選択を処理して、その部門のセットを取得します。

したがって、パフォーマンスチューニングの問題であり、1つのクエリですべての結果(必要かどうか)を取得したい(イージーフェッチ)、または必要なときに後者をクエリしたい(遅延フェッチ)ことについてです。

1つの選択(1つの大きなクエリ)で小さなデータを取得する必要がある場合は、熱心なフェッチを使用します。または、遅延フェッチを使用して、必要なものをクエリします(多くの小さいクエリ)。

次の場合にフェッチを使用します。

  • 取得しようとしているエンティティ内に、不要な大きなコレクション/セットはありません

  • アプリケーションサーバーからデータベースサーバーへの通信が遠すぎて時間がかかる

  • あなたはそれへのアクセス(持っていないときは、そのコレクションは、後者の必要があるかもしれない取引方法/クラス)


更新された質問で書いたばかりのクエリについて説明してください。
abbas 2013

便利な考慮事項:「取得しようとしているエンティティ内に大きな不要なコレクション/セットがない」
Divs

従業員内部の部門がのList代わりにだった場合、部門は依然として熱心にフェッチされますかSet
ステファン

FETCHJPQLステートメントでキーワードを使用することは、熱心に取得されたプロパティを意味しますか?
ステファン

15

参加する

JOINエンティティの関連付けに対して使用する場合、JPAは、生成されたSQLステートメントで、親エンティティと子エンティティテーブルの間にJOINを生成します。

したがって、例として、このJPQLクエリを実行すると、次のようになります。

FROM Employee emp
JOIN emp.department dep

Hibernateは次のSQLステートメントを生成します。

SELECT emp.*
FROM employee emp
JOIN department dep ON emp.department_id = dep.id

SQL SELECT句にはemployeeテーブルの列のみが含まれ、列は含まれないことに注意してくださいdepartmentdepartmentテーブルの列を取得するには、のJOIN FETCH代わりにを使用する必要がありますJOIN

結合フェッチ

そのためJOINJOIN FETCHではSELECT、生成されたSQLステートメントの句に結合テーブルの列を投影できます。

したがって、あなたの例では、このJPQLクエリを実行するとき:

FROM Employee emp
JOIN FETCH emp.department dep

Hibernateは次のSQLステートメントを生成します。

SELECT emp.*, dept.*
FROM employee emp
JOIN department dep ON emp.department_id = dep.id

今回departmentは、FROMJPQL句にリストされているエンティティに関連付けられている列だけでなく、テーブルの列も選択されていることに注意してください。

また、フェッチするメインエンティティと共にフェッチ戦略を使用してエンティティの関連付けを初期化できるJOIN FETCHため、LazyInitializationExceptionHibernateを使用する場合に対処する優れた方法FetchType.LAZYです。


同じクエリで複数のJOIN FETCHを使用することは可能ですか?
A.Onurオズカン

2
複数の多対1および1対1の関連付けと最大1つのコレクションをFETCHすることができます。1対多または多対多の関連付けなど、複数のコレクションをフェッチすると、最終的にデカルト積になります。ただし、複数のコレクションをフェッチする場合は、2番目、3番目、...、n番目のコレクションにセカンダリクエリを使用できます。チェックアウトこの記事を詳細については。
Vlad Mihalcea

5

あなたがしている場合@oneToOneにセットをマッピングFetchType.LAZYHibernateは何をするか(あなたは部門は従業員オブジェクトの一部としてロードするオブジェクト必要があるため)されていて、2番目のクエリを使用し、それが部門は、それはDBからのフェッチごとに個別のEmployeeオブジェクトのためのオブジェクトを取得するためにクエリを発行します。

その後、コードでEmployeeからDepartmentへの単一値の関連付けを介してDepartmentオブジェクトにアクセスし、Hibernateはクエリを発行して、指定されたEmployeeのDepartmentオブジェクトをフェッチしません。

Hibernateは、フェッチした従業員の数に等しいクエリを発行することに注意してください。すべてのEmployeeオブジェクトのDepartmentオブジェクトにアクセスする場合、Hibernateは上記の両方のクエリで同じ数のクエリを発行します


2

Dherik:何を言っているのかわかりません。フェッチを使用しない場合、結果のタイプは:List<Object[ ]>Employeeのリストではなく、Objectテーブルのリストになります。

Object[0] refers an Employee entity 
Object[1] refers a Departement entity 

フェッチを使用すると、選択は1つだけになり、結果はList<Employee>部門のリストを含む従業員のリストになります。エンティティの遅延宣言をオーバーライドします。


私はあなたの懸念を理解しているかどうかわかりません。を使用しない場合fetch、クエリは従業員のみを返します。この場合でも、Departmentsが引き続き返されるのは、EmployeeとDepartmentの間のマッピング(@OneToMany)がFetchType.EAGERで設定されているためです。この場合、すべてのHQL(with fetchまたはnot)クエリFROM Employeeはすべての部門をもたらします。
デリック

fetchを使用しない場合(結合期間のみ)、結果は2行のコレクションの配列になります。最初の行はEmployeesのコレクションで、2番目はDepartmentsのコレクションです。イーガーフェッチまたはレイジーフェッチを使用して、部門がフェッチされます。
ビラルBBB 2015年

HQLでのフェッチがない場合、これは、従業員と部門間のマッピングがEAGER(@OneToMany(fetch = FetchType.EAGER)の場合にのみ発生します。そうでない場合、部門は返却されません。
デリック

@Dherikは自分で試してください。ClassCastExceptionが発生します。
ビラルBBB

私は問題を理解します。フェッチの問題ではありませんselectが、HQLでどのように作成されたかを示します。お試しくださいSELECT emp FROM Employee emp JOIN FETCH emp.department dep。JPA / Hibernateは、パーツを省略した場合のreturn a Listの動作Object[]を備えていますSELECT
Dherik、2015年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.