JPQLのfetch その1
タイトルの通りだが勉強不足でハマったのでメモ。
Java8 + JPA2.1 + eclipselink2.6
テーブル
team
| id | name |
|---|---|
| 1 | Aチーム |
| 2 | MP |
member
| id | name | teamId |
|---|---|---|
| 1 | ハンニバル | 1 |
| 2 | フェイス | 1 |
| 3 | B.A. | 1 |
| 4 | マードック | 1 |
| 101 | リンチ | 2 |
| 102 | デッカー | 2 |
| 103 | フルブライト | 2 |
Entity
@Entity @Table(name="team") public class Team { @Id private Integer id; private String name; @OneToMany(mappedBy="team") private List<Member> members; } @Entity @Table(name="member") public class Member { @Id private Integer id; private String name; @ManyToOne @JoinColumn(name="teamId") private Team team; } ※getter, setter略
1件取得
@NamedQuery(name="Find", query="SELECT t FROM Team t WHERE t.id = :id") Team t = em.createNamedQuery("Find", Team.class).setParameter("id", 1).getSingleResult(); System.out.println(t.getId()); System.out.println(t.getName()); t.getMembers().forEach(m -> System.out.println("\t" + m.getName()));
実行ログ
SELECT ID, NAME FROM team WHERE (ID = ?) bind => [1]
1
Aチーム
SELECT ID, NAME, teamId FROM member WHERE (teamId = ?) bind => [1]
ハンニバル
フェイス
B.A.
マードック
JPQLでJOINしていないのでgetMembers()の際にSQLが発行される、N+1問題というやつ。
なのでクエリでJOIN FETCHしてやると、memberもまとめて取ってくる。
@NamedQuery(name="Find", query="SELECT t FROM Team t JOIN FETCH t.members WHERE t.id = :id")
SELECT t1.ID, t1.NAME, t0.ID, t0.NAME, t0.teamId FROM member t0, team t1 WHERE ((t1.ID = ?) AND (t0.teamId = t1.ID)) bind => [1]
1
Aチーム
ハンニバル
フェイス
B.A.
マードック
複数件取得
@NamedQuery(name="Find", query="SELECT t FROM Team t") List<Team> temas = em.createNamedQuery("Find", Team.class).getResultList(); for (Team t : teams) { System.out.println(t.getId()); System.out.println(t.getName()); t.getMembers().forEach(m -> System.out.println("\t" + m.getName())); System.out.println("-----"); }
SELECT ID, NAME FROM team
1
Aチーム
SELECT ID, NAME, teamId FROM member WHERE (teamId = ?) bind => [1]
ハンニバル
フェイス
B.A.
マードック
-----
2
MP
SELECT ID, NAME, teamId FROM member WHERE (teamId = ?) bind => [2]
リンチー
デッカー
フルブライト
-----
1行と同様、N+1でデータを取得してくる。
なのでJOIN FETCHをする。
@NamedQuery(name="Find", query="SELECT t FROM Team t JOIN FETCH t.members")
SELECT t1.ID, t1.NAME, t0.ID, t0.NAME, t0.teamId FROM member t0, team t1 WHERE (t0.teamId = t1.ID)
1
Aチーム
ハンニバル
フェイス
B.A.
マードック
-----
1
Aチーム
ハンニバル
フェイス
B.A.
マードック
-----
1
Aチーム
ハンニバル
フェイス
B.A.
マードック
-----
1
Aチーム
ハンニバル
フェイス
B.A.
マードック
-----
2
MP
リンチー
デッカー
フルブライト
-----
2
MP
リンチー
デッカー
フルブライト
-----
2
MP
リンチー
デッカー
フルブライト
-----
なんてこったい。
ということを無くすためにDISTINCTをつける。
@NamedQuery(name="Find", query="SELECT DISTINCT t FROM Team t JOIN FETCH t.members")
SELECT DISTINCT t1.ID, t1.NAME, t0.ID, t0.NAME, t0.teamId FROM member t0, team t1 WHERE (t0.teamId = t1.ID)
1
Aチーム
ハンニバル
フェイス
B.A.
マードック
-----
2
MP
リンチー
デッカー
フルブライト
-----
SQLにdistinctがついた上で、オブジェクトレベルでもTeamでグループ化される。
その2へ続く