* 본 내용은 인프런 JPA 기본 강의(김영한)를 정리한 내용을 포함하고 있습니다.
* 지연 로딩
- fetch = FetchType.LAZY
- 프록시로 조회해서 필요한 것만 쿼리문을 날림.(한꺼번에 자동으로 조인해서 데이터를 미리 가져오는 “즉시 로딩”과 반대 개념)
- 프록시의 메소드를 건드려서 실제로 “사용하는 시점”에 초기화가 되어 해당 쿼리문을 사용함
- 실무에서는 가급적 “지연 로딩”만 사용할 것. (즉시 로딩은 N+1 문제를 일으킴)
- @~ToOne은 디폴트가 즉시로딩. LAZY로 설정할 것
- @~ToMany는 디폴트가 LAZY
모든 연관관계를 지연로딩으로 깔기. 그 후, 문제 발생 시 해결방법 :
- 페치 조인 (대부분의 경우 해결 가능)
- 엔티티 그래프
- batch size
N+1 해결 방법 ① : 페치 조인
- JPQL 쿼리문에서 join fetch 키워드 사용
- 연관된 엔티티나 컬렉션을 SQL 한 번에 함께 조회하는 기능. 즉, 원하는 동적인 타이밍에 명시적으로 즉시 로딩이 가능하게 한 것.
- 지연 로딩을 걸어놔도, join fetch가 우선순위
- 필요성
- 일반 조인 실행 시, 연관된 엔티티를 함께 조회하지 않고 select 절에 지정된 데이터만 가져옴→ N+1 문제 발생
- → 다른 데이터는 로딩이 안 되어서, 지연 로딩으로 인해 다른 데이터에 접근이 될 때가 되서야 추가로 관련 쿼리가 나감
- 페치 조인은 SQL 한 번에 연관 엔티티 데이터들을 모두 갖고 옴(즉시 로딩. 객체 그래프를 SQL 한 번으로 조회하는 개념)
- 데이터가 한 번에 로딩되기 때문에, 추가 쿼리가 날아가지 않음.
N+1 해결 방법 ② : @EntityGraph
@EntityGraph란 Data JPA에서 fetch 조인을 어노테이션으로 사용할 수 있도록 만들어 준 기능으로, JPA가 어떤 entity를 불러올 때 이 엔티티와 관련된 entity를 함께 불러와 준다.
Repository의 메서드 위에 어노테이션을 선언하고, 함께 조회하고픈 entity column 명을 적어주면 된다.
ex)
@EntityGraph(attributePaths = {"user"})
N+1 해결 방법 ③ : hibernate.default_batch_fetch_size
이 옵션은 하위 엔티티를 로딩할 때, 한번에 상위 엔티티 ID를 지정한 batch size 만큼 in 쿼리로 로딩해 줌.
다시 말하면, 여기서 batch size 옵션에 할당되는 숫자는 IN 구분에 넣을 부모 엔티티 key(Id)의 최대 갯수를 의미한다.
예를 들어, batch size가 1000이고 상위 엔티티가 board, 하위 엔티티가 comment라고 하자.
원래라면 board 1000개가 담긴 리스트를 순회하면서 comment를 호출하는 코드는 지연 로딩으로 인해 1000개의 추가 쿼리가 발생하게 된다.
반면, batch size 옵션을 사용하면 comment를 최초로 조회하는 시점에
1. 영속성 컨텍스트에서 관리되고 있는 1000개의 board id가
2. comment 조회 쿼리의 in 구문 'where comment.board_id in (?, ?, ?...)'에 포함되어 날아간다.
3. 그렇기 때문에, 단 하나의 comment 조회 쿼리로 1000개의 board가 필요로 하는 모든 comment관련 데이터를 조회해 오게 되는 것이다.
application.yml에 다음과 같은 내용을 추가하면 글로벌하게 적용된다.
spring:
jpa:
properties:
hibernate.default_batch_fetch_size: 1000
+)
join fetch, @EntityGraph 사용 시 카테시안 곱이 발생하여 데이터가 중복됨
→ sol1 : List 대신 Set 사용
→ sol2 : sql문에 distinct 사용 (참고 자료 : https://ykh6242.tistory.com/96)
참고
https://jojoldu.tistory.com/414
https://tecoble.techcourse.co.kr/post/2021-07-26-jpa-pageable/
'프로젝트 기록' 카테고리의 다른 글
정적 팩토리 메서드 (0) | 2022.02.23 |
---|---|
Thread Local이란? (0) | 2022.02.23 |
기타 에러 해결 기록들 (0) | 2022.02.23 |
REST API 가이드 (0) | 2022.02.23 |
[SSAFY 공통 프로젝트] ssafé 회고 (0) | 2022.02.20 |