JiwonDev

JPA ์„ฑ๋Šฅ ๊ฐœ์„ ํŒ

by JiwonDev

์ธํ”„๋Ÿฐ ๊น€์˜ํ•œ๋‹˜

 

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

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

www.inflearn.com

๋Œ€๋ถ€๋ถ„ ์„ฑ๋Šฅ ๋ฌธ์ œ๋Š” ์กฐํšŒ์—์„œ ๋ฐœ์ƒํ•œ๋‹ค. ์•ฑ๊ณผ JPA๋‹จ์—์„œ ํ•  ์ˆ˜ ์žˆ๋Š” ์ตœ์ ํ™”๋ฅผ ์•Œ์•„๋ณด์ž.

 

๐Ÿ’ญ ์š”์•ฝ

1. ์šฐ์„  Entity๋กœ ๊ฐ์ฒด์ง€ํ–ฅ์ ์œผ๋กœ ์กฐํšŒํ•œ๋‹ค.
- ์—ฐ๊ด€ ๊ฐ์ฒด ์กฐํšŒ๋Š” Lazy๋ฅผ ๊ธฐ๋ณธ์œผ๋กœ ๊น”๊ณ , ์กฐํšŒํ•  ๋•Œ join fetch๋กœ ์‚ฌ์šฉํ•˜๋Š” ์—ฐ๊ด€ ๊ฐ์ฒด๋ฅผ ํ•œ๋ฐฉ์ฟผ๋ฆฌ๋กœ ๊ฐ€์ ธ์˜จ๋‹ค.


2. Entity์—์„œ ์—ฐ๊ด€๊ด€๊ณ„์ธ ์ปฌ๋ ‰์…˜ (List, Set)์„ ๊ฐ€์ ธ์˜ค๊ณ  ์‹ถ๋‹ค๋ฉด

- ํŽ˜์ด์ง•์ด ํ•„์š”์—†๋‹ค๋ฉด join fetch๋กœ ๊ฐ€์ ธ์˜จ๋‹ค. (๋‹จ ์ปฌ๋ ‰์…˜์ด 1๊ฐœ์ผ ๊ฒฝ์šฐ์—๋งŒ ํŒจ์น˜์กฐ์ธ ๊ฐ€๋Šฅ)

- ํŽ˜์ด์ง•์ด ํ•„์š”ํ•˜๋‹ค๋ฉด ๊ทธ๋ƒฅ Lazy๋กœ ๋ƒ…๋‘๊ณ  @BatchSize (hibernate.default_batch_fetch_size ์ถ”์ฒœ)๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.

- ์‚ฌ์‹ค ์ด์ •๋„๋งŒ ํ•˜๋”๋ผ๋„ ๋Œ€๋ถ€๋ถ„ ์„œ๋น„์Šค์—์„œ๋Š” ๋ฌธ์ œ ์—†๋‹ค.

 

3. ์ด ๋‹จ๊ณ„๋กœ ํ•ด๊ฒฐ์ด ์•ˆ๋˜๋Š” ๋Œ€ํ˜• ์„œ๋น„์Šค๋ผ๋ฉด ๊ทธ๋ƒฅ Redis ๊ฐ™์€ ์บ์‹œ๋ฅผ ์“ฐ์ž.

- ์ฐธ๊ณ ๋กœ DTO ๋ฅผ ์บ์‹œํ•ด์•ผํ•œ๋‹ค. Entity๋ฅผ ์บ์‹œ(2์ฐจ์บ์‹œ)ํ•˜๋Š”๊ฑด ์ •ํ•ฉ์„ฑ ๋ฌธ์ œ๋กœ ์‹ค๋ฌด์—์„œ ์‚ฌ์šฉํ•˜๊ธฐ ๋„ˆ๋ฌด ๊นŒ๋‹ค๋กญ๋‹ค.

- ์‚ฌ์‹ค ๊ทธ์ •๋„์˜ ๋Œ€ํ˜•์„œ๋น„์Šค๋ผ๋ฉด, ์กฐํšŒ์—์„œ๋Š” JPA๋ฅผ ํฌ๊ธฐํ•˜๊ณ  MyBatis๋‚˜ Native SQL ์ƒ ์ฟผ๋ฆฌ๋ฅผ ํ•จ๊ป˜ ์‚ฌ์šฉํ•œ๋‹ค.

 

4. OSIV๋Š” ์“ฐ์ง€๋ง์ž. ์ฐธ๊ณ ๋กœ ๊ธฐ๋ณธ๊ฐ’์ด true๋ผ์„œ ์ง์ ‘ ์„ค์ •ํ•ด์ค˜์•ผํ•œ๋‹ค.

spring.jpa.open-in-view : true // ๊ธฐ๋ณธ๊ฐ’

 


๐Ÿ’ญ API์—์„œ Entity๋ฅผ ์ง์ ‘ ์กฐํšŒํ•˜์ง€  ๋งˆ๋ผ.

์ด ์ •๋„๋Š” ๋‹น์—ฐํ•œ ์ƒ์‹์ด์ง€๋งŒ, ์•„๋ž˜์™€ ๊ฐ™์ด ๋„˜์–ด๊ฐ€๋ฉด ์•ˆ๋œ๋‹ค. ์•ˆ๋˜๋Š” ์ด์œ ๋ฅผ ์•Œ๊ณ  ์žˆ์–ด์•ผ ํ•œ๋‹ค.

๐Ÿคฆ‍โ™‚๏ธ : ์™œ DTO๋‚˜ Service๋ฅผ ์‚ฌ์šฉํ•˜๋ƒ๊ณ ..? ๊ทธ๋ƒฅ ๋‚จ๋“ค์ด ๋‹ค ๊ทธ๋ ‡๊ฒŒ ํ•˜๋‹ˆ๊นŒ?"

  • ๋„๋ฉ”์ธ์ด API์— ์˜์กด์„ฑ์ด ์ƒ๊ธด๋‹ค. ๋‚˜์ค‘์— ๋ณ€๊ฒฝ, ์œ ์ง€๋ณด์ˆ˜ํ•˜๊ธฐ ๋งค์šฐ ์–ด๋ ต๋‹ค.
  • ์บก์Šํ™”๊ฐ€ ๊นจ์ง„๋‹ค. ๊ฐ’์„ ์–ด๋””์„œ ๊บผ๋ƒˆ๋Š”์ง€, ์–ด๋””์„œ ๋ณ€๊ฒฝ๋˜์—ˆ๋Š”์ง€ ์•Œ๊ธฐ ์–ด๋ ต๋‹ค.
  • JPA ์—ฐ๊ด€ ๊ด€๊ณ„ ๋•Œ๋ฌธ์— 1+N ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•˜๊ธฐ ์‰ฝ๋‹ค. ( ํšŒ์› 1๊ฑด ์กฐํšŒ -> ์—ฐ๊ด€๋œ ์ฃผ๋ฌธ ๊ฐ์ฒด N๊ฑด ์กฐ์ธํ•ด์„œ ๊ฐ™์ด ์กฐํšŒ)
