🥭

JPAのFetchTypeとは

2022/06/06に公開

FetchType

今まで多様なリレーショナルマッピングを勉強しながら説明してなかったこと一つがあります。それはFetchTypeというものです。このFetchTypeはJPAの性能を決めるとっても重要なものです。

  • FetchTypeはDBからデータを持ってくるための戦略を決めるものです。
  • FetchTypeにはEAGER戦略とLAZY戦略があります。

EAGERローディングとLAZYローディング


単純なMemberのデータだけを使用するビジネスロジックでMemberを照会する時Teamも一緒に照会しなければならないですか?

LAZYローディング(FetchType.LAZY)

@Entity 
public class Member {
 private String name; 
 @ManyToOne(fetch = FetchType.LAZY) // LAZY
 @JoinColumn(name = "TEAM_ID") 
 private Team team;
 ... 
} 
member.getTeam(); // この時点ではProxy
memger.getTeam().getTeamName(); // この時点で初期化され本物のデータを持ってくる
  • FetchType.LAZYを設定して照会する場合Proxyが照会される。
  • 本物の値またはメソッドを使う前までは初期化されない。

EAGERローディング(FetchType.EAGER)

@Entity
public class Member {
 private String name;
 @ManyToOne(fetch = FetchType.EAGER) // EAGER
 @JoinColumn(name = "TEAM_ID")
 private Team team;
... 
} 
  • Memberを照会する時Teamも即時に持ってくる。
  • @ManyToOneEAGER戦略が基本値。
  • Member一個照会する時TeamJOINを利用してクエリ一発で照会
    • JPAがやってくれる性能最適化:em.find(Member.class, 1L)
  • JPQLを使用するときは性能最適化してくれない
    • JPQL : select m from Member m;すると
    • SQL : select * from Member;と翻訳される。
    • つまりMemberオブジェクト内にTeamオブジェクトがEAGERに設定されてるにも関わらずMemberのみを照会し、照会した後Member内のTeamEAGERである事を確認し、また、クエリを投げTeamを照会する
    • またMemberがいくつかあってTeamもいくつかある時Memberを照会するとその個々のMemberに関わっているTeamを呼び出すために考えられないクエリが追加で投げられる、詰まり、N+1問題が発生する。
    • N+1問題は他の記事で詳しく説明します。

まとめ

  • なるべく実務ではLAZYローディングを使用することがいい。
  • EAGERを適用すると予想できないクエリが投げられる。
  • EAGERはJPQLでN+1問題を引き起こす。
  • @ManyToOne, @OneToOneは基本値がEAGER、LAZYに変更すべき
  • @OneToMany, @ManyToManyは基本値がLAZY
  • 全てのリレーショナルマッピングでLAZYを使用しましょう。
  • 実務ではEAGERではなくJPQLのfetch joinやエンティティグラフを使用しましょう。
select m from Member m join fetch Team;

Discussion