๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
โญ Personal_Study/Java

JPA: ์ง€์—ฐ๋กœ๋”ฉ๊ณผ ์„ฑ๋Šฅ ์ตœ์ ํ™”

by ํฌ์ŠคํŠธ์‰์ดํฌ 2023. 3. 10.

์ง€์—ฐ ๋กœ๋”ฉ๊ณผ ์„ฑ๋Šฅ ์ตœ์ ํ™”

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


    @GetMapping("/api/v1/simple-orders")
    public List<Order> ordersV1() {
        List<Order> all = orderRepository.findAllByString(new OrderSearch());
        for (Order order : all) {
            order.getMember().getName(); //Lazy ๊ฐ•์ œ ์ดˆ๊ธฐํ™”
            order.getDelivery().getAddress(); //Lazy ๊ฐ•์ œ ์ดˆ๊ธฐํ™”
        }
        return all;
    }

โœ” ์—”ํ‹ฐํ‹ฐ ์ง์ ‘ ๋…ธ์ถœ ์ง€์–‘

โœ” order -> member ์™€ order -> address ๋Š” ์ง€์—ฐ ๋กœ๋”ฉ์ด๋‹ค. ๋”ฐ๋ผ์„œ ์‹ค์ œ ์—”ํ‹ฐํ‹ฐ ๋Œ€์‹ ์— ํ”„๋ก์‹œ ์กด์žฌ

โœ” ์–‘๋ฐฉํ–ฅ ๊ด€๊ณ„๋Š” @Jsonignore ์„ค์ •์„ ํ•ด์ค€๋‹ค.

  • ์„œ๋กœ ํ˜ธ์ถœํ•˜๋ฉด์„œ ๋ฌดํ•œ ๋ฃจํ”„
// Member.java

@Entity
@Getter @Setter
public class Member {

    ...

    @JsonIgnore
    @OneToMany(mappedBy = "member")
    private List<Order> orders = new ArrayList<>();

}

ํ•˜์ด๋ฒ„๋„ค์ดํŠธ ๋ชจ๋“ˆ ๋“ฑ๋ก (spring 3.0 ๋ฏธ๋งŒ)

โœ” jackson ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ ์ด ํ”„๋ก์‹œ ๊ฐ์ฒด๋ฅผ json์œผ๋กœ ์–ด๋–ป๊ฒŒ ์ƒ์„ฑํ•ด์•ผ ํ•˜๋Š”์ง€ ๋ชจ๋ฆ„ ์˜ˆ์™ธ ๋ฐœ์ƒ

โœ” Hibernate5Module ์„ ์Šคํ”„๋ง ๋นˆ์œผ๋กœ ๋“ฑ๋ก

  1. build gradle์— ์˜์กด์„ฑ ์ถ”๊ฐ€
    implementation 'com.fasterxml.jackson.datatype:jackson-datatype-hibernate5'
  2. JpashopApplication ์— ๋‹ค์Œ ์ฝ”๋“œ๋ฅผ ์ถ”๊ฐ€
  3. @Bean Hibernate5Module hibernate5Module() { return new Hibernate5Module(); }

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


    /**
     * V2. ์—”ํ‹ฐํ‹ฐ๋ฅผ ์กฐํšŒํ•ด์„œ DTO๋กœ ๋ณ€ํ™˜(fetch join ์‚ฌ์šฉX)
     * - ๋‹จ์ : ์ง€์—ฐ๋กœ๋”ฉ์œผ๋กœ ์ฟผ๋ฆฌ N๋ฒˆ ํ˜ธ์ถœ
     */
    @GetMapping("/api/v2/simple-orders")
    public List<SimpleOrderDto> ordersV2() {
        List<Order> orders = orderRepository.findAll();
        List<SimpleOrderDto> result = orders.stream()
                .map(o -> new SimpleOrderDto(o))
                .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();
            orderDate = order.getOrderDate();
            orderStatus = order.getStatus();
            address = order.getDelivery().getAddress();
        }
    }

โœ” ์—”ํ‹ฐํ‹ฐ๋ฅผ DTO๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ์ผ๋ฐ˜์ ์ธ ๋ฐฉ๋ฒ•

โœ” ์ฟผ๋ฆฌ 1 + N + N๋ฒˆ ์‹คํ–‰ (N + 1 ๋ฌธ์ œ)

  • order ์กฐํšŒ 1๋ฒˆ
  • order -> member ์ง€์—ฐ ๋กœ๋”ฉ ์กฐํšŒ N ๋ฒˆ
  • order -> delivery ์ง€์—ฐ ๋กœ๋”ฉ ์กฐํšŒ N ๋ฒˆ

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


    /**
     * V3. ์—”ํ‹ฐํ‹ฐ๋ฅผ ์กฐํšŒํ•ด์„œ DTO๋กœ ๋ณ€ํ™˜(fetch join ์‚ฌ์šฉO)
     * - fetch join์œผ๋กœ ์ฟผ๋ฆฌ 1๋ฒˆ ํ˜ธ์ถœ
     */
    @GetMapping("/api/v3/simple-orders")
    public List<SimpleOrderDto> ordersV3() {
        List<Order> orders = orderRepository.findAllWithMemberDelivery();
        List<SimpleOrderDto> result = orders.stream()
                .map(o -> new SimpleOrderDto(o))
                .collect(toList());
        return result;
    }