@GetMapping("/api/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;
}

 

 

๐Ÿ“‘ ํ•ด๊ฒฐ์ฑ… - Lazy์™€ ํŒจ์น˜์กฐ์ธ

1. ์—ฐ๊ด€๊ด€๊ณ„๋Š” Lazy๋ฅผ ๊ธฐ๋ณธ์œผ๋กœ ์‚ฌ์šฉํ•œ๋‹ค.

- ์˜คํ•ดํ•˜๋ฉด ์•ˆ๋˜๋Š”๊ฒŒ, Lazy๋Š” ๋งŒ๋Šฅ์ด ์•„๋‹ˆ๋‹ค. ์—ฐ๊ด€ ๊ด€๊ณ„๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์‹œ์ ์— ๊ฒฐ๊ตญ 1+N๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.

 

2. ์ ˆ๋Œ€๋กœ Entity ๋ฅผ ์ง์ ‘ ๋…ธ์ถœ์‹œํ‚ค์ง€ ์•Š๋Š”๋‹ค. DTO๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.

3. ์—ฐ๊ด€๊ด€๊ณ„๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ์—๋Š” ํŒจ์น˜์กฐ์ธ(join fetch)์œผ๋กœ ๊ฐ€์ ธ์˜จ๋‹ค

@GetMapping("/api/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.findAllWithMemberDelivery
public List<Order> findAllWithMemberDelivery() {
    return em.createQuery(
            "select o from Order o" +
                " join fetch o.member m" + // Inner Join ์œผ๋กœ o.member๋ฅผ ๊ฐ€์ ธ์˜จ๋‹ค.
                " join fetch o.delivery d", Order.class) // o.delivery๋„ ๊ฐ€์ ธ์˜จ๋‹ค.
        .getResultList();
}

JPQL ์กฐ์ธ์— fetch๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด๋Š” ์—ฐ๊ด€ ๊ฐ์ฒด(๋˜๋Š” ์ปฌ๋ ‰์…˜)๊นŒ์ง€ Inner Join์„ ์‚ฌ์šฉํ•ด์„œ ํ•œ ์ฟผ๋ฆฌ๋กœ ๊ฐ€์ ธ์˜จ๋‹ค.

์ฐธ๊ณ ๋กœ Lazy์— ๊ทธ๋ƒฅ ์กฐ์ธ๋งŒ ์‚ฌ์šฉ(select m form Member m join m.team)์€ ์—ฐ๊ด€ ๊ฐ์ฒด(team) ๊ฐ’์„ ๋ฏธ๋ฆฌ ๊ฐ€์ ธ์˜ค์ง€ ์•Š๋Š”๋‹ค.

 

 

4. (๊ทธ๋ž˜๋„ ์„ฑ๋Šฅ์ด ์•ˆ๋‚˜์˜จ๋‹ค๋ฉด) JPA๋ฅผ ํฌ๊ธฐํ•˜๊ณ , ํ•ด๋‹น DB์˜ SQL์„ ์ง์ ‘ ์ž‘์„ฑํ•œ๋‹ค. 

// JPA Native Query๋ฅผ ์ด์šฉํ•ด๋„ ๋˜๊ณ , ๊ทธ๋ƒฅ JDBC Template ์„ ์‚ฌ์šฉํ•ด๋„ ๋œ๋‹ค. (Mybatis ๋“ฑ)
String sql = "SELECT ID, AGE, NAME, TEAM_ID" +
             " FROM MEMBER WHERE AGE > ?";
 
Query nativeQuery = em.createNativeQuery(sql, Member.class)
                      .setParameter(1, 20);
 
List<Member> resultList = nativeQuery.getResultList();

 

 

๐Ÿ™„ : ๊ทธ๋ƒฅ ์›ํ•˜๋Š” ๊ฐ’๋งŒ ์ฟผ๋ฆฌ๋กœ ๋ฝ‘์•„์„œ DTO๋กœ ๋ฐ˜ํ™˜ํ•˜๋ฉด ์•ˆ๋˜๋‚˜์š”?

Entity๋ฅผ ๊ฑฐ์น˜์ง€ ์•Š๊ณ , ์ผ๋ฐ˜ ์ฟผ๋ฆฌ๋ฌธ์ฒ˜๋Ÿผ ๊ฐ’์„ ์„ ํƒํ•ด์„œ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ๋‹ค. DB I/O ์„ฑ๋Šฅ์ด ์กฐ๊ธˆ ์ข‹์•„์ง€๊ธด ํ•œ๋‹ค.

๋‹จ ์žฌ์‚ฌ์šฉ์„ฑ์ด ๋–จ์–ด์ง€๊ณ  DTO๊ฐ€ ๋ฆฌํฌ์ง€ํ† ๋ฆฌ์— ๋“ค์–ด๊ฐ€์„œ ์ถ”์ฒœํ•˜๋Š” ๋ฐฉ๋ฒ•์€ ์•„๋‹ˆ๋‹ค. (API์™€ DB์— ์˜์กด์„ฑ์ด ์ƒ๊ธด๋‹ค.)

@GetMapping("/api/orders")
public List<OrderSimpleQueryDto> ordersV4() {
    return orderSimpleQueryRepository.findOrderDtos();
}
public List<OrderSimpleQueryDto> findOrderDtos() {
    return em.createQuery(
            "select new package.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();
}

 

๋ชจ๋“  ๊ฑด ํŠธ๋ ˆ์ด๋“œ ์˜คํ”„์ด๋‹ค. ๋Œ€๋ถ€๋ถ„์˜ ๋น„์ฆˆ๋‹ˆ์Šค ์„œ๋น„์Šค๋Š” ์ด๋Ÿฐ ์ฝ”๋“œ๊ฐ€ ์•ˆ์ข‹๋‹ค.

ํ•˜์ง€๋งŒ ๋ณ€๊ฒฝํ•  ์ผ์ด ์—†๊ณ , ํ†ต๊ณ„ ๋ฐ์ดํ„ฐ๋ฅผ ์š”์ฒญํ•˜๋Š” API ํŠธ๋ž˜ํ”ฝ์ด ๋งŽ๋‹ค๋ฉด, ์ด๋Ÿฐ ๋ฐฉ๋ฒ•๋„ ๊ณ ๋ฏผ ๋Œ€์ƒ์— ๋“ค์–ด๊ฐˆ ์ˆ˜ ์žˆ๋‹ค.

 

 


๐Ÿ’ญ ์—ฐ๊ด€๊ด€๊ณ„ ์ปฌ๋ ‰์…˜ ์กฐํšŒ(OneToMany)

@OneToOne, @ManyToOne ๊ด€๊ณ„๋Š” ํ•„์š”ํ•  ๋•Œ ํŒจ์น˜์กฐ์ธ์œผ๋กœ ๊ฐ™์ด ๊ฐ€์ ธ์˜ค๋ฉด ๋œ๋‹ค.

๊ทธ๋ ‡๋‹ค๋ฉด @OneToMany, ์ฆ‰ ์—ฐ๊ด€ ์ปฌ๋ ‰์…˜์€ ์–ด๋–ป๊ฒŒ ํ•ด์•ผํ• ๊นŒ. ๊ทธ๋ƒฅ ํŒจ์น˜์กฐ์ธ์œผ๋กœ ํ†ต์งธ๋กœ ๋‹ค ๊ฐ€์ ธ์˜ค๋ฉด ๋˜๋Š”๊ฑธ๊นŒ? 

order์—์„œ List&amp;amp;amp;amp;amp;lt;OrderItem&amp;amp;amp;amp;amp;gt;์„ ๊ฐ€์ ธ์˜ฌ๋ ค๋ฉด?

 

 

๐Ÿ™„ : ?? ๊ทธ๋ƒฅ ์—ฐ๊ด€๊ด€๊ณ„์ธ ์ปฌ๋ ‰์…˜๋„ join fetch๋กœ ๊ฐ€์ ธ์˜ค๋ฉด ๋˜๋Š”๊ฑฐ ์•„๋‹Œ๊ฐ€์š”?

๋งž๋Š” ๋ง์ด๋‹ค. ํ•˜์ง€๋งŒ ์•„๋ฌด ์ƒ๊ฐ์—†์ด ์‚ฌ์šฉํ•˜๋ฉด ํฐ์ผ๋‚œ๋‹ค.

@GetMapping("/api/orders")
public List<OrderDto> ordersV3() {
    List<Order> orders = orderRepository.findAllWithItem();
    List<OrderDto> result = orders.stream()
            .map(o -> new OrderDto(o))
            .collect(toList());

    return result;
}
// orderRespoitory.findAllWithItem()
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" + // List<OrderItem> ํŒจ์น˜์กฐ์ธ
                " join fetch oi.item i", Order.class) // OrderItem.Item ํŒจ์น˜์กฐ์ธ
        .getResultList();
}

