JPA를 이용하고, post 엔티티의 writer 필드에 user가 있다고 가정해보자.
그럼 post를 전부 조회하게 되면 글을 쓴 user들 수 만큼 user 테이블을 조회하게 된다...
(post의 writer를 보고 그 때마다 user를 조회하니까)
그럼 글 쓴 사람이 100명이면? 글 조회 1번에 유저 조회 100번이 따라온다는 소리다. 이런 건 좋지 않다...
일단 lazy를 생각할 수 있다.
lazy를 쓰면 post 조회 이후 필요해야 user 조회를 한다. (post.writer를 호출할 때라던가)
@field:ManyToOne(fetch = FetchType.LAZY)
그럼 post만 필요한 경우라면? 조회는 1번으로 끝난다.
와! 정말 좋은 방법이야! ...싶지만 이건 모든 post.writer를 호출하게 된다면 여전히 100번 조회하게 된다.
그러니까 N+1은 피할 수 없다... EAGER든 LAZY든 피할 수 없는 것이다...
대신 LAZY는 DB 최적화 할 때는 좋다.
val query = jpaQuery
.selectFrom(post)
.join(post.writer)
.fetchJoin()
fetchJoin을 쓰면 연관된 데이터까지 다 가져온다.
그럼 하나씩 지연로딩으로 가져오던 걸 한 번의 조인 쿼리로 모두 로딩한다.
하지만...
이렇게 하면 EAGER일 때와 LAZY일 때 같은 방법으로 호출하게 된다. 즉, Lazy의 장점을 잃게 되는 것이다. (메모리상 들고 있는 데이터도 너무 많고!)
그리고 너무 많은 연관 관계(OneToMany)를 한 번에 fetch join하면 데이터 터진다.
점점 어카묘ㅠㅠ 상태가 되는 것 같다...
그럼 문제를 다시 생각해보자. 결국 한번에 하나씩 조회해서 이꼴이 난게 아닌가? 그럼 한번에 가져오면 되는 거 아닌가?
즉, 트레이(Size=트레이 크기)에 담아서 한번에 가져오는 거다.
//엔티티 필드 위 어노테이션 (특정 필드만 설정)
@field:BatchSize(size = 100)
//yml 파일의 jpa 설정으로 전역 설정
default_batch_fetch_size: 100
이러면 조회가 join에서 in이 된다. (=해당하는 값만 전부 가져옴)
그럼 쿼리는 2번이지만 메모리에 유리하다!
그리고 어 저는 특정 부분에서 fetchJoin 쓰고 싶어요 해도 쓸 수 있다!
물론 사이즈도 특정 부분별 자유롭게 조정 가능~
(근데 보통 100 쓰긴 함)
(전체 적용보다 개별 적용이 우선시 됨)