Spring JPQL을 사용하면 join과 fetch join이 존재한다.
join은 JPQL에서 조회하는 주체가 되는 엔티티만 조회하고 fetch join은 조회의 주체가 되는 것 뿐만 아니라 fetch join이 걸린 연관 엔티티도 모두 함께 select하여 영속화한다.
이 덕분에 fetch join을 사용하면 Fetch Type을 Lazy로 선언하더라도 한번에 호출되어 N+1 문제가 발생하지 않는다.
@Entity
public class Study {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long studyId;
@NotNull
private LocalDateTime startAt;
@OneToMany(mappedBy = "study")
private List<StudyEnrollment> enrollments = new ArrayList<>();
}
@Entity
public class StudyEnrollment {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long studyEnrollmentId;
@ManyToOne
@JoinColumn(name = "study_id")
private Study study;
}
다음과 같이 스터디 시작일을 갖는 스터디와 스터디 등록 엔티티가 존재한다고 하자.
@Repository
public interface StudyRepository extends JpaRepository<Study, Long> {
@Query(value = "select distinct s from Study s join fetch s.enrollments where s.startAt <= ?1")
List<Study> findAllPreparedAt(LocalDateTime dateTime);
}
for (Study study : studyRepository.findAllPreparedAt(LocalDateTime.now())) {
for (StudyEnrollment studyEnrollment : study.getEnrollments()) {
// studyEnrollment를 통한 비즈니스 로직
studyEnrollment.start();
}
}
위와 같이 시작일이 특정 시간 이후인 스터디 리스트를 조회하고 해당 스터디 리스트의 enrollments들을 조회하고 싶을 때, 일반 join을 사용한다면 모든 스터디마다 enrollments를 조회하는 쿼리가 추가로 호출되는 것을 볼 수 있다.
하지만 fetch join을 사용하여 하나의 쿼리로 스터디와 스터디 등록 엔티티 모두를 한번에 조회할 수 있다.