orders를 가져오는 것 까지는 똑같고 orderItems를 가져올 때 in 을 이용해서 한번에 가져오는 것이다.

그런 다음 메모리(애플리케이션)에서 값을 매칭 해주는 식이다.

public List<OrderQueryDTO> findAllByDTO_optimization() {
		List<OrderQueryDTO> orders = findOrders();
	Map<Long, List<OrderItemQueryDTO>> orderItemMap = findOrderItemMap(toOrderIds(orders));
	orders.forEach(o -> o.setOrderItems(orderItemMap.get(o.getOrderId())));

	return orders;
}

private List<Long> toOrderIds(List<OrderQueryDTO> orders) {
	return orders.stream()
				.map(OrderQueryDTO::getOrderId)
				.collect(Collectors.toList());
}

private Map<Long, List<OrderItemQueryDTO>> findOrderItemMap(List<Long> orderIds) {
	List<OrderItemQueryDTO> orderItems = em.createQuery("select new jpabook.jpashop.repository.order.query.OrderItemQueryDTO(oi.order.id, oi.item.name, oi.orderPrice, oi.count) from OrderItem oi" +
				" join oi.item i" +
				" where oi.order.id in :orderIds", OrderItemQueryDTO.class)
			.setParameter("orderIds", orderIds)
			.getResultList();

	return orderItems.stream()
			.collect(Collectors.groupingBy(OrderItemQueryDTO::getOrderId));
}

총 2번의 쿼리가 날아간다.

ToOne은 최대한 한번에 가져온다.

정리

많은 코드를 직접 이렇게 파싱 하기가 마냥 쉽지 않다는 생각이 든다.