38.オブジェクト向けクエリー言語(5)


38.オブジェクト向けクエリー言語(5)


2. JPQL


7.ペッキーコネクタ


fetch(fetch)結合はSQLでいう結合タイプではなく、JPQLで性能を最適化するための機能です.これは、join fetchコマンドとして使用できる関連エンティティまたはコレクションを同時にクエリーする機能です.
JPA標準リストで定義されているパッチ結合構文は次のとおりです.
페치 조인 ::= [ LEFT [OUTER] | INNER ] JOIN FETCH 조인경로
ペチジョーンについて詳しく説明しましょう.
エンティティペア
事前結合を使用してメンバー・エンティティを問い合わせるとともに、関連するチーム・エンティティも表示されるJPQLを表示します.
 select m
 from Member m join fetch m.team
例では、joinの後にfetchを書きます.これにより、メンバー(m)とチーム(m.team)がクエリーする関連エンティティまたはセットがクエリーされます.ちなみに、一般的なJPQL結合とは異なり、m.team以降は別名はなく、ペッキー結合では別名は使用できません.
ヘボネットはまた、ペッキーコネクタに別名を使用することを許可した.
実行されるSQLは次のとおりです.
SELECT
    M.*, T.*
FROM MEMBER M
INNER JOIN TEAM T ON M.TEAM_ID=T.ID
次の図に示すように、ペッキー結合を使用してSQL結合を試みます.

次の図は、SQLで結合した結果です.


エンティティペッキー連結JPQLでは、selectmとしてメンバーエンティティのみが選択されており、実行されるSQLから、メンバーに関連付けられたチームもSELECT M.*、T.*を一緒に照会していることがわかります.そして上の画像を見ると、会員やチームオブジェクトがオブジェクトの図形を維持しながらクエリーされているのが見えます.次の例は、このJPQLを使用するコードです.
String jpql = "select m from Member m join fetch m.team";

List<Member> members = em.createQuery(jpql, Member.class)
                         .getResultList();

for (Member member : members) {
    // 페치 조인으로 회원과 팀을 함께 조회해서 지연 로딩 발생 안 함
    System.out.println("username = " + member.getUsername() + ", " +
        "teamname = " + member.getTeam().getName());
}
出力結果は次のとおりです.
username = 회원1, teamname = 팀A
username = 회원2, teamname = 팀A
username = 회원3, teamname = 팀B
会員とチームが遅延ロードに設定されていると仮定します.メンバーのクエリーにペッキー結合が使用され、チームも一緒にクエリーされるため、関連するチームエンティティはエージェントではなく実際のエンティティです.したがって、関連するチームを使用しても、遅延ロードは発生しません.また、これはエージェントではなく、実際のエンティティです.したがって、関連するチームを使用しても、遅延ロードは発生しません.また、エージェントエンティティではないため、メンバーエンティティが永続性コンテキストから分離された永続状態にある場合でも、関連するチームをクエリーできます.
コレクションペア
今度は一対の多関係の集合をつなぎ合わせる.次の例です.
select t
from Team t join fetch t.members
where t.name = '팀A'
次の例では、クエリチーム(t)とともに、クエリに関連付けられたメンバーのセット(t.members)をペッキー結合で結合します.
SELECT
    T.*, M.*
FROM TEAM T
INNER JOIN MEMBER M ON T.ID = M.TEAM_ID
WHERE T.NAME = '팀A'



上記の例の集合をまとめたJPQLでは、select tとして1つのチームのみが選択され、実行されたSQLでは、チームに関連付けられたメンバーもT.*、M.*を一緒にクエリーしていることがわかります.なお、上図のTEAMテーブルでは「Team A」は1つであるが、MEMBERテーブルとの結合により結果が増加し、上図の結合結果テーブルから同様の「Team A」が2つ検索されていることがわかる.したがって、上記の図のセットから結果オブジェクトのTeams結果を取り出す例では、0 x 100の2つのアドレスの「Team A」が見られる.
次に、コレクションペアリングを使用する例を示します.
1対の複数の組合せは結果を増加させる可能性がありますが、1対の複数の組合せは結果を増加させません.
String jpql = "select t from Team t join fetch t.members where t.name = '팀A'";
List<Team> teams = em.createQuery(jpql, Team.class).getResultList();

