엔티티 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차 캐시에 저장