양방향 매핑시 무한루프
한쪽 방향에 @JsonIgnore
Jackson DateType Hibernate5
Config에 Bean으로 등록
강제 초기화
order.getMember().getNmae() //강제초기화
→ Entity를 직접 노출 하는 건 안좋음 DTO로 반환
N + 1 문제 발생
관계 매핑 조회 마다 쿼리 생성
쿼리 작성
em.createQuery(
"select o from Order o" +
" join fetch o.member m" +
" join fetch o.delivery d", Order.class)
쿼리작성
em.createQuery(
"select new me.jiho.repository.OrderDTO(id, date, ,,,,)"
" from Order o" +
" join fetch o.member m" +
" join fetch o.delivery d", OrderDTO.class)
쿼리 작성
em.createQuery(
"select o from Order o" +
" join fetch o.member m" +
" join fetch o.delivery d" +
" join fetch o.orderItems oi" + // join이 되어 리스트 형식이 아닌 order가 여러개
" join fetch op.item i", Order.class)
.getResultList();
Distinct
jpa에서는 distinct의 id가 같으면 중복을 제거해준다 + db의 distinct
OneToMany 조인이 있으므로 같은 Order의 조회수가 증가
em.createQuery(
"select distinct o from Order o" +
" join fetch o.member m" +
" join fetch o.delivery d" +
" join fetch o.orderItems oi" + // join이 되어 리스트 형식이 아닌 order가 여러개
" join fetch op.item i", Order.class)
.getResultList();
페이징이 불가능하다
em.createQuery(
"select distinct o from Order o" +
" join fetch o.member m" +
" join fetch o.delivery d" +
" join fetch o.orderItems oi" + // join이 되어 리스트 형식이 아닌 order가 여러개
" join fetch op.item i", Order.class)
.getFirstResult(1)
.setMaxResults(100)
.getResultList();
컬랙션 패치 조인을 사용하면 페이징이 불가능하다 하이버 네이트는 모든 데이터를 DB에서 읽어오고 메모리에서 페이징을 한다
컬랙션 패치 조인은 1개만 사용할 수 있다 컬랙션 둘 이상에 패치 조인을 사용하면 안된다
페이징 + 컬렉션 엔티티 조회
NTOOne 관계를 모두 패치 조인한다
컬렉션은 지연로딩으로 조회한다
지연로딩 성능 최적화를 위해 hibernate.default_batch_fetch_size @BatchSize를 적용한다
hibernate.default_batch_fetch_size
orders의 orderitems를 size의 수만큼 가져온다
@BatchSize
각각의 BatchSize를 설정
ToOne은 Entity에다가
ToMany는 ToMany에 설정
public List<Order> findAllWithMemberDelivery() {
return em.createQuery(
"select distinct o from Order o" +
" join fetch o.member m" +
" join fetch o.delivery d" Order.class)
.getResultList();
}
public List<OrderDTO> orderV3_page() {
List<Order> orders = orderRepository.findAllMemberDelivery();
List<OrderDto> result = orders.stream()
.map(o -> new OrderDto(o))
.collect(Collectors.toList());
return result;
}
쿼리 호출 수가 1 + N → 1 + 1로 최적화된다
패치 조인 방식과 비교해서 쿼리 호출 수가 약간 증가하지만, DB 데이터 전송량이 감소한다
컬렉션 페치 조인은 페이징이 불가능 하지만 이 방법은 페이징이 가능하다
hibernate.default_batch_fetch_size 100 ~1000 사이를 권장
JPA에서 DTO 직접 조회
public List<OrderQueryDto> findOrderQueryDtos() {
List<OrderQueryDto> result = findOrders();
result.forEach(o -> {
List<OrderItemQueryDto> orderItems = findOrderItems(o.getOrderId());
o.setOrderItems(orderItems);
});
return result;
}
private List<OrderItemQueryDto> findOrderItems(Long orderId) {
return em. createQuery(
"select new ~~~" +
" from OrderItem oi" +
" join oi.item i" +
" where oi.order.id = :orderId",OrderItemQueryDto.class)
.setParameter("orderId", orderId)
.getResultList();
);
}
private List<OrderQueryDto> findOrders() {
em.createQuery(
"select new me.jiho.repository.OrderQyeryDTO(id, date, ,,,,)"
" from Order o" +
" join fetch o.member m" +
" join fetch o.delivery d", OrderQueryDTO.class)
}
JPA에서 DTO 직접 조회 N+1 최적화
public List<OrderQueryDto> findOrderQueryDtos() {
List<OrderQueryDto> result = findOrders();
List<Long> orderIds = result.stream().map(o -> o.getOrderID()).collector(toList);
List<OrderItemQueryDto> orderItems = findOrderItems(orderIds);
Map<Long ,List<OrderItemQueryDto>> orderItemMap = orderItems.stream().collector(.groupingBy(OrderItemQueryDto:getOrderId));
result.forEach(o -> o.setOrderItems(orderItemMap.get(o.getOrderId)));
return result;
}
private List<OrderItemQueryDto> findOrderItems(Long orderId) {
return em. createQuery(
"select new ~~~" +
" from OrderItem oi" +
" join oi.item i" +
" where oi.order.id in :orderIds",OrderItemQueryDto.class)
.setParameter("orderIds", orderIds)
.getResultList();
);
}
private List<OrderQueryDto> findOrders() {
em.createQuery(
"select new me.jiho.repository.OrderQyeryDTO(id, date, ,,,,)"
" from Order o" +
" join fetch o.member m" +
" join fetch o.delivery d", OrderQueryDTO.class)
}
in을이용해 한 id리스트를 한꺼번에 조회