์ด ํŒจ์น˜์กฐ์ธ์€, ์•„๋ž˜์˜ SQL๋ฌธ์„ ๋งŒ๋“ ๋‹ค.

Select * from orders o
join order_item oi on o.order_id = oi.order_id

 

DB๊ณต๋ถ€๋ฅผ ํ–ˆ๋‹ค๋ฉด ์•Œ๊ฒ ์ง€๋งŒ ์ด๋Ÿฐ ํ˜•์‹์˜ ์กฐ์ธ ์ฟผ๋ฆฌ๋Š” ๋˜‘๊ฐ™์€ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐฐ๋กœ ๋ปฅํŠ€๊ธฐ์‹œ์ผœ๋ฒ„๋ฆฐ๋‹ค.
Order๊ฐ€ ๋‹จ 1๊ฐœ ๋ฐ–์— ์—†๋”๋ผ๋„, OrderItem์ด N๊ฐœ๋ผ๋ฉด

์กฐ์ธํ–ˆ์„ ๋•Œ ์ค‘๋ณต๋œ Order ๋ฐ์ดํ„ฐ๋กœ N๊ฐœ๊ฐ€ ์ฐํžˆ๊ฒŒ๋˜๋ฒ„๋ฆฐ๋‹ค. (์—ฌ๋Ÿฌ ๊ฐœ๋ผ๋ฉด ๊ทธ๋งŒํผ ํ–‰ ์ˆ˜๊ฐ€ ๊ณฑํ•ด์ง„๋‹ค.)

ํ…Œ์ด๋ธ”์„ ์กฐ์ธํ–ˆ๊ธฐ ๋•Œ๋ฌธ์— ๋˜‘๊ฐ™์€ Order_ID ๋ฅผ ์—ฌ๋Ÿฌ์ค„๋กœ ๋ณต์‚ฌํ•ด์„œ ์กฐํšŒํ•œ๋‹ค.

 

์‹ค์ œ๋กœ ํ•˜์ด๋ฒ„๋„ค์ดํŠธ์— 2๊ฐœ์ด์ƒ์˜ ํŒจ์น˜์กฐ์ธ(entity -> toMany -> toMany)์„ ์‹œ๋„ํ•˜๋ฉด `MultipleBagFetchException` ๋ฐœ์ƒ์‹œํ‚จ๋‹ค.

 

๐Ÿ“‘ ํ•ด๊ฒฐ์ฑ… - ํŒจ์น˜์กฐ์ธ์— distinct ๋ฅผ ๋„ฃ์–ด์„œ ์ค‘๋ณต์„ ์ œ๊ฑฐํ•œ๋‹ค.

์‹ค์ œ๋กœ JPQL ์—์„œ distinct ํ‚ค์›Œ๋“œ๋Š” DB SQL์— ๊ทธ๋Œ€๋กœ ๋ถ™์—ฌ์ฃผ๋Š” ํšจ๊ณผ ์™ธ์— ํ•œ๊ฐ€์ง€ ๊ธฐ๋Šฅ์„ ๋”ํ•ด์ค€๋‹ค.

๋ฐ”๋กœ Entity ID๋ฅผ ์ด์šฉํ•ด ์ค‘๋ณต๋œ ๋ฐ์ดํ„ฐ๊ฐ€ ์กด์žฌํ•œ๋‹ค๋ฉด, JPA ์•ฑ ๋ฉ”๋ชจ๋ฆฌ์ƒ์—์„œ ์ค‘๋ณต์„ ์ œ๊ฑฐํ•ด์ค€๋‹ค.

