api controller
@GetMapping("/api/v3/simple-orders")
public List<SimpleOrderDto> orderV3(){
List<Order> orders = orderRepository.findAllWithMemberDeliver();
List<SimpleOrderDto> result = orders.stream().map(SimpleOrderDto::new).collect(toList());
return result;
}
@Data
static class SimpleOrderDto{
private Long orderId;
private String name;
private LocalDateTime orderDate;
private OrderStatus orderStatus;
private Address address;
public SimpleOrderDto(Order order) {
orderId = order.getId();
name = order.getMember().getName(); //LAZY 초기화
orderDate = order.getOrderDate();
orderStatus = order.getStatus();
address = order.getDelivery().getAddress(); //LAZY 초기화
}
}
repository
public List<Order> findAllWithMemberDeliver(){
return em.createQuery(
"select o from Order o" +
" join fetch o.member m" +
" join fetch o.delivery d", Order.class)
.getResultList();
//이렇게 하면 order의 MEMBER, DELIVERY가 LAZY로 페치전략으로 되어있어도 한번에 실제 값으로 객체가 조회된다.
}
엔티티를 페치조인하여 쿼리 1번으로 조회하게 되고 이미 이렇게 select로 모두 조회하므로 지연로딩으로 인한 추가적인 쿼리문이 발생하지 않게 된다.
사진에서 보다시피 모든 필드를 조회해오기 때문에 아주 약간의 성능문제가 발생할 수 있다는 점이다.
리포지토리의 역할은 엔티티를 조회해오고, 그 엔티티를 조회하기 위해 테이블의 그래프 탐색이라는 것을 수행한다. 그렇기 때문에 리포지토리 내 메서드의 리턴값의 제네릭이 엔티티 Order인 것은 당연히 이렇게 작성해야 한다. 만약 DTO를 반환하도록 메서드를 작성했다면, 그건 API 스펙에 맞게만 작성한 것이므로 API 스펙이 바뀌면 리포지토리를 계속 수정해야한다.. 그렇기에 강사는 엔티티 제네릭을 리턴하는 것을 권장한다.
api controller
@GetMapping("/api/v4/simple-orders")
public List<OrderSimpleQueryDto> orderV4(){
return orderSimpleQueryRepository.findOrderDtos(); //리포지토리는 엔티티를 조회하는데 써야한다. 그래프 탐색 용도여야 하지만, 이 api는
//그런 용도에 알맞지 않다.
}
DTO
@Data
public class OrderSimpleQueryDto {
private Long orderId;
private String name;
private LocalDateTime orderDate;
private OrderStatus orderStatus;
private Address address;
public OrderSimpleQueryDto(Long orderId, String name, LocalDateTime orderDate, OrderStatus status, Address address) {
orderId = orderId;
name = name; //LAZY 초기화
orderDate = orderDate;
orderStatus = status;
address = address; //LAZY 초기화
}
}
query 전용 리포지토리 생성
@Repository
@RequiredArgsConstructor
public class OrderSimpleQueryRepository {
private final EntityManager em;
public List<OrderSimpleQueryDto> findOrderDtos(){
return em.createQuery(
"select new jpabook.jpashop.repository.order.simplequery.OrderSimpleQueryDto(o.id, m.name, o.orderDate, o.status, d.address) from Order o"+
" join o.member m"+
" join o.delivery d", OrderSimpleQueryDto.class
).getResultList();
}
}
➡️엔티티(fetch join)를 dto로 변환하거나 dto(inner join)로 바로 조회하는 두 방법은 각각 장단점이 있다.
상황에 따라 더 나은 방법을 선택해서 사용하면 된다. 다만, 엔티티로 조회하여 DTO로 변환하는 건 더 재사용성이 높고 개발도 단순해진다.