본문 바로가기

JPA

2월 27일 JPA 주문조회 V3

엔티티를 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