정의

엔티티 B와 연관 관계가 설정된 엔티티 A를 조회할 때, 첫 1번의 쿼리로 엔티티 A들을 조회하고,

각 A와 연관된 엔티티 B들을 조회하기 위해 매 A마다 추가적인 쿼리가 발생하는 현상

예시:

-- 1: Post 조회
SELECT * FROM post;

-- + N: 각 Post의 Comment 조회
SELECT * FROM comment WHERE post_id = 1;
SELECT * FROM comment WHERE post_id = 2;
SELECT * FROM comment WHERE post_id = 3;
SELECT * FROM comment WHERE post_id = 4;
SELECT * FROM comment WHERE post_id = 5;

원인

첫 번째 쿼리에서 엔티티 A만을 1차 캐시에 저장한 상태로 반환하고,

이후 비즈니스 로직에서 그 A의 FetchType.LAZY인 필드에 접근하려고 하면(지연 로딩) 연관 객체인 엔티티 B는 1차 캐시에 없기 때문에 매번 추가적인 쿼리를 발생시킴

그렇다고 FetchType.EAGER로 하면 N+1 문제는 발생하지 않아도, 매 조회 시마다 불필요한 데이터를 가져오게 되어 매우 비효율적임, 빈대 잡자고 초가삼간 태우는 격…

해결 방법

Fetch Join

@Query("SELECT p FROM Post p JOIN FETCH p.comments")
List<Post> findAllWithComments();

첫 번째 쿼리에서 명시된 모든 연관 객체를 1차 캐시에 저장