// orderRespoitory.findAllWithItem()
public List<Order> findAllWithItem() {
    return em.createQuery(
            "select distinct o from Order o" +
                " join fetch o.member m" +
                " join fetch o.delivery d" +
                " join fetch o.orderItems oi" + // List<OrderItem> ํŒจ์น˜์กฐ์ธ
                " join fetch oi.item i", Order.class) // OrderItem.Item ํŒจ์น˜์กฐ์ธ
        .getResultList();
}

 

 

๐Ÿงจ ์ฃผ์˜ (ํŽ˜์ด์ง•) - ์ฟผ๋ฆฌ๋Š” ํ•˜๋‚˜์ง€๋งŒ, ๋ฐ์ดํ„ฐ ์ค‘๋ณต์€ ๊ทธ๋Œ€๋กœ๋‹ค.

์—ฌ๊ธฐ์„œ ์ค‘์š”ํ•œ์ ์€, ์•ฑ ์ƒ์—์„œ๋Š” ์ค‘๋ณต์ด ํ•ด๊ฒฐ๋œ ๊ฒƒ ๊ฐ™์ง€๋งŒ ์š”์ฒญ ์ฟผ๋ฆฌ๋ฌธ์€ ๊ทธ๋Œ€๋กœ ๋ผ๋Š” ์ ์ด๋‹ค.

์กฐ์ธ๋œ ํ…Œ์ด๋ธ”์€ Order ๋งŒ ์ค‘๋ณต์ผ ๋ฟ, Item์— ๋Œ€ํ•œ ์ •๋ณด๋Š” ์ค‘๋ณต์ด ์•„๋‹ˆ๋‹ค. 

 

1. ์•„๋ž˜์™€ ๊ฐ™์€ ํŽ˜์ด์ง• ์ฟผ๋ฆฌ๊ฐ€ ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ•ด๋ณด์ž.

public List<Order> findAllWithMemberDelivery() {
    return em.createQuery(
            "select o from Order o" +
                    " join fetch o.member m" +
                    " join fetch o.delivery d", Order.class)
            .setFirstResult(1) // ์‹œ์ž‘์ง€์ 
            .setMaxResults(100) // ํŽ˜์ด์ง€ ๊ฐœ์ˆ˜
            .getResultList();
}

ํ…Œ์ŠคํŠธํ•  ๋•Œ์—๋Š” ์•ฑ ์ƒ์—์„œ ์ค‘๋ณต์ด ์ œ๊ฑฐ๋˜๋‹ˆ๊นŒ ์ •์ƒ์ž‘๋™ ํ•˜๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ๋ณด์ธ๋‹ค.

ํ•˜์ง€๋งŒ ์‹ค์ œ๋กœ๋Š”? ๋ง๋„ ์•ˆ๋˜๋Š” ๊ฐœ์ˆ˜์˜ ์ฟผ๋ฆฌ๊ฐ€ ๋ปฅํŠ€๊ธฐ๋˜์–ด ์ฐํžˆ๊ณ  ์žˆ๋‹ค. ์ฆ‰, ์ปฌ๋ ‰์…˜์„ ํŒจ์น˜์กฐ์ธ ํ•ด๋ฒ„๋ฆฌ๋ฉด ํŽ˜์ด์ง•์ด ์‚ฌ์‹ค ์ƒ ๋ถˆ๊ฐ€๋Šฅํ•ด์ง„๋‹ค.

 

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

ํ•˜๋‚˜์˜ Order์—์„œ List<OrderItems> , List<SpecialItems> ์ด๋ ‡๊ฒŒ 2๊ฐœ ์ด์ƒ์˜ ์ปฌ๋ ‰์…˜์„ ํŒจ์น˜์กฐ์ธํ•˜๋ฉด ์ ˆ๋Œ€ ์•ˆ๋œ๋‹ค.

์™œ๋ƒํ•˜๋ฉด ๋ฐ์ดํ„ฐ๊ฐ€ ์—„์ฒญ๋‚˜๊ฒŒ ๋ปฅํŠ€๊ธฐ ๋˜์–ด [ ํšŒ์›์ˆ˜ * ์•„์ดํ…œ ์ˆ˜ *  ์•„์ดํ…œ ์ˆ˜ ] ์˜ ๋ฏธ์นœ ํ•œ๋ฐฉ ์ฟผ๋ฆฌ๊ฐ€ ๋‚˜์˜ค๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

์‹ค์ œ๋กœ ์ด๋Ÿฐ์‹์˜ ํŒจ์น˜์กฐ์ธ์˜ ์‚ฌ์šฉ์€ ์ •ํ•ฉ์„ฑ์„ ๋ณด์žฅํ•  ์ˆ˜ ์—†๊ธฐ์— ์•„๋ž˜์™€ ๊ฐ™์€ ํ•˜์ด๋ฒ„๋„ค์ดํŠธ ๊ฒฝ๊ณ  ๋กœ๊ทธ๊ฐ€ ์ฐํžŒ๋‹ค.

ํ•˜์ด๋ฒ„๋„ค์ดํŠธ ๊ฒฝ๊ณ . ๋ง๋„ ์•ˆ๋˜๊ฒŒ ๋งŽ์€ ๋ฐ์ดํ„ฐ๋ฅผ ๋ถˆ๋Ÿฌ์™€์„œ, ๋ฉ”๋ชจ๋ฆฌ์—์„œ ํŽ˜์ด์ง•์„ ์‹œ๋„ํ•œ๋‹ค. (์ตœ์•…์˜ ๊ฒฝ์šฐ ๋ฉ”๋ชจ๋ฆฌ ๋ถ€์กฑ์œผ๋กœ ์•ฑ์ด ์ฃฝ๋Š”๋‹ค.)
Order - List&amp;amp;amp;lt;OrderItem&amp;amp;amp;gt; ๋งŒ ํ•ด๋„ ์ด๋ ‡๊ฒŒ ์ค‘๋ณต๋˜๋Š”๋ฐ, ์—ฌ๊ธฐ์— List&amp;amp;amp;lt;SpecialItem&amp;amp;amp;gt; ํ•œ๊ฐœ๋ฅผ ๋” ์กฐ์ธํ•˜๋ฉด..?

 

 

๐Ÿ“‘ ํŽ˜์ด์ง• ํ•ด๊ฒฐ์ฑ… - @BatchSize

1. ๋จผ์ € ToOne ๊ด€๊ณ„๋งŒ ๋”ฐ๋กœ ํŒจ์น˜์กฐ์ธ์œผ๋กœ ๊ฐ€์ ธ์˜จ๋‹ค.

