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へ続く