03 JPA N+1
03 JPA N+1
1. Note
1. Note
- 마이바티스의 경우에는 SQL을 직접 작성해야하는 불편하지만 복잡한 SQL에서는 편한 편이고,
- 단순한 SQL에서는 JPA가 압도적으로 편한 패턴을 가지는 듯함.
- N+1 패턴은 일단 ORM의 구조적 trade-off일 뿐 해결 불가능한 문제는 아니기 때문에 사용함.
- ORM이 표준이고 반드시 써야한다는 것에 대해서는 모르겠음 (2026.03.17.)
- 상황에 따라서 필요에 따라서 시스템에 맞는 방식을 활용해야할듯 하고,
- MSA면 서비스에 따라서 SQL이 메인으로 활용하는 서비스에서는 분리하고,
- 로직과 단순한 흐름과 관련된 서비스에서는 JPA를 써야하지 않을까.
- 어떻게 해야 메리트가 있는거지?
2. 02JPA_Join -> 1.Note
- Join을 사용하다보면 N+1 패턴이 발생하고 그와 관련된 여러가지 개념이 필요
- 상황
- user(1)와 Order(N) 테이블이 있을때 Lazy 전략을 사용할 경우에,
- 초기에 User를 조회하면, 캐시에 user 테이블정보만 있고 Order는 없음
- 이때 Order가 필요한 경우가 생겨버리면,
- 필요한 Order만큼 N번씩 조회하는 상황이 생김.
- 나는 조회를 1번했지만 캐시에 전체 Order가 없기 때문에 필요한 order 엔티티를 계속 조회하는 상황.
2. N+1 패턴
1. N+1 패턴
- N+1 문제는 연관 엔티티를 조회하는 과정에서 초기 조회 1번 이후 추가로 N번의 쿼리가 발생하는 패턴
- ORM에서 발생하기 쉬운 조심해야하는 문제
2. 발생 하는 상황
1
2
3
4
5
6
user : Order
List<Order> orders = orderRepository.findAll();
for (Order order : orders) {
order.getUser().getName();
}
- 영속성 컨텍스트에 엔티티가 전부 올라가 있지 않은 상태에서
- JPA 특성상 조회에 필요한 엔티티만 DB에서 조회하기 때문에
- 로직에서 1건씩 조회하는 패턴(For문 한정X, 특정 엔티티에 대한 접근 O)이 발생하면
- 반복적으로 특정 엔티티를 select 해야하는 상황이 발생하는데
- 이때, 처음 1건 + 추가조회 N건이여서 N+1 문제가 발생함.
2. JPQL
1. SQL / JPQL
- SQL (테이블 기준) / JPQL(엔티티 기준)
- SQL <-> JPQL
- 기준 테이블
1 2
orders user
- SQL
1 2
SELECT * FROM orders o JOIN user u ON o.user_id = u.id
- JPQL
1
select o from Order o join o.user u
- 기준 테이블
2. 조건 조회
1
2
-- 엔티티기준으로 조회함.
select o from Order o where o.status = 'READY'
3. DTO 리턴
1
2
3
select new com.example.OrderDto(o.id, u.name) -- OrderDto는 id와 name 필드를 가짐.
from Order o
join o.user u
4. Join
1
2
select o from Order o
join o.user u -- 조인은 엔티티 설정값을 보고 결정함.
3. N+1 패턴의 해결
1. Fetch Join
- 레파지토리에서 특정 엔티티를 조회함.
1 2
@Query("select o from Order o join fetch o.user") List<Order> findAllWithUser(); - SQL
1 2 3
SELECT o.*, u.* FROM orders o JOIN user u ON o.user_id = u.id
2. EntityGraph
- 레파지토리에서 조회 대상설정
1 2
@EntityGraph(attributePaths = "user") List<Order> findAll();
3. DTO 조회
- 레파지토리에서 조회 대상을 직접 설정
1 2 3 4 5 6
@Query(""" select new com.example.OrderDto(o.id, u.name) from Order o join o.user u """) List<OrderDto> findOrderDtos();
4. Batch Size
- 하이버네이트 설정을 변경
1 2 3 4
spring: jpa: properties: hibernate.default_batch_fetch_size: 100 - in 패턴으로 묶어서 조회하게됨.
- 조회해야하는 건수가 많을 경우 더욱 복잡한 패턴을 가질 수 있음
- in (‘A’, ‘B’, ‘C’)
This post is licensed under CC BY 4.0 by the author.