2. ์ปฌ๋ ‰์…˜์€ ํŒจ์น˜์กฐ์ธ ํ•˜์ง€์•Š๊ณ , ๊ธฐ์กด Lazy๋กœ ๊ทธ๋Œ€๋กœ ๋‘”๋‹ค. (์ด๋Œ€๋กœ ๋‘๋ฉด 1+N์ด ๋ฐœ์ƒํ•œ๋‹ค.)

@GetMapping("/api/orders")
public List<OrderDto> orders_page(
    @RequestParam(value = "offset", defaultValue = "0") int offset,
    @RequestParam(value = "limit", defaultValue = "100") int limit) {

    List<Order> orders = orderRepository.findAllWithMemberDelivery(offset, limit);
    
    // order.OrderItems ๋ฅผ ๊บผ๋‚ผ ๋•Œ 1+N ๋ฐœ์ƒ
    // order.OrderItems.Item ์„ ๊บผ๋‚ด๋ฉด์„œ 1(Order) + N(OrderItems) + N(Items) ๊ฐœ์˜ ์ฟผ๋ฆฌ ์™„์„ฑ
    List<OrderDto> result = orders.stream().
        .map(o -> new OrderDto(o)) 
        .collect(toList());

    return result;
}
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();
}

 

3. @BatchSize ๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค. (๋˜๋Š” hibernate.default_batch_fetch_size ์„ค์ •์„ ์ด์šฉํ•ด ์ „์—ญ์ ์œผ๋กœ ์„ค์ •ํ•œ๋‹ค.)

์ด ์˜ต์…˜์„ ์‚ฌ์šฉํ•˜๋ฉด ์ปฌ๋ ‰์…˜์ด๋‚˜ ํ”„๋ก์‹œ ๊ฐ์ฒด๋ฅผ ์„ค์ •ํ•œ ๊ฐœ์ˆ˜๋งŒํผ [ Where IN (pk1, pk2 ...) ]๋กœ ์กฐํšŒํ•œ๋‹ค.

  • ํ…Œ์ด๋ธ”๋งˆ๋‹ค key๋กœ ์ฐ์–ด์„œ ์กฐํšŒํ•˜๋Š”๊ฑฐ๋ผ, ์ค‘๋ณต ๋ฐ์ดํ„ฐ๊ฐ€ ์—†๋‹ค. (SQL ์ •๊ทœํ™”)
  • Where in (key)๋Š” ๋™๋“ฑ๋น„๊ต(=)์™€ ๋™์ผํ•œ ์„ฑ๋Šฅ์ด๋ผ DB์—๋„ ์ตœ์ ํ™” ๋˜์žˆ๋‹ค.

@BatchSize(size = 100) // ์—”ํ‹ฐํ‹ฐ์— ์„ค์ •
@Entity
public class Item {
    ...
}


@Entity
public class Parent {

    @BatchSize(size = 100) // ์ปฌ๋ ‰์…˜ ํ•„๋“œ์— ์„ค์ •
    @OneToMany(mappedBy = "parent")
    private List<Child> children = new ArrayList<>();
}
spring: # resource/application.yml
  jpa:
    hibernate:
      ddl-auto: create
    
    properties:
      hibernate:
        format_sql: true
        default_batch_fetch_size: 1000 # ๊ธฐ๋ณธ ๋ฐฐ์น˜์‚ฌ์ด์ฆˆ (limit 1000)

 

@BatchSize๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์ปฌ๋ ‰์…˜์ด๋‚˜ ํ”„๋ก์‹œ ๊ฐ์ฒด๋ฅผ ์„ค์ •ํ•œ ๊ฐœ์ˆ˜๋งŒํผ [ WHERE IN ์ฟผ๋ฆฌ ]๋กœ ํ•„์š”ํ•œ ์—ฐ๊ด€ ๊ฐ’์„ ๊ฐ€์ ธ์˜จ๋‹ค.

์ฟผ๋ฆฌ๋ฅผ ํ•˜๋‚˜๋กœ๋Š” ๋ชป๋งŒ๋“ค์ง€๋งŒ, 1+N+N ์ฟผ๋ฆฌ๋ฅผ 1+1+1 ์ฟผ๋ฆฌ๋กœ ๋ฐ”๊ฟ€ ์ˆ˜ ์žˆ๋‹ค.

์ฐธ๊ณ ๋กœ 100๊ฐœ๋ฅผ ์กฐํšŒํ•˜๋Š”๋ฐ, @BatchSize(20)์ด๋ผ๋ฉด JPA๋Š” ์ฟผ๋ฆฌ๋ฅผ 5๋ฒˆ (100/20 = 5๊ฐœ) ๋‚ ๋ฆฐ๋‹ค.

ํŒจ์น˜์กฐ์ธ์€ ์ค‘๋ณต์€ ๋ฐœ์ƒํ•˜์ง€๋งŒ, ์ฟผ๋ฆฌ๋Š” ๋‹จ 1๊ฐœ๊ฐ€ ์ฐํžŒ๋‹ค. (ํŽ˜์ด์ง•์€ ์‚ฌ์‹ค์ƒ ๋ถˆ๊ฐ€๋Šฅ)

๊ธฐ์กด์˜ ์ฝ”๋“œ๋Š” Order * OrderItems * Items ๊ฐœ์ˆ˜๋งŒํผ ์ฟผ๋ฆฌ๊ฐ€ ์ฐํžŒ๋‹ค.

 

DB์—์„œ์˜ Where In ์‚ฌ์šฉ
@BatchSize๋ฅผ ์‚ฌ์šฉํ–ˆ์„ ๋•Œ JPA์— ์ฐํžŒ ์ฟผ๋ฆฌ
์‚ฌ์šฉํ•˜๋Š” ID (4,11)๋งŒ where in ํ•œ๋ฐฉ ์ฟผ๋ฆฌ๋กœ ๊ฐ€์ ธ์™”๋‹ค.

 

 

๐Ÿ™„ : ์ฟผ๋ฆฌ๊ฐ€ ์ ๊ฒŒ ์ฐํžˆ๋„๋ก BatchSize๋ฅผ ํฌ๊ฒŒํ•˜๋Š”๊ฒŒ ์ข‹์„๊นŒ์š”?

