N+1 이란?
예를 들어 Stuedent 엔티티에 GradeInfo엔티티와 ClassInfo엔티티가 EAGER Loading으로 연관매핑된 경우
@Comment("교실정보")
@ManyToOne //디폴트 값은 (fetch = FetchType.EAGER)
@JoinColumn(name = "class", referencedColumnName = "classId")
private ClassInfo class;
@Comment("학년정보")
@ManyToOne
@JoinColumn(name = "grade", referencedColumnName = "gradeId")
private GradeInfo grade;
아래와 같은 JPA 쿼리메소드를 사용할 때
studentRepository.findAll();
findAll() 수행 시점에 Student 엔티티를 조회하는 select 쿼리와
+ 매핑된 GradeInfo 엔티티를 조회하는 select 쿼리,
+ 매핑된 ClassInfo 엔티티를 조회하는 select 쿼리,
모두 수행된다.
예를 들어 1000개의 Student 엔티티는 각각 1,2,3,4,5,6 이라는 6개 중 하나의 gradeInfo 값을 가질 수 있고
1-1, 1-2, 1-3, 1-4, 1-5, 1-6 부터 6학년 6-6 까지 이라는 총 36개중 하나의 ClassInfo 값을 가질 수 있다하면
전체 Student 를 조회할 시 student에 연관된 GradeInfo 엔티티를 조회하기 위해 총 6번의 select,
ClassInfo 엔티티를 조회하기 위한 36번의 select가 추가로 나감. 이것을 N+1 문제라고 함
왜 join으로 쿼리 생성이 되지 않고 select가 각각 나가는걸까
=> JPA가 메소드 이름을 분석해서 JPQL을 생성하고 실행함.
=> JPQL을 생성할때는 fetch 전략을 참고하지 않기 때문
LAZY Loading 전략을 사용하면 N+1문제가 해결되나?
=> 해당 엔티티에 연관매핑된 엔티티를 조회(사용)할 때 select가 추가로 나가게 됨.
=> 또는 엔티티 return 시에 결국 select하게 됨
=> 해결 안 됨
QueryDSL을 사용해도 N+1 문제가 생기나
QueryDSL은 JPQL 빌더역할을 해주는 것이기 때문에 QueryDSL 로 entity select하는 경우에도 발생함.
leftJoin을 걸어놨지만 join이 되지않고 select가 각각 나간다.
public List<Goods> nplus1test(){
List<Student> result = queryFactory
.select(qStudent)
.from(qStudent)
.leftJoin(qStudent.grade,qGradeInfo)
.leftJoin(qStudent.class,qClassInfo)
.fetch();
entity 전체가 아닌 컬럼을 지정해서 뽑는다면 해결되나
컬럼 지정시 join이 되지만 컬럼을 지정해서 뽑으면 tuple 로 반환되어 불편함
public List<Tuple> nplus1test(){
List<Tuple> result = queryFactory
.select(qStudent.name,qStudent.grade)
.from(qStudent)
.fetch();
for(Tuple tuple : result){
System.out.println(tuple.get(qStudent.name));
System.out.println(tuple.get(qStudent.grade));
}
return result;
}
=> Projections.bean 혹은 Projections.fields를 사용해 DTO를 반환받으면 됨
그래도 entity 전체를 select하고 싶다면
EntityGraph를 사용하면 됨
EntityGraph : DataJPA에서 fetchjoin을 어노테이션으로 사용할 수 있도록 하였다. 연관관계가 지연로딩으로 되어있는 엔티티를 조회할 경우 fetch join을 사용한다.
fetchjoin : select 대상 엔티티와 fetch join이 걸려있는 엔티티를 포함해 join하여 select 함.
EntityGraph 사용 예시
@EntityGraph(attributePaths = {"class","grade"})
List<Student> findAll();
fetchjoin 사용 예시
@Override
public List<Goods> nplus1test(){
List<Goods> result = queryFactory
.select(qStudent)
.from(qStudent)
.leftJoin(qStudent.class,qClassInfo)
.fetchJoin()
.leftJoin(qStudent.grade,qGradeInfo)
.fetchJoin()
.fetch();
return result;
}
'기타' 카테고리의 다른 글
알파스캔 콘퀘스트 32U90G 게이밍 4K UHD 144 (0) | 2024.01.30 |
---|---|
구글 캘린더 스크롤 오류 / 윈도우 스크롤 오류 (0) | 2023.10.23 |
큐센 MK450 고장 / 큐센 DT45 고장 / 큐센 무선키보드 연결 안 됨 (0) | 2023.04.26 |
게임 화면 alt tab 시 신호없음 (0) | 2022.05.03 |
KCA 국가기술자격검정 응시자격 서류제출 방법 / 응시자격 서류제출 우편 방법 / 정보보안기사 응시자격 서류제출 우편 이메일 (0) | 2022.03.25 |