스프링데이터 + JPA/API 개발

14. 컬렉션 Dto로 직접 조회

sdafdq 2023. 11. 16. 08:13

화면에 fit한 데이터들 찾는 용도의 Repository는 따로 패키지및 클래스를 만든다.

 

 

@GetMapping("/api/v4/orders")
public List<OrderQueryDto> ordersV4(){
    return orderQueryRepository.findOrderQueryDtos();
}

 

public List<OrderQueryDto> findOrderQueryDtos() {
    List<OrderQueryDto> result = findOrders();
    result.forEach((o)->{
        List<OrderItemQueryDto> orderItems = findOrderItems(o.getOrderId());
        o.setOrderItems(orderItems);
    });

    return result;
}

여러 메소드로 나눠서 조회했다. ToOne?과 ToMany는 따로 조회했다. 

 

이유는 new 오퍼레이션은 컬렉션을 바로 넣을 수 없다.

new 오퍼레이션은 sql처럼 쓰는건데, 그래서 데이터를 플랫하게 넣을 수 밖에 없다. 근데 컬렉션은 일대다, 데이터가 뻥튀기가 되니까 안된다.

그리고 지금 new 오퍼레이션 쓰면서 패키지명도 다 넣어줘야 하는데, QueryDSL 쓰면 클래스타입을 바로 적을 수 있어서, 패키지명을 안 넣어줘도 된다.

 

private List<OrderQueryDto> findOrders() {
    return em.createQuery(
                    "select new  jpabook.jpashop.repository.order.query.OrderQueryDto(o.id, m.name, o.orderDate, o.status, d.address)" +
                            " from Order o" +
                            " join o.member m" +
                            " join o.delivery d", OrderQueryDto.class)
            .getResultList();
}

이건데, 먼저 컬렉션을 빼고 조회해왔다.

 

@Data
public class OrderQueryDto {
    private Long orderId;
    private String name;
    private LocalDateTime orderDate;
    private OrderStatus orderStatus;
    private Address address;
    private List<OrderItemQueryDto> orderItems;

    public OrderQueryDto(Long orderId, String name, LocalDateTime orderDate, OrderStatus orderStatus, Address address) {
        this.orderId = orderId;
        this.name = name;
        this.orderDate = orderDate;
        this.orderStatus = orderStatus;
        this.address = address;
        this.orderItems = orderItems;
    }
}

 

OrderItems는 따로 넣어준다. 

private List<OrderItemQueryDto> findOrderItems(Long orderId) {
    return em.createQuery(
            "select new jpabook.jpashop.repository.order.query.OrderItemQueryDto(i.name, oi.orderPrice, oi.count)" +
                    " from OrderItem oi" +
                    " join oi.item i" +
                    " where oi.order.id = :orderId", OrderItemQueryDto.class)
            .setParameter("orderId", orderId)
            .getResultList();
}

외래키가 OrderItem 엔티티의

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "order_id")
@JsonIgnore
private Order order;

저 @JoinColumn에 있기 때문에 굳이 Order까지 들어가서 참조하지 않는다고 했다.

@Data
public class OrderItemQueryDto {
    private String itemName;
    private int orderPrice;
    private int count;

    public OrderItemQueryDto(String itemName, int orderPrice, int count) {
        this.itemName = itemName;
        this.orderPrice = orderPrice;
        this.count = count;
    }
}

 

여튼 이렇게 넣어주는데,

 

컬렉션 빼고는 필요한 것만 딱 한번에 쿼리로 나가서 다 가져오는데,

    select
        o1_0.order_id,
        m1_0.name,
        o1_0.order_date,
        o1_0.status,
        d1_0.city,
        d1_0.street,
        d1_0.zipcode 
    from
        orders o1_0 
    join
        member m1_0 
            on m1_0.member_id=o1_0.member_id 
    join
        delivery d1_0 
            on d1_0.delivery_id=o1_0.delivery_id
2023-11-16T07:57:57.848+09:00  INFO 16308 --- [nio-8080-exec-3] p6spy                                    : #1700089077848 | took 0ms | statement | connection 8| url jdbc:h2:tcp://localhost/./jpashop
select o1_0.order_id,m1_0.name,o1_0.order_date,o1_0.status,d1_0.city,d1_0.street,d1_0.zipcode from orders o1_0 join member m1_0 on m1_0.member_id=o1_0.member_id join delivery d1_0 on d1_0.delivery_id=o1_0.delivery_id
select o1_0.order_id,m1_0.name,o1_0.order_date,o1_0.status,d1_0.city,d1_0.street,d1_0.zipcode from orders o1_0 join member m1_0 on m1_0.member_id=o1_0.member_id join delivery d1_0 on d1_0.delivery_id=o1_0.delivery_id;
2023-11-16T07:57:57.849+09:00 DEBUG 16308 --- [nio-8080-exec-3] org.hibernate.SQL                        : 
    select
        o1_0.order_id,
        i1_0.name,
        o1_0.order_price,
        o1_0.count 
    from
        order_item o1_0 
    join
        item i1_0 
            on i1_0.item_id=o1_0.item_id 
    where
        o1_0.order_id=?
2023-11-16T07:57:57.849+09:00 TRACE 16308 --- [nio-8080-exec-3] org.hibernate.orm.jdbc.bind              : binding parameter [1] as [BIGINT] - [1]
2023-11-16T07:57:57.850+09:00  INFO 16308 --- [nio-8080-exec-3] p6spy                                    : #1700089077850 | took 0ms | statement | connection 8| url jdbc:h2:tcp://localhost/./jpashop
select o1_0.order_id,i1_0.name,o1_0.order_price,o1_0.count from order_item o1_0 join item i1_0 on i1_0.item_id=o1_0.item_id where o1_0.order_id=?
select o1_0.order_id,i1_0.name,o1_0.order_price,o1_0.count from order_item o1_0 join item i1_0 on i1_0.item_id=o1_0.item_id where o1_0.order_id=1;
2023-11-16T07:57:57.851+09:00 DEBUG 16308 --- [nio-8080-exec-3] org.hibernate.SQL                        : 
    select
        o1_0.order_id,
        i1_0.name,
        o1_0.order_price,
        o1_0.count 
    from
        order_item o1_0 
    join
        item i1_0 
            on i1_0.item_id=o1_0.item_id 
    where
        o1_0.order_id=?

 

컬렉션이 문제다.

저렇게 따로 가져오다 보니 LAZY로딩으로 건드릴 때마다 쿼리가 날아간다..

default로 batch를 설정해 놓긴 했는데, 엔티티를 조회해오는게 아니라서 안먹히나 보다..

 

 

ToOne관계들은 조인해도 row수가 뻥튀기 되지 않으므로 그냥 함께 조회하고,

ToMany인 컬렉션은 row 수가 뻥튀기 되므로 별도로 넣어준다.