ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [๊ฐ•์˜] ์‹ค์ „! ์Šคํ”„๋ง ๋ถ€ํŠธ์™€ JPA ํ™œ์šฉ2 - API ๊ฐœ๋ฐœ๊ณผ ์„ฑ๋Šฅ ์ตœ์ ํ™” 2
    SPRING/INFLEARN 2022. 8. 2. 03:38

     

    https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81%EB%B6%80%ED%8A%B8-JPA-API%EA%B0%9C%EB%B0%9C-%EC%84%B1%EB%8A%A5%EC%B5%9C%EC%A0%81%ED%99%94/dashboard

     

    ์‹ค์ „! ์Šคํ”„๋ง ๋ถ€ํŠธ์™€ JPA ํ™œ์šฉ2 - API ๊ฐœ๋ฐœ๊ณผ ์„ฑ๋Šฅ ์ตœ์ ํ™” - ์ธํ”„๋Ÿฐ | ๊ฐ•์˜

    ์Šคํ”„๋ง ๋ถ€ํŠธ์™€ JPA๋ฅผ ํ™œ์šฉํ•ด์„œ API๋ฅผ ๊ฐœ๋ฐœํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  JPA ๊ทนํ•œ์˜ ์„ฑ๋Šฅ ์ตœ์ ํ™” ๋ฐฉ๋ฒ•์„ ํ•™์Šตํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค., - ๊ฐ•์˜ ์†Œ๊ฐœ | ์ธํ”„๋Ÿฐ...

    www.inflearn.com

    4. API ๊ฐœ๋ฐœ ๊ณ ๊ธ‰ - ์ปฌ๋ ‰์…˜ ์กฐํšŒ ์ตœ์ ํ™”

    - ์ฃผ๋ฌธ ์กฐํšŒ V1: ์—”ํ‹ฐํ‹ฐ ์ง์ ‘ ๋…ธ์ถœ

    @RestController
    @RequiredArgsConstructor
    public class OrderApiController {
    
        private final OrderRepository orderRepository;
    
        @GetMapping("/api/v1/orders")
        public List<Order> ordersV1(){
            List<Order> all = orderRepository.findAllByString(new OrderSearch());
            for (Order order : all) {
                order.getMember().getName();
                order.getDelivery().getAddress();
                List<OrderItem> orderItems = order.getOrderItems();
                orderItems.forEach(o -> o.getItem().getName());
            }
            return all;
        }
    }

    ์ „๋ถ€ lazy๊ธฐ ๋•Œ๋ฌธ์— ํ”„๋ก์‹œ๋ฅผ ๊ฐ•์ œ ์ดˆ๊ธฐํ™” ํ•œ ํ›„ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฟŒ๋ฆฌ๊ฒŒ ํ•จ

    ๊ฒฐ๊ตญ ์—”ํ‹ฐํ‹ฐ ์ง์ ‘ ๋…ธ์ถœ์ด๋ผ ๋ณ„๋กœ

     

    - ์ฃผ๋ฌธ ์กฐํšŒ V2: ์—”ํ‹ฐํ‹ฐ๋ฅผ DTO๋กœ ๋ณ€ํ™˜

    @RestController
    @RequiredArgsConstructor
    public class OrderApiController {
    
        /*์ƒ๋žต*/
    
        @GetMapping("/api/v2/orders")
        public List<OrderDto> orderV2(){
            List<Order> orders = orderRepository.findAllByString(new OrderSearch());
            List<OrderDto> collect = orders.stream()
                    .map(o-> new OrderDto(o))
                    .collect(Collectors.toList());
    
            return collect;
        }
    
        //@Data ๊ฐ€ ๋„ˆ๋ฌด ๋งŽ์œผ๋ฉด ์• ๋งคํ•ด์„œ ์•ˆ์“ฐ๋Š” ๊ฒŒ ๋‚˜์„ ์ˆ˜๋„ ์žˆ์Œ
        @Getter
        static class OrderDto {
            private Long orderId;
            private String name;
            private LocalDateTime orderDate;
            private OrderStatus orderStatus;
            private Address address;
            private List<OrderItemDto> orderItems;
            //๋†“์น˜๊ธฐ ์œ„ํ•œ ์‹ค์ˆ˜ : OrderItem๋„ DTO๋กœ ๋ฐ”๊ฟ”์„œ ๋ณด๋‚ด์•ผํ•จ
            //private List<OrderItem> orderItems;
    
            public OrderDto(Order order){
                orderId = order.getId();
                name = order.getMember().getName();
                orderDate = order.getOrderDate();
                orderDate = order.getOrderDate();
                orderStatus = order.getStatus();
                address = order.getMember().getAddress();
                /*//์ด๊ฑฐ ์•ˆ์“ฐ๋ฉด lazy๋ผ ์•„๋ฌด๊ฒƒ๋„ ๋‚˜์˜ค์ง€ ์•Š์Œ
                order.getOrderItems().stream().forEach(o -> o.getItem().getName());
                orderItems = order.getOrderItems();*/
                //-> OrderItem๋„ Dto๋กœ ๋ณ€๊ฒฝ
                orderItems = order.getOrderItems().stream()
                        .map(orderItem -> new OrderItemDto(orderItem))
                        .collect(Collectors.toList());
    
            }
    
        }
    
        @Getter
        static class OrderItemDto{
            private String itemName;
            private int orderPrice;
            private int count;
    
            public OrderItemDto(OrderItem orderItem){
                itemName = orderItem.getItem().getName();
                orderPrice = orderItem.getOrderPrice();
                count = orderItem.getCount();
            }
        }
    }

    ์›ํ•˜๋Š” ์ •๋ณด๋งŒ ๋ณด์ด๋Š” ๊ฒƒ์„ ์•Œ ์ˆ˜ ์žˆ๋‹ค.

    ์ง€์—ฐ ๋กœ๋”ฉ์ด ๋งŽ์•„์„œ

    Order ์กฐํšŒ์— ๋‘๊ฐœ๋‚˜์˜ด

    ๊ฐ Order๋งˆ๋‹ค

    ๋ฉค๋ฒ„ ํ•œ ๋ช… ์กฐํšŒ

    delivery ์ •๋ณด

    Items ๋‘๊ฐœ ์กฐํšŒ

    Items๋งˆ๋‹ค Item ์กฐํšŒ์— 2๋ฒˆ์”ฉ

    ...

    SQL ์ฟผ๋ฆฌ๊ฐ€ ์กฐ์ง€๊ฒŒ ๋งŽ์ด ๋‚˜๊ฐ

    ๋”ฑ ๋ด๋„ ์„ฑ๋Šฅ ๋š ๋–จ์–ด์ ธ ๋ณด์ž„

     

    - ์ฃผ๋ฌธ ์กฐํšŒ V3: ์—”ํ‹ฐํ‹ฐ๋ฅผ DTO๋กœ ๋ณ€ํ™˜ - ํŒจ์น˜ ์กฐ์ธ ์ตœ์ ํ™”

    @Repository
    @RequiredArgsConstructor
    public class OrderRepository {
    
        /*์ƒ๋žต*/
    
        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 oi" +
                            " join fetch oi.item i", Order.class)
                    .getResultList();
        }
    }


    DB์—์„œ ์ผ๋Œ€๋‹ค์ผ๋•Œ ๋‹ค์— ๋งž์ถฐ์„œ ๋ฆฌํ„ดํ•˜๊ธฐ ๋•Œ๋ฌธ์— order 4๊ฐœ๊ฐ€ ๋ฆฌํ„ด๋œ๋‹ค -> distinct๋ฅผ ์ถ”๊ฐ€ํ•˜๋ฉด 2๊ฐœ ๋ฆฌํ„ด
    ์‚ฌ์‹ค ์ฟผ๋ฆฌ์—์„œ๋Š” ์™„์ „ํžˆ ๋˜‘๊ฐ™์•„์•ผ ์ค‘๋ณต์ œ๊ฑฐ์ด๋ฏ€๋กœ, ์—ฌ์ „ํžˆ 4๊ฐœ๊ฐ€ ๋‚˜์˜ค์ง€๋งŒ,
    JPA์—์„œ ์ž์ฒด์ ์œผ๋กœ distinct๋ฅผ ๋ณด๋ฉด ์ค‘๋ณต์„ ๊ฑธ๋Ÿฌ์„œ ์ปฌ๋ ‰์…˜์— ๋‹ด์•„์ค€๋‹ค.

    distinct๊ฐ€ ์ค‘๋ณต ์กฐํšŒ๋ฅผ ๋ง‰์•„์ค€๋‹ค

    @RestController
    @RequiredArgsConstructor
    public class OrderApiController {
    	/*์ƒ๋žต*/
        @GetMapping("/api/v3/orders")
        public List<OrderDto> orderV3(){
            List<Order> orders = orderRepository.findAllWithItem();
    
            List<OrderDto> collect = orders.stream()
                    .map(o-> new OrderDto(o))
                    .collect(Collectors.toList());
    
            return collect;
    	/*์ƒ๋žต*/
        }
    }

    ํŒจ์น˜ ์กฐ์ธ์œผ๋กœ SQL์ด ํ•œ ๋ฒˆ๋ฐ˜ ์‹คํ–‰๋จ

     

    ํ•˜์ง€๋งŒ ๋‹จ์ , ์ผ๋Œ€๋‹ค๋ฅผ ํ•˜๋Š” ์ˆœ๊ฐ„ ํŽ˜์ด์ง•์ด ๋ถˆ๊ฐ€๋Šฅํ•˜๋‹ค

    .setFirstResult(1), .setMaxResults(100) : 1๋ถ€ํ„ฐ 100๊ฐœ ๊ฐ€์ ธ์˜ด

    WARN : firstResult/maxResults sepcified with collection fetch; applying in memory

    Distinct๋œ ๊ฒฐ๊ณผ ๊ธฐ์ค€์ด ์•„๋‹Œ, ๋ปฅํŠ€๊ธฐ ๋œ ์ƒํƒœ, order item์„ ๊ธฐ์ค€์œผ๋กœ ์ ์šฉ์ด ๋จ

    ์ ์ ˆํ•œ ๊ฐœ์ˆ˜๋ฅผ ๋ถˆ๋Ÿฌ์˜ฌ ์ˆ˜ ์—†์–ด ๋งค์šฐ ์œ„ํ—˜ํ•จ(ex) OutOfMemory), ํŽ˜์ด์ง•์ด ๋ถˆ๊ฐ€๋Šฅํ•จ

     

    ๋˜ํ•œ ์ปฌ๋ ‰์…˜ ํŒจ์น˜ ์กฐ์ธ์€ ํ•œ ๋ฒˆ๋งŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

    ๋‘๊ฐœ ์ด์ƒ ์‚ฌ์šฉํ•˜๊ฒŒ ๋˜๋ฉด ๋ปฅํŠ€๊ธฐ์— ๋ปฅํŠ€๊ธฐ ํ•ด์„œ ํ˜ผ๋ž€์„ ์ผ์œผํ‚ฌ ์ˆ˜ ์žˆ์Œ

     

    - ์ฃผ๋ฌธ ์กฐํšŒ V3.1: ์—”ํ‹ฐํ‹ฐ๋ฅผ DTO๋กœ ๋ณ€ํ™˜ - ํŽ˜์ด์ง•๊ณผ ํ•œ๊ณ„ ๋ŒํŒŒ

    @Repository
    @RequiredArgsConstructor
    public class OrderRepository {
    	public List<Order> findAllWithMemberDelivery(int offset, int limit) {
            return em.createQuery(
                    "select o from Order o" +
                            " join fetch o.member m" +
                            " join fetch o.delivery d", Order.class)
                    .setFirstResult(offset)
                    .setMaxResults(limit)
                    .getResultList();
        }
    @RestController
    @RequiredArgsConstructor
    public class OrderApiController {
    
        /*์ƒ๋žต*/
    
        @GetMapping("/api/v3/orders")
        public List<OrderDto> orderV3(){
            List<Order> orders = orderRepository.findAllWithItem();
    
            List<OrderDto> collect = orders.stream()
                    .map(o-> new OrderDto(o))
                    .collect(Collectors.toList());
    
            return collect;
        }
    
        @GetMapping("/api/v3.1/orders")
        public List<OrderDto> orderV3_page(
                @RequestParam(value = "offset" , defaultValue = 0) int offset,
                @RequestParam(value = "limit" , defaultValue = 100) int limit){
            //ToOne ๊ด€๊ณ„๊นŒ์ง€๋งŒ ๋ญ‰ํƒฑ์ด๋กœ ํ•œ๋ฒˆ์— ๊ฐ€์ ธ์˜จ๋‹ค
            List<Order> orders = orderRepository.findAllWithMemberDelivery(offset, limit);
            // ์—ฌ๊ธฐ๊นŒ์ง€๋งŒ ํ•˜๋ฉด 1 + orders ๋ฃจํ”„ (2๋ฒˆ + 1) * (orderItems 1 + orderitem 2  * item 2)... ๊ณ„์‚ฐ ๋‹ค์Œ์— ๋งˆ์ € ์ผ๋‹จ ์—ด๋ผ ๋งŽ์Œ
    
            List<OrderDto> collect = orders.stream()
                    .map(o-> new OrderDto(o))
                    .collect(Collectors.toList());
    
            return collect;
        }
        /*์ƒ๋žต*/
    }

    jpa.hibernate.properties.default_batch_fetch_size: 100  ์ถ”๊ฐ€

     

    100์˜ ์˜๋ฏธ๋Š” inquery์˜ ๊ฐœ์ˆ˜๋ฅผ ๋ช‡๊ฐœ๋กœ ํ•  ๊ฒƒ์ธ๊ฐ€

    ํ•œ ๋ฐฉ์— 100๊ฐœ๋ฅผ ๋‹ค ๋•ก๊ฒจ์˜จ๋‹ค

    ์‚ฌ์ด์ฆˆ : ๊ณ ๊ณ ์ต์„  ํ•˜์ง€๋งŒ ์ˆœ๊ฐ„ ๋ถ€ํ•˜๋ฅผ ๊ฒฌ๋”œ ์ˆ˜ ์žˆ์–ด์•ผํ•จ ์ฃผ๋กœ 100~1000 ์‚ฌ์ด

    1 + N + N๋ฌธ์ œ ํ•ด๊ฒฐ

     

    ๋ฐ์ดํ„ฐ ์–‘์ด ์—„์ฒญ ๋งŽ์•„์ง€๋ฉด ์˜คํžˆ๋ ค ํ•œ๋ฒˆ์— ์ „๋ถ€ joinํ•ด ๊ฐ€์ ธ์˜ค๋Š” ์œ— ๋ฐฉ๋ฒ•๋ณด๋‹ค ์ด ๋ฐฉ๋ฒ•์ด ๋” ์ข‹์„ ์ˆ˜ ๋„ ์žˆ๋‹ค

    ๊ฒฐ๋ก  ์žฅ์  : 

    ์ฟผ๋ฆฌ ํ˜ธ์ถœ ์ˆ˜๊ฐ€ 1 + N -> 1 + 1 ๋กœ ์ตœ์ ํ™”
    ์กฐ์ธ๋ณด๋‹ค DB ๋ฐ์ดํ„ฐ ์ „์†ก๋Ÿ‰์ด ์ตœ์ ํ™” , ์ค‘๋ณต๋œ ๋ฐ์ดํ„ฐ ์ „์†ก ์•ˆํ•ด๋„ ๋จ

    ํŒจ์น˜ ์กฐ์ธ ๋ฐฉ์‹๊ณผ ๋น„๊ตํ•ด์„œ ์ฟผ๋ฆฌ ํ˜ธ์ถœ ์ˆ˜๊ฐ€ ์•ฝ๊ฐ„ ์ฆ๊ฐ€ํ•˜์ง€๋งŒ, DB ๋ฐ์ดํ„ฐ ์ „์†ก๋Ÿ‰์€ ๊ฐ์†Œ
    ์ปฌ๋ ‰์…˜ ํŽ˜์น˜ ์กฐ์ธ์€ ํŽ˜์ด์ง• ๋ถˆ๊ฐ€๋Šฅ ํ•˜์ง€๋งŒ ์ด ๋ฐฉ๋ฒ•์€ ํŽ˜์ด์ง• ๊ฐ€๋Šฅ

     

    - ์ฃผ๋ฌธ ์กฐํšŒ V4: JPA์—์„œ DTO ์ง์ ‘ ์กฐํšŒ

    repository ์ƒˆ๋กœ ์ƒ์„ฑ

    @Repository
    @RequiredArgsConstructor
    public class OrderQueryRepository {
        private final EntityManager em;
    
        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 jpabook.jpashop.repository.order.query.OrderItemQueryDto(oi.order.id, i.name, oi.orderPrice,oi.count)" +
                        " from OrderItem oi" +
                        " join oi.item i" +
                        " where oi.order.id = :orderId", OrderItemQueryDto.class)
                    .setParameter("orderId", orderId)
                    .getResultList();
        }
    
        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();
        }
    }

     

    @RestController
    @RequiredArgsConstructor
    public class OrderApiController {
        /*์ƒ๋žต*/
    	@GetMapping("/api/v4/orders")
    	public List<OrderQueryDto> orderV4(){
        	return orderQueryRepository.findOrderQueryDtos();
    	}
        /*์ƒ๋žต*/
    }

    DTO ์ƒ์„ฑ

    @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;
        }
    }
    @Data
    public class OrderItemQueryDto {
    
        @JsonIgnore
        private Long orderId;
        private String itemName;
    
        private int orderPrice;
        private int count;
    
        public OrderItemQueryDto(Long orderId, String itemName, int orderPrice, int count) {
            this.orderId = orderId;
            this.itemName = itemName;
            this.orderPrice = orderPrice;
            this.count = count;
        }
    }

    ๊ฒฐ๊ณผ๋Š” ๋ญ... ๋น„์Šท

     

    row ์ˆ˜๊ฐ€ ์ฆ๊ฐ€ํ•˜์ง€ ์•Š๋Š” ToOne ๊ด€๊ณ„๋Š” ์กฐ์ธ์œผ๋กœ ์ตœ์ ํ™” ํ•˜๊ธฐ ์‰ฌ์šฐ๋ฏ€๋กœ ํ•œ๋ฒˆ์— ์กฐํšŒํ•˜๊ณ , ToMany
    ๊ด€๊ณ„๋Š” ์ตœ์ ํ™” ํ•˜๊ธฐ ์–ด๋ ค์šฐ๋ฏ€๋กœ findOrderItems() ๊ฐ™์€ ๋ณ„๋„์˜ ๋ฉ”์„œ๋“œ๋กœ ์กฐํšŒํ•œ๋‹ค.

    ๋ณ„๋„์˜ ๋ฉ”์„œ๋“œ๋ฅผ ์“ฐ๋Š” ๊ณผ์ •์—์„œ N+1๋ฒˆ ์ฟผ๋ฆฌ๊ฐ€ ๋‚˜๊ฐ€๋Š” ๋‹จ์ ์ด ์žˆ์Œ

     

    - ์ฃผ๋ฌธ ์กฐํšŒ V5: JPA์—์„œ DTO ์ง์ ‘ ์กฐํšŒ - ์ปฌ๋ ‰์…˜ ์กฐํšŒ ์ตœ์ ํ™”

    @RestController
    @RequiredArgsConstructor
    public class OrderApiController {
        /*์ƒ๋žต*/
    	@GetMapping("/api/v5/orders")
        public List<OrderQueryDto> orderV5(){
            return orderQueryRepository.findAllByDto_optimization();
        }
        /*์ƒ๋žต*/
    }
    @Repository
    @RequiredArgsConstructor
    public class OrderQueryRepository {
        public List<OrderQueryDto> findAllByDto_optimization(){
            List<OrderQueryDto> result = findOrders();
    
            List<Long> orderIds = result.stream().map(o -> o.getOrderId())
                    .collect(Collectors.toList());
    
            List<OrderItemQueryDto> orderItems = em.createQuery("select new jpabook.jpashop.repository.order.query.OrderItemQueryDto(oi.order.id, i.name, oi.orderPrice,oi.count)" +
                            " from OrderItem oi" +
                            " join oi.item i" +
                            " where oi.order.id in :orderIds", OrderItemQueryDto.class)
                    .setParameter("orderIds", orderIds)
                    .getResultList();
    
            //์‚ฌ์šฉํ•˜๊ธฐ ํŽธํ•˜๊ฒŒ ๋งต ํ˜•์‹์œผ๋กœ ๋ณ€๊ฒฝ
            Map<Long, List<OrderItemQueryDto>> orderItemMap = orderItems.stream()
                    .collect(Collectors.groupingBy(orderItemQueryDto -> orderItemQueryDto.getOrderId()));
    
            result.forEach(o -> o.setOrderItems(orderItemMap.get(o.getOrderId())));
    
            return result;
    
        }
    }

    ์ฟผ๋ฆฌ ๋‘ ๋ฒˆ์œผ๋กœ ์ตœ์ ํ™” ๊ฐ€๋Šฅ

    ๋ฃจํŠธ 1๋ฒˆ, ์ปฌ๋ ‰์…˜ 1๋ฒˆ

    Map์„ ์‚ฌ์šฉํ•ด์„œ ๋งค์นญ ์„ฑ๋Šฅ ํ–ฅ์ƒ

     

    - ์ฃผ๋ฌธ ์กฐํšŒ V6: JPA์—์„œ DTO ์ง์ ‘ ์กฐํšŒ, ํ”Œ๋žซ ๋ฐ์ดํ„ฐ ์ตœ์ ํ™”

    ์ฟผ๋ฆฌ ํ•œ๋ฒˆ์œผ๋กœ ํ•ด๊ฒฐ ๊ฐ€๋Šฅ

    @RestController
    @RequiredArgsConstructor
    public class OrderApiController {
        /*์ƒ๋žต*/
    	//์ฟผ๋ฆฌ๊ฐ€ ํ•œ ๋ฒˆ๋งŒ ๋‚˜๊ฐ
        //ํ•˜์ง€๋งŒ join์„ ์ด์šฉํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋‹ค์— ๋งž์ถฐ์„œ ๋ฟ”๋ ค์ ธ ๋‚˜์˜ด
        //๋”ฐ๋ผ์„œ ํŽ˜์ด์ง•์„ ๋ชปํ•จ
        //API ์ŠคํŽ™์ด ์•ˆ๋งž๋Š” ๋ฌธ์ œ๋Š” ๊ทธ๋ƒฅ ๋…ธ๊ฐ€๋‹ค๋กœ ๋ฐœ๋ผ๋‚ด๋Š” ์ฝ”๋“œ๋ฅผ ์งœ๋ฉด ๋จ
        @GetMapping("/api/v6/orders")
        public List<OrderFlatDto> orderV6(){
            return orderQueryRepository.findAllByDto_flat();
        }
        /*์ƒ๋žต*/
    }
    package jpabook.jpashop.repository.order.query;
    
    import lombok.RequiredArgsConstructor;
    import org.springframework.stereotype.Repository;
    
    import javax.persistence.EntityManager;
    import java.util.List;
    import java.util.Map;
    import java.util.stream.Collectors;
    
    @Repository
    @RequiredArgsConstructor
    public class OrderQueryRepository {
        /*์ƒ๋žต*/
    
        public List<OrderFlatDto> findAllByDto_flat() {
            return em.createQuery(
                    "select new jpabook.jpashop.repository.order.query.OrderFlatDto(o.id, m.name, o.orderDate, o.status, d.address, i.name, oi.orderPrice, oi.count)" +
                            " from Order o" +
                            " join o.member m" +
                            " join o.delivery d" +
                            " join o.orderItems oi" +
                            " join oi.item i", OrderFlatDto.class)
                    .getResultList();
        }
    }

    ์ง์ ‘ ์ •๋ฆฌํ•˜๋Š” ์ฝ”๋“œ(์ •๋ฆฌ ์•ˆ๋˜์–ด์žˆ์Œ, ๊ทธ๋ƒฅ ๋ณต๋ถ™)

    ๋”๋ณด๊ธฐ

    return flats.stream()
    .collect(groupingBy(o -> new OrderQueryDto(o.getOrderId(),
    o.getName(), o.getOrderDate(), o.getOrderStatus(), o.getAddress()),
    mapping(o -> new OrderItemQueryDto(o.getOrderId(),
    o.getItemName(), o.getOrderPrice(), o.getCount()), toList())
    )).entrySet().stream()
    .map(e -> new OrderQueryDto(e.getKey().getOrderId(),
    e.getKey().getName(), e.getKey().getOrderDate(), e.getKey().getOrderStatus(),
    e.getKey().getAddress(), e.getValue()))
    .collect(toList());

     

    ๊ทธ๋ฆฌ๊ณ  OrderQueryDto์— @EqualsAndHashCode(of = orderId) ์ถ”๊ฐ€

    orderId ๊ธฐ์ค€์œผ๋กœ ๋ฌถ์–ด์คŒ

    ๋‹จ :

    ์ฟผ๋ฆฌ๋Š” ํ•œ๋ฒˆ์ด์ง€๋งŒ ์กฐ์ธ์œผ๋กœ ์ธํ•ด DB์—์„œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์— ์ „๋‹ฌํ•˜๋Š” ๋ฐ์ดํ„ฐ์— ์ค‘๋ณต ๋ฐ์ดํ„ฐ๊ฐ€ ์ถ”๊ฐ€

    -> V5 ๋ณด๋‹ค ๋” ๋А๋ฆด ์ˆ˜ ๋„
    ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ์ถ”๊ฐ€ ์ž‘์—…์ด ํผ
    ํŽ˜์ด์ง• ๋ถˆ๊ฐ€๋Šฅ

     

    - API ๊ฐœ๋ฐœ ๊ณ ๊ธ‰ ์ •๋ฆฌ

     

    ์—”ํ‹ฐํ‹ฐ ์กฐํšŒ

    • ์—”ํ‹ฐํ‹ฐ๋ฅผ ์กฐํšŒํ•ด์„œ ๊ทธ๋Œ€๋กœ ๋ฐ˜ํ™˜: V1
    • ์—”ํ‹ฐํ‹ฐ ์กฐํšŒ ํ›„ DTO๋กœ ๋ณ€ํ™˜: V2
    • ํŽ˜์น˜ ์กฐ์ธ์œผ๋กœ ์ฟผ๋ฆฌ ์ˆ˜ ์ตœ์ ํ™”: V3
    • ์ปฌ๋ ‰์…˜ ํŽ˜์ด์ง•๊ณผ ํ•œ๊ณ„ ๋ŒํŒŒ: V3.1
      ์ปฌ๋ ‰์…˜์€ ํŽ˜์น˜ ์กฐ์ธ์‹œ ํŽ˜์ด์ง•์ด ๋ถˆ๊ฐ€๋Šฅ
      ToOne ๊ด€๊ณ„๋Š” ํŽ˜์น˜ ์กฐ์ธ์œผ๋กœ ์ฟผ๋ฆฌ ์ˆ˜ ์ตœ์ ํ™”
      ์ปฌ๋ ‰์…˜์€ ํŽ˜์น˜ ์กฐ์ธ ๋Œ€์‹ ์— ์ง€์—ฐ ๋กœ๋”ฉ์„ ์œ ์ง€ํ•˜๊ณ , hibernate.default_batch_fetch_size ,
      @BatchSize ๋กœ ์ตœ์ ํ™”\

     

    DTO ์ง์ ‘ ์กฐํšŒ

    • JPA์—์„œ DTO๋ฅผ ์ง์ ‘ ์กฐํšŒ: V4
    • ์ปฌ๋ ‰์…˜ ์กฐํšŒ ์ตœ์ ํ™” - ์ผ๋Œ€๋‹ค ๊ด€๊ณ„์ธ ์ปฌ๋ ‰์…˜์€ IN ์ ˆ์„ ํ™œ์šฉํ•ด์„œ ๋ฉ”๋ชจ๋ฆฌ์— ๋ฏธ๋ฆฌ ์กฐํšŒํ•ด์„œ ์ตœ์ ํ™”: V5
    • ํ”Œ๋žซ ๋ฐ์ดํ„ฐ ์ตœ์ ํ™” - JOIN ๊ฒฐ๊ณผ๋ฅผ ๊ทธ๋Œ€๋กœ ์กฐํšŒ ํ›„ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ์›ํ•˜๋Š” ๋ชจ์–‘์œผ๋กœ ์ง์ ‘ ๋ณ€ํ™˜: V6

    ๊ถŒ์žฅ ์ˆœ์„œ
    1. ์—”ํ‹ฐํ‹ฐ ์กฐํšŒ ๋ฐฉ์‹์œผ๋กœ ์šฐ์„  ์ ‘๊ทผ
        1. ํŽ˜์น˜์กฐ์ธ์œผ๋กœ ์ฟผ๋ฆฌ ์ˆ˜๋ฅผ ์ตœ์ ํ™”
        2. ์ปฌ๋ ‰์…˜ ์ตœ์ ํ™”
            1. ํŽ˜์ด์ง• ํ•„์š” hibernate.default_batch_fetch_size , @BatchSize ๋กœ ์ตœ์ ํ™”
            2. ํŽ˜์ด์ง• ํ•„์š”X ํŽ˜์น˜ ์กฐ์ธ ์‚ฌ์šฉ
    2. ์—”ํ‹ฐํ‹ฐ ์กฐํšŒ ๋ฐฉ์‹์œผ๋กœ ํ•ด๊ฒฐ์ด ์•ˆ๋˜๋ฉด DTO ์กฐํšŒ ๋ฐฉ์‹ ์‚ฌ์šฉ
    3. DTO ์กฐํšŒ ๋ฐฉ์‹์œผ๋กœ ํ•ด๊ฒฐ์ด ์•ˆ๋˜๋ฉด NativeSQL or ์Šคํ”„๋ง JdbcTemplate

     

    5. API ๊ฐœ๋ฐœ ๊ณ ๊ธ‰ - ์‹ค๋ฌด ํ•„์ˆ˜ ์ตœ์ ํ™”

    - OSIV์™€ ์„ฑ๋Šฅ ์ตœ์ ํ™”

     

Designed by Tistory.