์‚ฌ์‹ค DB์™€ ํšŒ์‚ฌ๋งˆ๋‹ค ๋‹ฌ๋ผ์„œ ์ •๋‹ต์€ ์—†์ง€๋งŒ, ์ตœ๋Œ€ 1000๊ฐœ๋ฅผ ์•ˆ๋„˜๋„๋ก ์„ค์ •ํ•ด์ฃผ๋Š”๊ฒŒ ์ข‹๋‹ค.

  • Where In ์ฟผ๋ฆฌ๊ฐ€ 1000๊ฐœ๊ฐ€ ๋„˜์–ด๊ฐ€๋ฉด DB ์˜ค๋ฅ˜๋ฅผ ์ผ์œผํ‚ค๋Š” ๊ฒฝ์šฐ๊ฐ€ ์ข…์ข… ์žˆ์–ด์„œ ๊ทธ ์ด์ƒ์€ ๊ถŒ์žฅํ•˜์ง€ ์•Š๋Š”๋‹ค.
  • ๊ฐœ์ˆ˜๊ฐ€ ๋„ˆ๋ฌด ๋งŽ์œผ๋ฉด ํ•œ๋ฒˆ์— ์ฒ˜๋ฆฌํ•ด์•ผํ•  ์š”์ฒญ ๋ฐ์ดํ„ฐ๊ฐ€ ์ƒ๋‹นํžˆ ์ปค์ง„๋‹ค. ์ด๋Š” DB์™€ ์•ฑ ์„œ๋ฒ„์— ๋ถ€๋‹ด์„ ์ค„ ์ˆ˜ ์žˆ๋‹ค.
  • ๊ฐœ์ˆ˜๊ฐ€ ์ž‘์œผ๋ฉด DB, ์•ฑ์„œ๋ฒ„์— ๋ถ€ํ•˜๋Š” ์ค„๊ฒ ์ง€๋งŒ, ๊ทธ๋งŒํผ ์š”์ฒญ ํšŸ์ˆ˜๊ฐ€ ๋งŽ์•„์ ธ์„œ ๋„คํŠธ์›Œํฌ ๋Œ€๊ธฐ์‹œ๊ฐ„์ด ๊ธธ์–ด์ง„๋‹ค.

์•ฑ์„œ๋ฒ„(WAS), DB์— ๋ถ€๋‹ด์„ ์ฃผ์ง€์•Š๊ฒŒ 100์œผ๋กœ ์„ค์ •ํ•ด๋†“๊ณ , ๋‚˜์ค‘์— ๋ณ€๊ฒฝํ•˜๋Š”๊ฑธ ์ถ”์ฒœํ•œ๋‹ค. 

์ฐธ๊ณ ๋กœ ๋„ˆ๋ฌด ํฌ๋ฉด ์•ฑ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ๋งŽ์ด ์‚ฌ์šฉํ•ด์„œ ์•ˆ๋œ๋‹ค๋Š” ๋ง๋„ ํ•˜๋Š”๋ฐ, ์ด๋Š” ์ „ํ˜€ ์ƒ๊ด€์—†๋Š” ์ด์•ผ๊ธฐ์ด๋‹ค.

List<OrderItem>์— ์ฟผ๋ฆฌ๋ฅผ ์—ฌ๋Ÿฌ๋ฒˆ ์ฐ๋Š”๋‹ค๊ณ ํ•ด์„œ, ์ ˆ๋ฐ˜๋งŒ ์ฑ„์šฐ๊ณ  API๋ฅผ ์—ฌ๋Ÿฌ๋ฒˆ ์ „์†กํ•˜์ง€๋Š” ์•Š๋Š”๋‹ค. ๊ฒฐ๊ตญ ์ฟผ๋ฆฌ๊ฐ€ ํ•œ๋ฐฉ์ด๋“  ์—ฌ๋Ÿฌ๋ฐฉ์ด๋“  ์•ฑ์—์„  List๊ฐ€ ๊ฐ€๋“ ์ฐฐ ๋•Œ๊นŒ์ง€ ๊ธฐ๋‹ค๋ฆฌ๊ธฐ ๋•Œ๋ฌธ์— ์•ฑ ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ๋Ÿ‰๊ณผ๋Š” ์ƒ๊ด€์—†๋‹ค.

 

 

+ ํŠน๋ณ„ํ•œ ์ด์œ ๊ฐ€ ์—†๋‹ค๋ฉด @BatchSize๋กœ ํ•˜๋‚˜ํ•˜๋‚˜ ์„ค์ •ํ•˜๊ธฐ ๋ณด๋‹ค๋Š”, ์•„๋ž˜์ฒ˜๋Ÿผ ์ „์—ญ์ ์œผ๋กœ ์„ค์ • ํ•ด๋†“๋Š” ๊ฑธ ์ถ”์ฒœํ•œ๋‹ค.

spring: # resource/application.yml
  jpa:
    hibernate:
      ddl-auto: create
    
    properties:
      hibernate:
        format_sql: true
        default_batch_fetch_size: 1000 # ๊ธฐ๋ณธ ๋ฐฐ์น˜์‚ฌ์ด์ฆˆ (limit 1000)

 

 

๐Ÿ“‘ ํŠธ๋ ˆ์ด๋“œ ์˜คํ”„ [ ์ปฌ๋ ‰์…˜ ํŒจ์น˜์กฐ์ธ vs @BatchSize ]

๊ทธ๋ƒฅ @BatchSize๋ฅผ ์ „์—ญ์ ์œผ๋กœ ์‚ฌ์šฉํ•˜๋Š”๊ฑธ ๊ถŒ์žฅํ•˜์ง€๋งŒ, ์„ฑ๋Šฅ ํŠœ๋‹์ด ํ•„์š”ํ•œ ์‹œ์ ์—๋Š” ์ปฌ๋ ‰์…˜ ํŒจ์น˜์กฐ์ธ๋„ ์ƒ๊ฐํ•ด์•ผํ•œ๋‹ค.

 

๋ฌผ๋ก  ํŽ˜์ด์ง•์—์„  ์‚ฌ์šฉ ๋ถˆ๊ฐ€๋Šฅํ•˜์ง€๋งŒ, ๊ทธ๊ฒŒ ์•„๋‹ˆ๋ผ๋ฉด ์ปฌ๋ ‰์…˜ ํŒจ์น˜์กฐ์ธ์ด ํ•œ๋ฐฉ ์ฟผ๋ฆฌ๊ฐ€ ๋‚˜์˜ค๊ธฐ์— ์„ฑ๋Šฅ์ด ๋” ์ข‹์„ ์ˆ˜ ์žˆ๋‹ค.

๊ฒฐ๊ตญ  [DB ๋„คํŠธ์›Œํฌ ํ˜ธ์ถœ ํšŸ์ˆ˜]์™€ [์ „์†ก ๋ฐ์ดํ„ฐ๋Ÿ‰]์˜ ํŠธ๋ ˆ์ด๋“œ ์˜คํ”„๋ฅผ ๊ณ ๋ คํ•ด์•ผํ•œ๋‹ค.

 

