3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

JAVA JPAのN+1問題について

Last updated at Posted at 2021-05-30

GOAL

JAVAでDBMSを操作するためにJPAを使用する際に頻繁に発生する「N+1問題」の発生原因及び解決策の基本を知る。

目次

  1. N+1問題とは
  2. 原因
  3. 解決策
  4. 最後に

N+1問題とは

以下のようなEntity, Repositoryがあるとしましょう。

@Entity
public class Teacher {

    @Id
    @GeneratedValue
    private long id;

    @OneToMany
    private List<Student> students = Lists.newArrayList();
}
@Entity
public class Student {

    @Id
    @GeneratedValue
    private long id;

    @ManyToOne
    private Teacher teacher;
}
public interface TeacherRepository extends JpaRepository<Long, Master> {

    //JpaRepositoryを継承しているため、デフォルトのメソッドは入っている

}

TeacherRepository.findAll()を実行すると以下のようなクエリが発行されることを期待するでしょう。

SELECT * FROM Teacher
LEFT JOIN STUDENT
ON STUDENT.TEACHER_ID = TEACHER.ID

でも実際のクエリを確認すると以下のように
TEACHERを取得するクエリ → 1回
各TEACHERのSTUDENTを取得するクエリ → N回
のクエリが発行され、「N+1問題」が発生してしまいます。
TEACHERの数が多い場合、パフォマンスに致命的な影響を与えることになるでしょう。

SELECT * FROM TEACHER //1回目のクエリ

SELECT * FROM STUDENT WHERE TEACHER_ID = 0 //2目のクエリ
SELECT * FROM STUDENT WHERE TEACHER_ID = 1 //3目のクエリ
SELECT * FROM STUDENT WHERE TEACHER_ID = 2 //4目のクエリ
SELECT * FROM STUDENT WHERE TEACHER_ID = 3 //5目のクエリ
・・・・
SELECT * FROM STUDENT WHERE TEACHER_ID = N  //N+1目のクエリ

原因

JPAから提供しているメソッドはDBにダイレクトにクエリを発行するのではなく、JPQLというオブジェクト指向クエリ言語を生成・実行してからJPAがこれを分析し、SQLを生成してクエリをDBに発行する動きになっているからです。
上記の例の場合の詳細な動きは以下のようになる。

  1. select o from Order o JPQLを分析し、 select * from Teacher SQLを1回発行する
  2. SQLの結果に基づいてTeacherオブジェクトを生成する。
  3. TeacherのStudentオブジェクトを取得するために、SELECT * FROM STUDENT WHERE TEACHER_ID=? SQLをN回発行する

解決策

join fetch適用
カスタムレポジトリを作成してJPQLにてfetch明記すれば問題は簡単に解決できます。
select m from Teacher m join fetch m.students
上記のように明記すると、以下のようなSQLクエリが発行されるようになります。

SELECT * FROM TEACHER
LEFT JOIN STUDENT
ON STUDENT.TEACHER_ID = TEACHER_ID

しかし、join fetchをすることにより、以下のようなデメリットが生じます。
・一発で全てのオブジェクトを取得するため、ページング単位で取得ができなくなり、ページング機能の実現が難しい。
・不要なクエリが増える。


#最後に
当記事ではJPAの「N+1問題」の基本を紹介しているため、
LazyFetch、EagerFetchのようなFetch戦略に関して詳しく説明していないが、「N+1問題」をきちんと理解するためには必須知識であるためFetch戦略は他の記事を参考にしてください。

3
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?