// Post - User 다대일 관계 (LAZY 기본)@EntitypublicclassPost{@ManyToOne(fetch =FetchType.LAZY)// [**Fetch Type**](/vault/fetch-type) 참고@JoinColumn(name ="author_id")// [**외래 키(Foreign Key)**](/vault/외래-키foreign-key) 컬럼privateUser author;}// N+1 발생 코드@Transactionalpublicvoidproblem(){List<Post> posts = postRepository.findAll();// 쿼리 1번for(Post post : posts){System.out.println(post.getAuthor().getName());// 각 Post마다 쿼리 1번 → N번}// 총 1 + N번 쿼리 실행}
// RepositorypublicinterfacePostRepositoryextendsJpaRepository<Post,Long>{@Query("SELECT p FROM Post p JOIN FETCH p.author WHERE p.status = :status")List<Post>findAllWithAuthor(@Param("status")String status);}// 실행되는 SQL// SELECT p.*, u.* FROM posts p INNER JOIN users u ON p.author_id = u.id// → 쿼리 1번으로 Post + User 동시 조회
publicinterfacePostRepositoryextendsJpaRepository<Post,Long>{// author 연관 객체를 EAGER로 함께 로딩@EntityGraph(attributePaths ={"author"})List<Post>findByStatus(String status);// 여러 연관 객체 동시 로딩@EntityGraph(attributePaths ={"author","tags"})Optional<Post>findById(Long id);}
@EntitypublicclassPost{@ManyToOne(fetch =FetchType.LAZY)@JoinColumn(name ="author_id")@BatchSize(size =100)// 최대 100개 ID를 IN 절로 묶어서 조회privateUser author;}// 또는 글로벌 설정 (application.yml)// spring.jpa.properties.hibernate.default_batch_fetch_size: 100
-- @BatchSize 적용 시 실행 SQLSELECT*FROM users WHERE id IN(1,2,3,...,100)-- N번이 아닌 ceil(N/100)번으로 줄어듦
// DTO 클래스publicrecordPostSummaryDto(Long id,String title,String authorName){}// JPQL로 DTO 직접 반환publicinterfacePostRepositoryextendsJpaRepository<Post,Long>{@Query("""
SELECT new com.example.dto.PostSummaryDto(p.id, p.title, u.name)
FROM Post p JOIN p.author u
WHERE p.status = :status
""")List<PostSummaryDto>findSummaries(@Param("status")String status);}
// 위험: 페이징 + 컬렉션 fetch join → HHH90003004 경고 → 메모리에서 페이징 처리@Query("SELECT p FROM Post p JOIN FETCH p.comments")Page<Post>findAll(Pageable pageable);// 안전한 방법: @BatchSize + 페이징 분리@Query("SELECT p FROM Post p")Page<Post>findAll(Pageable pageable);// 페이징은 Post만// Post 엔티티에 @BatchSize 설정@OneToMany(mappedBy ="post", fetch =FetchType.LAZY)@BatchSize(size =100)privateList<Comment> comments;