// OrderRepository.java

    public List<Order> findAllWithMemberDelivery() {
        return em.createQuery(
                "select o from Order o" +
                        " join fetch o.member m" +
                        " join fetch o.delivery d", Order.class)
                .getResultList();
    }

โœ” ์—”ํ‹ฐํ‹ฐ๋ฅผ ํŽ˜์น˜ ์กฐ์ธ(fetch join)์„ ์‚ฌ์šฉํ•ด์„œ ์ฟผ๋ฆฌ 1๋ฒˆ์— ์กฐํšŒ

โœ” ํŽ˜์น˜ ์กฐ์ธ์œผ๋กœ order -> member , order -> delivery ๋Š” ์ด๋ฏธ ์กฐํšŒ ๋œ ์ƒํƒœ ์ด๋ฏ€๋กœ ์ง€์—ฐ๋กœ๋”ฉX

์ฃผ๋ฌธ์กฐํšŒ 4: JPA์—์„œ DTO๋กœ ๋ฐ”๋กœ ์กฐํšŒ


    @GetMapping("/api/v4/simple-orders")
    public List<OrderSimpleQueryDto> ordersV4() {
        return orderSimpleQueryRepository.findOrderDtos();
    }
// OrderSimpleQueryRepository.java

package jpabook.jpashop.repository.order.simplequery;

import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Repository;

import javax.persistence.EntityManager;
import java.util.List;

@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();
    }
}
// OrderSimpleQueryDto.java

package jpabook.jpashop.repository.order.simplequery;

import jpabook.jpashop.domain.Address;
import jpabook.jpashop.domain.OrderStatus;
import lombok.Data;

import java.time.LocalDateTime;

@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 orderStatus, Address address) {
        this.orderId = orderId;
        this.name = name;
        this.orderDate = orderDate;
        this.orderStatus = orderStatus;
        this.address = address;
    }
}

โœ” ์ผ๋ฐ˜์ ์ธ SQL์„ ์‚ฌ์šฉํ•  ๋•Œ์ฒ˜๋Ÿผ ์›ํ•˜๋Š” ๊ฐ’ ์„ ํƒํ•ด์„œ ์กฐํšŒ

โœ” new ๋ช…๋ น์–ด๋ฅผ ์‚ฌ์šฉํ•ด์„œ JPQL์˜ ๊ฒฐ๊ณผ๋ฅผ DTO๋กœ ์ฆ‰์‹œ ๋ณ€ํ™˜

โœ” SELECT ์ ˆ์—์„œ ์›ํ•˜๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ์ง์ ‘ ์„ ํƒํ•˜๋ฏ€๋กœ DB ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋„คํŠธ์› ์šฉ๋Ÿ‰ ์ตœ์ ํ™”(์ƒ๊ฐ๋ณด๋‹ค ํฌ์ง„ ์•Š๋‹ค)

โœ” ๋ฆฌํฌ์ง€ํ† ๋ฆฌ ์žฌ์‚ฌ์šฉ์„ฑ ๋–จ์–ด์ง, API ์ŠคํŽ™์— ๋งž์ถ˜ ์ฝ”๋“œ๊ฐ€ ๋ฆฌํฌ์ง€ํ† ๋ฆฌ์— ๋“ค์–ด๊ฐ€๋Š” ๋‹จ์ 

์ •๋ฆฌ

โœ” ์ฟผ๋ฆฌ ๋ฐฉ์‹ ์„ ํƒ ๊ถŒ์žฅ ์ˆœ์„œ

  1. ์šฐ์„  ์—”ํ‹ฐํ‹ฐ๋ฅผ DTO๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์„ ํƒํ•œ
  2. ํ•„์š”ํ•˜๋ฉด ํŽ˜์น˜ ์กฐ์ธ์œผ๋กœ ์„ฑ๋Šฅ ์ตœ์ ํ™”. (๋Œ€๋ถ€๋ถ„์˜ ์„ฑ๋Šฅ ์ด์Šˆ๊ฐ€ ํ•ด๊ฒฐ๋จ)
  3. ๊ทธ๋ž˜๋„ ์•ˆ๋˜๋ฉด DTO๋กœ ์ง์ ‘ ์กฐํšŒํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์‚ฌ์šฉ
  4. ์ตœํ›„์˜ ๋ฐฉ๋ฒ•์€ JPA๊ฐ€ ์ œ๊ณตํ•˜๋Š” ๋„ค์ดํ‹ฐ๋ธŒ SQL์ด๋‚˜ ์Šคํ”„๋ง JDBC Template์„ ์‚ฌ์šฉํ•ด์„œ SQL์„ ์ง์ ‘ ์‚ฌ์šฉ

๋Œ“๊ธ€