엔티티를 DTO로 반환 - 페치조인 최적화
@GetMapping("/api/v3/orders")
public List<orderDTO> orderV3(){
List<order> orders = orderRepository.findAllWithItem();
List<orderDTO> collect = orders.stream()
.map(o -> new orderDTO(o))
.collect(collections.toList());
return collect;
//여기 까진 v2와 동일
}
public List<order> findAllWithItem(){
return em.createQuery(
"select o from Order o "+
" join fetch o.member m" +
" join fetch o.delivery d" +
" join fetch o.orderItems o" +
" join fetch o.item i" , order.class)
.getResultList();
}
이 쿼리로 조회하면 예제에서 조회되는 order는 2개인데 orderItem이 4개가 조회되어서 모든 경우의 수가 나오는 특성상
최종 결과도 4개가 나오게 된다, 이는 order가 중복된 형태로
join에 의해 결과가 Collection만큼 뻥튀기 된 경우이다.
그래서 중복을 제거하기 위해 Distinct를 쓰는데
JPA에 Distict는 DB의 Distinct와 조금 다른 기능을 가진다.
em.createQuery(
"select distinct o from Order o "+
" join fetch o.member m" +
" join fetch o.delivery d" +
" join fetch o.orderItems o" +
" join fetch o.item i" , order.class)
.getResultList();
JPA에서 Distinct를 사용하려면 쿼리문 select 절에 Distinct를 붙여준다.
그리고 원래 DB에서 Distinct 컬럼값이 완전히 동일한 컬럼만 없애주는데
JPA에서 Distinct는 메인 테이블의 컬럼값만 동일하면 데이터를 생략해주는 기능이 있다.
즉 from절에 엔티티의 PK값이 동일한 컬럼을 애플리케이션에서 제거해준다.
결과 뻥튀기는 일대다 관계에서만 발생하는 문제이다.
요점은 fetch join으로 쿼리가 딱 1번만 실행되는 것
그렇다면 일대다 fetch join에는 제약조건이 존재하는데 페이징이 불가능하다는것.
.setFirstResult(0)
.setMaxResult(100)
위 페이징 함수의 쿼리가 아예 나가지 않는다.
fetch join과 페이징을 같이 쓰게 되면 메모리에서 페이징을 처리해 out of memory가 발생할 수 있다.
일대다 패치조인에 페이징을 하는순간 기준이 from절에서 이상한 기준으로 변경되어 버린다.
그래서 일대다 패치조인에서 페이징을 써서는 안된다 (매우 위험)
컬렉션(다) 일대다 fetch join은 한 쿼리에 1개만 사용할 수 있다.
만약 2개이상 사용하면 데이터가 부정확하게 조회돌 수 있다.
*fetch join 에서 페이징함수를 처리할수 없다,
'JPA' 카테고리의 다른 글
| 3월 3일 JPA 주문조회 v4 (0) | 2023.03.03 |
|---|---|
| 2월 27일 주문조회 V3.1 (0) | 2023.02.27 |
| 2월 27일 JPA 주문조회 V2 (0) | 2023.02.27 |
| 2월 24일 JPA API개발 고급 , 주문조회 v1 (0) | 2023.02.24 |
| 2월 24일 JPA 간단한 주문조회 V4 (0) | 2023.02.24 |