์ปฌ๋ ‰์…˜์ด ๋”ฑ ํ•˜๋‚˜์ด๊ณ  ๋ฐ์ดํ„ฐ๊ฐ€ ๋ช‡ ๊ฐœ ์•ˆ๋œ๋‹ค๋ฉด ํŒจ์น˜์กฐ์ธ ํ•œ๋ฐฉ์ฟผ๋ฆฌ๋กœ ๊ฐ€์ ธ์˜ค๋Š”๊ฒŒ ์„ฑ๋Šฅ์ด ์ข‹๋‹ค.

ํ•˜์ง€๋งŒ ๋ฐ์ดํ„ฐ๊ฐ€ 1000๊ฑด์ด ๋„˜๋Š”๋‹ค๋ฉด, ๊ธด ํ•œ๋ฐฉ ์ฟผ๋ฆฌ๋ณด๋‹ค ๋ช‡๋ฒˆ ๋” ํ˜ธ์ถœํ•˜๋”๋ผ๋„ @BatchSize ๋ฅผ ์“ฐ๋Š”๊ฒŒ ์„ฑ๋Šฅ์ด ์ข‹๋‹ค.

 

 

๐Ÿ“‘ ๋‹ค๋ฅธ๋ฐฉ๋ฒ• - ๋งŒ์•ฝ ์—ฐ๊ด€ ์ปฌ๋ ‰์…˜์„ DTO๋กœ ์ง์ ‘ ์กฐํšŒํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด

Entity๋ฅผ ๋งŒ๋“ค์ง€ ์•Š๊ณ  DTO๋กœ ๋ฐ”๋กœ ์กฐํšŒํ•˜๋Š” ๊ฒฝ์šฐ @BatchSize๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๋Š”๋ฐ, ์ด๋•Œ๋Š” ์–ด๋–ป๊ฒŒ ํ•ด์•ผํ• ๊นŒ?

 List<OrderDto> ์•ˆ์— List<OrderItemDto> ๊ฐ€ ์กด์žฌํ•œ๋‹ค.

1. ToOne ๊ด€๊ณ„๋ฅผ ๋จผ์ € ์กฐํšŒํ•œ๋‹ค. OrderDto์—์„œ ์ปฌ๋ ‰์…˜ ๋ถ€๋ถ„์€ ์ผ๋‹จ ๋น„์›Œ๋‘”๋‹ค.

2. ๋ฐ˜๋ณต๋ฌธ์œผ๋กœ ToMany ๊ด€๊ณ„๋ฅผ ๋ณ„๋„๋กœ ์กฐํšŒํ•ด์„œ ํ•˜๋‚˜์”ฉ ๋ผ์›Œ๋„ฃ๋Š”๋‹ค. (๋ฐ˜๋ณต ํšŸ์ˆ˜๋งŒํผ ์ฟผ๋ฆฌ ๋ฐœ์ƒ, ์‚ฌ์‹ค์ƒ 1+N ๋ฌธ์ œ์™€ ๋™์ผ)

