How to Avoid N+1 Queries

JPAλ₯Ό μ‚¬μš©ν•˜λ©΄ 자주 λ§Œλ‚˜κ²Œ λ˜λŠ” N+1 Query에 λŒ€ν•΄ μ•Œμ•„λ³΄μ•„μš”

References: jojoldu.tistory.com

What are N+1 queries?

ν•˜μœ„ 엔티티듀을 첫 쿼리 μ‹€ν–‰μ‹œ ν•œ λ²ˆμ— κ°€μ Έμ˜€μ§€ μ•Šκ³ , Lazy Loading으둜 ν•„μš”ν•œ κ³³μ—μ„œ μ‚¬μš©λ˜μ–΄ 쿼리가 싀행될 λ•Œ λ°œμƒν•˜λŠ” 문제

How to Avoid N+1 Queries?

1. Join Fetch

쑰회 μ‹œ λ°”λ‘œ κ°€μ Έμ˜€κ³  싢은 Entity Fieldλ₯Ό 지정 ν•˜λŠ” 것

ex)

SELECT a FROM School a JOIN FETCH a.subjects

ν•˜μœ„ EntityκΉŒμ§€ ν•œ λ²ˆμ— 가져와야 ν•  λ•Œλ„ μ‚¬μš©ν•  수 μžˆλ‹€.

ex)

SELECT a from School a JOIN FETCH a.subjets s JOIN FETCH s.teacher

but, 이 방법은 λΆˆν•„μš”ν•œ 쿼리문이 μΆ”κ°€λ˜λŠ” 단점이 μžˆλ‹€

이 fieldλŠ” Eager 쑰회, μ € fieldλŠ” Lazy 쑰회 λ₯Ό ν•΄μ•Όν•œλ‹€ κΉŒμ§€ queryμ—μ„œ ν‘œν˜„ν•˜λŠ” 것은 λΆˆν•„μš”ν•˜λ‹€κ³  λŠλ‚„ 수 μžˆλ‹€.

그럴 λ•Œ, μ•„λž˜μ˜ 방법을 μ‚¬μš©ν•  수 μžˆλ‹€.

2. @EntityGraph

@EntityGraph의 attributePath에 query μˆ˜ν–‰ μ‹œ λ°”λ‘œ κ°€μ Έμ˜¬ fieldλͺ…을 μ§€μ •ν•˜λ©΄, Lazyκ°€ μ•„λ‹Œ Eager 쑰회둜 κ°€μ Έμ˜€κ²Œ λœλ‹€

ex)

@EntityGraph(attributePaths = "subjects")
@Query("SELECT a FROM School a")

μœ„μ™€ 같이 attributePath λ₯Ό μ§€μ •ν•˜λ©΄, 원본 쿼리 (SELECT a FROM School a)의 손상 없이 Eager/Lazy fieldλ₯Ό μ •μ˜ν•˜κ³  μ‚¬μš©ν•  수 μžˆλ‹€.

μΆ”κ°€λ‘œ TearcherκΉŒμ§€ ν•œ λ²ˆμ— κ°€μ Έμ˜€λŠ” query도 μ•„λž˜μ™€ 같이 ν‘œν˜„ν•  수 μžˆλ‹€

@EntityGraph(attributePaths = {"subjects", "subjects.teacher"})
@Query("SELECT a FROM School a")

μ£Όμ˜ν•  점

JoinFetchλŠ” Inner Join, Entity GraphλŠ” Outer Join λΌλŠ” 차이점이 μžˆμŒμ„ μœ μ˜ν•˜μž. κ³΅ν†΅μ μœΌλ‘œ μΉ΄ν…Œμ‹œμ•ˆ κ³±(Cartesian Product) 이 λ°œμƒν•˜μ—¬, Subject 수 만큼 School이 쀑볡 λ°œμƒν•˜κ²Œ λœλ‹€

ν•΄κ²° λ°©μ•ˆ

Solution 1

1:N field의 type을 Set으둜 μ„ μ–Έν•˜κΈ° Set은 쀑볡을 ν—ˆμš©ν•˜μ§€ μ•ŠλŠ” 자료 ꡬ쑰이기 λ•Œλ¬Έμ—, 쀑볡 등둝이 λ˜μ§€ μ•ŠλŠ”λ‹€

ex)

    @OneToMany(cascade = CascadeType.ALL)
    @JoinColumn(name="school_id")
    private Set<Subject> subjects = new LinkedHashSet<>();

Set은 μˆœμ„œκ°€ 보μž₯λ˜μ§€ μ•ŠκΈ° λ•Œλ¬Έμ—, LinkedHashSet을 μ‚¬μš©ν•˜μ—¬ μˆœμ„œλ₯Ό 보μž₯ν•œλ‹€

Solution 2

distinctλ₯Ό μ‚¬μš©ν•˜μ—¬ 쀑볡을 μ œκ±°ν•˜κΈ°

이 뢀뢄은 @Queryμ—μ„œ μ μš©ν•˜λŠ” 것이기 λ•Œλ¬Έμ—, join fetch, @EntityGraphλŠ” λ™μΌν•˜λ‹€

ex)

@Query("select DISTINCT a from School a join fetch a.subjects s join fetch s.teacher")
List<Academy> findAllWithTeacher();
@EntityGraph(attributePaths = {"subjects", "subjects.teacher"})
@Query("select DISTINCT a from School a")
List<Academy> findAllEntityGraphWithTeacher();

Last updated