for (Team team : teams) {

    System.out.println("teamname = " + team.getName() + ",
      team = " + team);
    
    for (Member member : team.getMembers()) {
        
        // 페치 조인으로 팀과 회원을 함께 조회해서 지연 로딩 발생 안함
        System.out.println(
            "->username = " + member.getUsername() + ",
            member = " + member);
    }
}
出力結果は次のとおりです.
teamname = 팀A, team = Team@0x100
->username = 회원1, member = Member@0x200
->username = 회원2, member = Member@0x300
teamname = 팀A, team = Team@0x100
-> username = 회원1, member = Member@0x200
-> username = 회원2, member = Member@0x300
出力結果から、同じ「チームA」が2回照会された.
プリカップリングとDISTINT
SQLのDISTINCTは重複結果を消去するコマンドです.JPQLのDISTINTコマンドは、SQLにDISTINTを追加するだけでなく、アプリケーションで重複データを消去することもできます.
前の瞬間、コレクションのスペルマップはチームAによって繰り返し検索されました.下記のようにDISTINCTを追加します.
select distinct t
from Team t join fetch t.members
where t.name = '팀A'
まず、DISTINTを使用してSELECT DISTINTをSQLに追加します.しかし、現在ではlowごとにデータが異なり、以下の表のようにSQLのDISTINTは機能しません.
データが異なり、SQLのDISTINT効果は以下のようにはなりません.
トランペット
チーム
会員.
1
チームA
会員.
2
チームA
会員.
次に、distinctコマンドが表示され、重複データがフィルタされます.selectdistintは、チームエンティティの重複を削除することを意味します.したがって、重複するチームAは、下図のように1つだけクエリされる.

コレクションペアリングを使用する例にdistinctを追加すると、出力結果は次のようになります.
teamname = 팀A, team = Team@0x100
-> username = 회원1, member = Member@0x200
-> username = 회원2, member = Member@0x300
ペッキーコネクタと普通のコネクタの違い
プリ結合を使用せずに結合のみを使用するとどうなりますか?
select t
from Team t join t.members m
where t.name = '팀A'
SELECT
    T.*
FROM TEAM T
INNER JOIN MEMBER M ON T.ID = M.TEAM_ID
WHERE T.NAME = '팀A'
上記の例のJPQLでは、チームとメンバーの集合に署名しているので、メンバーの集合も一緒にクエリすることは期待できません.上のSQLのSELECT節から見ると、チームを見て署名したメンバーだけは全く見ません.
JPQLは結果を返す際に関連関係を考慮しない.SELECTセクションで指定したエンティティのみを検索します.したがって、チームエンティティのみが表示され、関連するメンバーのセットは表示されません.会員セットが遅延ロードに設定されている場合は、次の図に示すようなエージェントまたは初期化されていないセットRapperを返します.すぐにロードできない場合は、もう一度クエリーを実行して、会員セットをすぐにロードします.

逆に、ペアリングを使用すると、関連するエンティティが同時にクエリーされます.次の例です.
select t
from Team t join fetch t.members
where t.name = '팀A'
次の例から、SELECT T.*、M.*を使用してチームとメンバーをクエリーしていることがわかります.
SELECT
    T.*, M.*
FROM TEAM T
INNER JOIN MEMBER M ON T.ID = M.TEAM_ID
WHERE T.NAME = '팀A'
パッチ結合の特徴と限界
プリフェッチ結合を使用すると、SQLに関連付けられたエンティティを同時に問い合わせることができ、SQL呼び出しの回数を減らし、パフォーマンスを最適化できます.
エンティティに直接適用されるロード・ポリシーは、アプリケーション全体に影響を与えるため、グローバル・ロード・ポリシーと呼ばれます.ペッキー・ジョーンは、グローバル・ロード・ポリシーを優先しています.たとえば、グローバル・ロード・ポリシーが遅延ロードに設定されている場合でも、JPQLでペッキー結合を使用すると、ペッキー結合とともにクエリーが適用されます.
@OneToMany(fetch = FetchType.LAZY)  // 글로벌 로딩 전략
グローバル・ロード・ポリシーを「≪即時ロード|Immediate Load|emdw≫」に設定して最適化すると、アプリケーションの前に常にすぐにロードされます.もちろん、より速い場合もありますが、全体的には未使用のエンティティがロードされることが多く、パフォーマンスに影響を与えます.したがって、可能な場合はグローバル・ロード・ポリシーを使用し、最適化が必要な場合はプリロード・ポリシーを使用します.
また、プリフェッチ結合を使用すると、クエリーポイントで関連付けられたエンティティをクエリーできます.したがって、遅延ロードは発生しません.したがって,準永久状態でもオブジェクトグラフィックをブラウズすることができる.
プリ結合には次の制限があります.

  • プリ結合されたオブジェクトに別名を付けることはできません.
    -構文をよく見ると、スペルマップに別名の内容は定義されていません.したがって、SELECT、WHEREセクションおよびサブクエリでは、プリ結合ターゲットを使用することはできません.
    -JPA規格ではサポートされていないが、Hypernetを含むいくつかのインプリメンテーションは、パラレルポートの別名をサポートする.ただし、エイリアスが適切に使用されないと、関連するデータの数が変化し、データの整合性が損なわれる可能性があるため、注意して使用する必要があります.特に、2番目のキャッシュと一緒に使用する場合は、関連付けられたデータが異なる状態で2番目のキャッシュに格納されている場合、他の場所でクエリーされると、関連付けられたデータの数も変化することに注意してください.

  • 2つ以上のコレクションは着用できません.実施体によっても、集合*集合のCathesian積が生じるので注意が必要である.HyperNation:[javax.persistence.PersistenceException:org.hibernate.loader.M u l t ipleBagFetchException:cannotで複数のパッケージを同時に取得]例外を使用します.

  • コレクションをペアリングする場合は、「ページングAPI」(setFirstResult,setMaxResults)は使用できません.
    -非集合(1対複数)の単一値関連フィールド(1対1、複数対1)は、ページング結合を使用できます.
    -hyvernate上でコレクションをページングし、ページングAPIを使用して警告ログを残し、メモリ内でページング処理を行います.データが少なすぎるのは構わないかもしれませんが、データが多すぎるとパフォーマンスの問題やメモリの過剰な異常を引き起こす可能性があります.これは危険です.
  • プリフェッチ結合では、SQLに関連付けられた複数のエンティティを一度に問い合わせることができ、パフォーマンスの最適化に役立ちます.そして実務でよく使われています.しかし、すべてのことをペッキー・ジョーンで解決することはできません.オブジェクトシェイプを維持するときにプリフェッチ結合を使用するのは有効です.逆に、エンティティの形状ではなく複数のテーブルを結合することによってまったく異なる結果を生成する必要がある場合、複数のテーブルで必要なフィールドのみをクエリーしてDTOに戻るのは、事前結合を強制するよりも効果的です.
    リファレンス
  • Java ORM標準JPAプログラミング