public List<OrderQueryDto> findOrderQueryDtos() {
    //๋ฃจํŠธ ์กฐํšŒ(toOne ์ฝ”๋“œ๋ฅผ ๋ชจ๋‘ ํ•œ๋ฒˆ์— ์กฐํšŒ)
    List<OrderQueryDto> result = findOrders();

    //๋ฃจํ”„๋ฅผ ๋Œ๋ฉด์„œ ์ปฌ๋ ‰์…˜ ์ถ”๊ฐ€(์ถ”๊ฐ€ ์ฟผ๋ฆฌ ์‹คํ–‰)
    result.forEach(o -> {
        List<OrderItemQueryDto> orderItems = findOrderItems(o.getOrderId());
        o.setOrderItems(orderItems);
    });
    return result;
}
private List<OrderQueryDto> findOrders() {
    return em.createQuery(
            "select new package.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();
}

private List<OrderItemQueryDto> findOrderItems(Long orderId) {
    return em.createQuery(
            "select new package.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();
}

 

 

์ด๋Ÿฐ ๊ฒฝ์šฐ์—๋Š” ์ฟผ๋ฆฌ๋ฅผ ๋ฐ˜๋ณตํ•˜์ง€๋ง๊ณ , List<OrderId>๋ฅผ ๋งŒ๋“ค์–ด์„œ Where in ํ•œ๋ฐฉ์ฟผ๋ฆฌ๋กœ ์กฐํšŒํ•˜์ž.

๊ทธ๋ฆฌ๊ณ  OrderDto์— OrderItemDto๋ฅผ ํ•˜๋‚˜์”ฉ ๋งคํ•‘ํ•ด์ฃผ๋ฉด ๋˜๋Š”๋ฐ, ์ด๋•Œ Map ์ž๋ฃŒํ˜•์„ ์ด์šฉํ•˜๋ฉด ๊ฐ„๋‹จํ•˜๊ฒŒ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ค.

public List<OrderQueryDto> findAllByDto_optimization() {

    //๋ฃจํŠธ ์กฐํšŒ(toOne ์ฝ”๋“œ๋ฅผ ๋ชจ๋‘ ํ•œ๋ฒˆ์— ์กฐํšŒ)
    List<OrderQueryDto> result = findOrders();

    //orderItem ์ปฌ๋ ‰์…˜์„ MAP ํ•œ๋ฐฉ์— ์กฐํšŒ
    Map<Long, List<OrderItemQueryDto>> orderItemMap = findOrderItemMap(toOrderIds(result));

    //๋ฃจํ”„๋ฅผ ๋Œ๋ฉด์„œ ์ปฌ๋ ‰์…˜ ์ถ”๊ฐ€(์ถ”๊ฐ€ ์ฟผ๋ฆฌ ์‹คํ–‰X)
    result.forEach(o -> o.setOrderItems(orderItemMap.get(o.getOrderId())));

    return result;
}
private Map<Long, List<OrderItemQueryDto>> findOrderItemMap(List<Long> orderIds) {
    List<OrderItemQueryDto> orderItems = em.createQuery(
            "select new package.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();

    return orderItems.stream() // ์กฐํšŒํ•œ List<OrderItemDto> ๋ฅผ  Map<id, OrderItemDto> ๋กœ ๋ณ€ํ™˜
            .collect(Collectors.groupingBy(orderItemQueryDto -> orderItemQueryDto.getOrderId()));
}

 

 

๐Ÿ’ญ OSIV (Open Session In View) ๋„๊ธฐ

์—ฌ๋‹ด์œผ๋กœ JPA์—์„œ๋Š” Open EntityManager In View๋ผ๊ณ  ๋ถ€๋ฅธ๋‹ค.

๊ทผ๋ฐ ๊ธฐ์กด์˜ ํ•˜์ด๋ฒ„๋„ค์ดํŠธ์—์„œ Open Session In View๋ผ๊ณ  ์‚ฌ์šฉํ–ˆ๊ธฐ์—, OSIV๋ผ๊ณ  ๋งŽ์ด ์‚ฌ์šฉํ•œ๋‹ค.

 

์ด๋ฅผ ์Šคํ”„๋ง ์ง„ํ˜•์—์„œ ์ธ์ง€ํ–ˆ๋Š”์ง€, ์„ค์ •๊ฐ’์€ open-in-view๋ผ๊ณ  ์ด๋ฆ„์„ ์ง€์—ˆ๋‹ค.

spring.jpa.open-in-view : true // ๊ธฐ๋ณธ๊ฐ’

 

๐Ÿ“‘ OSIV๋ฅผ ์™œ ์‚ฌ์šฉํ•˜๋Š”๊ฑฐ์ฃ ?

JPA ์˜ ์ง€์—ฐ๋กœ๋”ฉ์€ ํŠธ๋žœ์žญ์…˜ ๋ฒ”์œ„๋ฅผ ๋ฒ—์–ด๋‚˜๋ฉด (View์— ์ „๋‹ฌ๋œ ์ดํ›„์—๋Š”) ์ฟผ๋ฆฌ ํ˜ธ์ถœ์ด ๋ถˆ๊ฐ€๋Šฅํ•˜๋‹ค.

๊ทธ๋ž˜์„œ ํŠธ๋žœ์žญ์…˜์ด ๋๋‚˜๊ธฐ์ „, ์ง€์—ฐ๋กœ๋”ฉ๋œ ๊ฐ’๋“ค์ด ํ•„์š”ํ•˜๋‹ค๋ฉด ๋ฏธ๋ฆฌ ํ˜ธ์ถœํ•ด๋‘ฌ์•ผํ•œ๋‹ค.

 

ํ•˜์ง€๋งŒ View์—์„œ ๊ฐ’์ด ํ•„์š”์žˆ๋Š”์ง€ ์—†๋Š”์ง€๋Š” ๋ฏธ๋ฆฌ ์•Œ๊ธฐ ์–ด๋ ต๋‹ค. ๊ทธ๋ž˜์„œ OSIV๋ผ๋Š” ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•œ๋‹ค.

 

OSIV๋Š” ์‰ฝ๊ฒŒ๋งํ•ด ํŠธ๋žœ์žญ์…˜์˜ ์‹œ์ž‘(DB ์ปค๋„ฅ์…˜์ด ์ƒ์„ฑ)๋ถ€ํ„ฐ API ์‘๋‹ต์ด ๋๋‚  ๋•Œ ๊นŒ์ง€ ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์™€ DB ์ปค๋„ฅ์…˜์„ ๋‹ซ์ง€์•Š๊ณ  ์œ ์ง€์‹œ์ผœ์ฃผ๋Š” ๊ธฐ๋Šฅ์ด๋‹ค.

 

๐Ÿ™„ ์–ด ๊ทธ๋Ÿผ ์ข‹์€๊ฑฐ ์•„๋‹Œ๊ฐ€์š”? ์„ฑ๋Šฅ์— ๋ฌธ์ œ๊ฐ€ ์ƒ๊ธธ๋ ค๋‚˜?

๊ทธ๋ ‡๋‹ค. ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ปค๋„ฅ์…˜์„ ๋„ˆ๋ฌด ์˜ค๋žœ์‹œ๊ฐ„ ๋™์•ˆ ์‚ฌ์šฉํ•œ๋‹ค.

๋งŒ์•ฝ ์ปจํŠธ๋กค๋Ÿฌ์—์„œ ์™ธ๋ถ€ API๋ฅผ ํ˜ธ์ถœํ•œ๋‹ค๋ฉด, ๊ทธ API์˜ ๋Œ€๊ธฐ์‹œ๊ฐ„๋งŒํผ ์ปค๋„ฅ์…˜์ด ์œ ์ง€๊ฐ€ ๋˜์–ด์žˆ๋‹ค.

 

์ด๋Š” ์„ฑ๋Šฅ์ƒ์— ๋ฌธ์ œ๋„ ์žˆ๊ฒ ์ง€๋งŒ, ๊ฐœ๋ฐœ์ž๊ฐ€ ์ฟผ๋ฆฌ๊ฐ€ ์‹คํ–‰๋˜๋Š” ์‹œ์ ์„ ์˜ˆ์ƒํ•˜๊ธฐ ์–ด๋ ต๋‹ค.

๊ทธ๋ž˜์„œ ๋˜๋„๋ก์ด๋ฉด OSIV๋ฅผ ๋„๋Š”๊ฑธ ์ถ”์ฒœํ•œ๋‹ค. ํŠนํžˆ ์š”์ฆ˜ ์œ ํ–‰ํ•˜๋Š” SPA - Rest API ๋ฐฉ์‹์ด๋ฉด ๋”ฑํžˆ ํ•„์š”์—†๊ธฐ๋„ ํ•˜๋‹ค.

(์ฐธ๊ณ ๋กœ ์ปค๋„ฅ์…˜์ด ์—†๋Š”๋ฐ ์ง€์—ฐ๋กœ๋”ฉ์„ ์‹œ๋„ํ•˜๋ฉด LazyInitializationException ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.)

 

OSIV๊ฐ€ ์—†์–ด๋„ DTO, Application ๊ณ„์ธต (Facade), ์ปค๋งจ๋“œ์™€ ์ฟผ๋ฆฌ๋ถ„๋ฆฌ (CQS)๋“ฑ์œผ๋กœ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋‹ค.

๋ฌผ๋ก  ์–ด๋“œ๋ฏผ ํŽ˜์ด์ง€์ฒ˜๋Ÿผ ๋”ฑํžˆ ์ปค๋„ฅ์…˜ ๊ด€๋ฆฌ๊ฐ€ ์ค‘์š”ํ•˜์ง€์•Š๋‹ค๋ฉด, OSIV๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์ฝ”๋“œ๋ฅผ ๊น”๋”ํ•˜๊ฒŒ ๋‘๋Š”๊ฒŒ ๋‚˜์„ ์ˆ˜ ์žˆ๋‹ค.

๋ธ”๋กœ๊ทธ์˜ ์ •๋ณด

JiwonDev

JiwonDev

ํ™œ๋™ํ•˜๊ธฐ