Posts Spring JPA 3
Post
Cancel

Spring JPA 3

Spring JPA 3


๐Ÿ’ฟ Fetch Join & EAGER & LAZY

์ฆ‰์‹œ ๋กœ๋”ฉ(EARGR๋กœ ์„ค์ •)

  1. ๋ฉค๋ฒ„ ์ „์ฒด๋ฅผ ์กฐํšŒํ•˜๊ธฐ ์œ„ํ•ด JPQL ์‹คํ–‰ select m from member m
  2. JPQL์€ EAGER์™€ ๋ฌด๊ด€ํ•˜๊ฒŒ SQL๋กœ ๊ทธ๋Œ€๋กœ ๋ฒˆ์—ญ -> select m.* from member
  3. JPQL ๊ฒฐ๊ณผ๊ฐ€ member๋งŒ ์กฐํšŒํ•˜๊ณ , team์€ ์กฐํšŒํ•˜์ง€ ์•Š์Œ
  4. member์™€ team์ด ์ฆ‰์‹œ ๋กœ๋”ฉ์œผ๋กœ ์„ค์ •๋˜์–ด ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ์—ฐ๊ด€๋œ ํŒ€์„ ๊ฐ๊ฐ ์ฟผ๋ฆฌ๋ฅผ ๋‚ ๋ ค์„œ ์ถ”๊ฐ€ ์กฐํšŒ (N+1)

์ง€์—ฐ ๋กœ๋”ฉ(LAZY๋กœ ์„ค์ •)

  1. ๋ฉค๋ฒ„ ์ „์ฒด๋ฅผ ์กฐํšŒํ•˜๊ธฐ ์œ„ํ•ด JPQL ์‹คํ–‰ select m from member m
  2. JPQL์€ EAGER์™€ ๋ฌด๊ด€ํ•˜๊ฒŒ SQL๋กœ ๊ทธ๋Œ€๋กœ ๋ฒˆ์—ญ -> select m.* from member
  3. JPQL ๊ฒฐ๊ณผ๊ฐ€ member๋งŒ ์กฐํšŒํ•˜๊ณ , team์€ ์กฐํšŒํ•˜์ง€ ์•Š์Œ
  4. member์™€ team์ด ์ง€์—ฐ ๋กœ๋”ฉ์œผ๋กœ ์„ค์ •๋˜์–ด ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ๊ฐ€์งœ ํ”„๋ก์‹œ ๊ฐ์ฒด๋ฅผ ๋„ฃ์–ด๋‘๊ณ , ์‹ค์ œ ํšŒ์›์€ ํŒ€์€ ์กฐํšŒํ•˜์ง€ ์•Š์Œ
  5. ์‹ค์ œ team์„ ์‚ฌ์šฉํ•˜๋Š” ์‹œ์ ์— ์ฟผ๋ฆฌ๋ฅผ ๋‚ ๋ ค์„œ ๊ฐ๊ฐ ์กฐํšŒ(N+1)

fetch join ๋˜๋Š” ์—”ํ‹ฐํ‹ฐ ๊ทธ๋ž˜ํ”„(EAGER, LAZY ์ƒ๊ด€ ์—†์Œ)

  1. ๋ฉค๋ฒ„์™€ ํŒ€์„ ํ•œ๋ฒˆ์— ์กฐํšŒํ•˜๊ธฐ ์œ„ํ•ด JPQL+fetch join ์‹คํ–‰ select m from member m join fetch m.team
  2. JPQL์—์„œ fetch join์„ ์‚ฌ์šฉํ–ˆ์œผ๋ฏ€๋กœ SQL์€ ๋ฉค๋ฒ„์™€ ํŒ€์„ ํ•œ ์ฟผ๋ฆฌ๋กœ ์กฐํšŒ -> select m., t. from member join team โ€ฆ
  3. JPQL ๊ฒฐ๊ณผ๊ฐ€ member์™€ team์„ ํ•œ๊บผ๋ฒˆ์— ์กฐํšŒํ•จ
  4. member์™€ team์ด fetch join์œผ๋กœ ํ•œ๋ฒˆ์— ์กฐํšŒ๋˜์—ˆ์œผ๋ฏ€๋กœ N+1 ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•˜์ง€ ์•Š์Œ

๐Ÿ’ฟ Fetch Join ๊ณผ ๋ณ„์นญ

JPA ํ‘œ์ค€ ์ŠคํŽ™์—๋Š” fetch join ๋Œ€์ƒ์— ๋ณ„์นญ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๋‹ค.

  • ํ•˜์ง€๋งŒ Hibernate๋Š” ํ—ˆ์šฉ
  • ์ฃผ์˜์‚ฌํ•ญ์ด ์žˆ์Œ

์ฃผ์˜ ์‚ฌํ•ญ

  1. fetch join JPQL์— on ์กฐ๊ฑด์„ ๋‹ฌ๋ฉด ๋ฌด์กฐ๊ฑด ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.(with-clause not allowed on fetch association)
  2. Select t from Team t join fetch t.member m on m.name=:memberName
    • member ์ปฌ๋ ‰์…˜์ด ์ „๋ถ€ ์กฐํšŒ๋˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ์‹คํ–‰ ๋ถˆ๊ฐ€
    • ๊ทธ๋Ÿฌ๋ฉด Select t from Team t join fetch t.member m on t.name=:teamName
    • ์œ„์˜ ๊ฒƒ์€ Member ์ปฌ๋ ‰์…˜์— ์˜ํ–ฅ์„ ์ฃผ์ง€ ์•Š๋Š”๋ฐ ์™œ ์•ˆ๋ ๊นŒ?
  3. ์กฐ์ธ ๊ณผ๋Š” ๋ฌด๊ด€ํ•˜๊ฒŒ Team ์ž์ฒด์˜ ๋ฐ์ดํ„ฐ๋ฅผ ํ•„ํ„ฐ๋ง ํ•˜๋Š” ๊ฒƒ์ด๊ธฐ ๋•Œ๋ฌธ์— on ์— ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์€ ์˜๋„์— ๋งž์ง€ ์•Š๋‹ค. where์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ๋งž๋‹ค.
  4. select t from Team t join fetch t.members m where m.username = โ€˜m1โ€™
    • where์„ ์‚ฌ์šฉํ•˜๋”๋ผ๋„ ํŒจ์น˜ ์กฐ์ธ ๋Œ€์ƒ์—๋Š” where์„ ์‚ฌ์šฉํ•˜๋ฉด ์•ˆ๋œ๋‹ค.
    • ์‹คํ–‰ ๊ฒฐ๊ณผ team1 = teamA member = m1(member๊ฐ€ ํ•˜๋‚˜๋งŒ ์กด์žฌ)

๋”ฐ๋ผ์„œ fetch join์˜ ๊ฒฐ๊ณผ๋Š” ์—ฐ๊ด€๋œ ๋ชจ๋“  Entity๊ฐ€ ์žˆ์„๊ฑฐ๋ผ๊ณ  ๊ฐ€์ •ํ•˜๊ณ  ์‚ฌ์šฉํ•ด์•ผ ํ•จ ์ด๋ ‡๊ฒŒ fetch join์— ๋ณ„์นญ์„ ์ž˜๋ชป ์‚ฌ์šฉํ•ด์„œ ๊ฒฐ๊ณผ๋ฅผ ํ•„ํ„ฐ๋ง ํ•˜๋ฉด ๊ฐ์ฒด์˜ ์ƒํƒœ์™€ DB์˜ ์ƒํƒœ ์ผ๊ด€์„ฑ์ด ๊นจ์ง

๊ฒฐ๋ก : fetch join์˜ ๋Œ€์ƒ์€ on, where ๋“ฑ์—์„œ ํ•„ํ„ฐ๋ง ์กฐ๊ฑด์œผ๋กœ ์‚ฌ์šฉํ•˜๋ฉด ์•ˆ๋œ๋‹ค.


๐Ÿ’ฟ Hibernate ํ—ˆ์šฉ

์ผ๊ด€์„ฑ์ด ๊นจ์ง€์ง€ ์•Š๋Š” ๋ฒ”์œ„ ๋‚ด์—์„œ ํ—ˆ์šฉ

  • ex) Select m from Member m join fetch m.team t where t.name=:teamName
    • ์กฐํšŒ๋œ ํšŒ์›์€ db์™€ ๋™์ผํ•œ ์ผ๊ด€์„ฑ์„ ์œ ์ง€ํ•œ ํŒ€์˜ ๊ฒฐ๊ณผ๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.
  • ํ•˜์ง€๋งŒ ์ด ์ฟผ๋ฆฌ๋ฅผ left join fetch๋กœ ๋ณ€๊ฒฝํ•˜๋ฉด ์ผ๊ด€์„ฑ์ด ๊นจ์งˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๐Ÿ’ฟ Fetch Join์˜ ํ•œ๊ณ„

  1. Fetch Join ๋Œ€์ƒ์—๋Š” ๋ณ„์นญ์„ ์ค„ ์ˆ˜ ์—†๋‹ค.
    • Hibernate ๋Š” ๊ฐ€๋Šฅ, ๊ฐ€๊ธ‰์  ์‚ฌ์šฉX(fetch join์„ ์—ฌ๋Ÿฌ๋ฒˆ ํ•ด์•ผ ํ• ๋•Œ ๊ฐ€๋” ์‚ฌ์šฉ)
  2. ๋‘˜ ์ด์ƒ์˜ ์ปฌ๋ ‰์…˜์€ ํŽ˜์น˜ ์กฐ์ธ ํ•  ์ˆ˜ ์—†๋‹ค.
  3. ์ผˆ๋ ‰์…˜์„ Fetch Joinํ•˜๋ฉด ํŽ˜์ด์ง• API(setFirstResult, setMaxResults)๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๋‹ค.
    • 1:1, N:1 ๊ฐ™์€ ๋‹จ์ผ ๊ฐ’ ์—ฐ๊ด€ ํ•„๋“œ๋“ค์€ ํŽ˜์น˜ ์กฐ์ธํ•ด๋„ Paging ๊ฐ€๋Šฅ
    • Hibernate๋Š” ๊ฒฝ๊ณ  ๋กœ๊ทธ๋ฅผ ๋‚จ๊ธฐ๊ณ  ๋ฉ”๋ชจ๋ฆฌ์—์„œ ํŽ˜์ด์ง•(์œ„ํ—˜)
    • ์˜ˆ๋ฅผ ๋“ค์–ด ํŽ˜์ด์ง€๊ฐ€ 1์ด๋ฉด ๋‹ค๋Œ€์ผ์—์„œ ์ปฌ๋ ‰์…˜์„ ์กฐํšŒํ•˜๋ฉด ํ•˜๋‚˜์— ํ•˜๋‚˜๋งŒ ๋งค์น˜๋˜์–ด DB์™€ ์ผ์น˜๋˜์ง€ ์•Š๋Š” ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒ
    • ํŽ˜์ด์ง• ๋Œ€์‹  ์ปฌ๋ ‰์…˜์— @BatchSize๋ฅผ ์„ค์ •ํ•ด์„œ limit์„ ์„ค์ •ํ•ด ์ค€๋‹ค. ํ˜น์€ persistence.xml์—์„œ ๊ธ€๋กœ๋ฒŒ ํ•˜๊ฒŒ ์„ค์ •(ํ•œ๋ฒˆ์— N+1 ๋ฌธ์ œ์—์„œ limit์„ ์ •ํ•ด์ค„ ์ˆ˜ ์žˆ๋‹ค.)
    • Dto๋กœ ๋ฝ‘์•„๋„ ๋œ๋‹ค.
  4. ๋‹ค๋Œ€์ผ์—์„œ ์ปฌ๋ ‰์…˜์— ๋“ค์–ด์˜ค๋Š” ๊ฒƒ์€ ํ•˜๋‚˜์˜ ๊ฐ์ฒด์— ์—ฌ๋Ÿฌ๊ฐœ์˜ Entity๊ฐ€ ๋“ค์–ด์˜ค๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ ๊ฐ Entity์— ์—ฐ๊ด€๋œ Entity๊ฐ€ ์ „๋ถ€ ํ•˜๋‚˜์”ฉ ๋Œ€์‘๋˜์–ด ๋“ค์–ด์˜จ๋‹ค.

__

๐Ÿ’ฟ Fetch Join์˜ ์ตœ์ ํ™”

image

Entity ์ง์ ‘ ๋…ธ์ถœ

  • ์ ˆ๋Œ€ Entity๋ฅผ ์ง์ ‘ ๋…ธ์ถœํ•ด์„œ๋Š” ์•ˆ๋œ๋‹ค.
1
2
3
4
5
   @GetMapping("/api/v1/simple-orders")
   public List<Order> orderV1() {
      List<Order> all = orderRepository.findAllByString(new OrderSearch());
      return all;
   }

์ฃผ์˜

  • hibernatemodule์„ Bean์— ๋“ฑ๋กํ•ด์•ผํ•จ(์ง€์—ฐ ๋กœ๋”ฉ ๋ฌธ์ œ๋กœ ๋ฌดํ•œ query์— ๊ฐ‡ํž˜)
    • ์ด๋Ÿฌ๋ฉด LAZY ๋ฌด์‹œํ•จ
  • ํ˜น์€ ์–‘๋ฐฉํ–ฅ์ด ๊ฑธ๋ฆฐ ๊ณณ์— @JsonIgnore์„ ์„ค์ •ํ•ด ์ฃผ์–ด์•ผ ํ•จ
  • ์ฆ‰์‹œ๋กœ๋”ฉ(EAGER)์“ฐ๋ฉด ๋˜๋Š”๋ฐ ์“ฐ์ง€ ๋ง์ž
  • ๊ทธ๋ƒฅ ์“ฐ์ง€ ๋ง์ž

Dto ๋ณ€ํ™˜

1
2
3
4
5
6
7
   @GetMapping("/api/v2/simple-orders")
   public List<SimpleOrderDto> orderV2() {
      List<Order> orders = orderRepository.findAllByString(new OrderSearch());
      return orders.stream()
               .map(SimpleOrderDto::new)
               .collect(Collectors.toList());
   }

์ฃผ์˜

  • ์•„์ง n+1 ๋ฌธ์ œ๊ฐ€ ๋‚จ์•„์žˆ์Œ(์ฒ˜์Œ์— ํ•˜๋‚˜ ๋ณด๋‚ด๋ฉด members ์–ป์œผ๋ ค๊ณ  2๋ฒˆ ๊ทธ๋ฆฌ๊ณ  delivery ์–ป์œผ๋ ค๊ณ  2๋ฒˆ ํ•ด์„œ ์ด 5๋ฒˆ ๋‚˜๊ฐ)
  • DTO ๋งŒ๋“ค๋•Œ ํ•ญ์ƒ ์•ˆ์— ์žˆ๋Š” ์—ฐ๊ด€ ๊ด€๊ณ„๊ฐ€ ์žˆ๋Š” Entity๋“ค๋„ ๊ทธ๋Œ€๋กœ ๋…ธ์ถœํ•˜์ง€ ๋ง๊ณ  DTO๋กœ ๋งŒ๋“ค์–ด์„œ ๋…ธ์ถœ์‹œ์ผœ๋ผ

fetch join

  • ๊ฐ€์ ธ์˜ฌ๋•Œ ์ „๋ถ€ DB์—์„œ ๋ถ™ํ˜€์„œ ๊ฐ€์ ธ์˜ด
1
2
3
4
5
6
   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();
   }

์ฃผ์˜

  • ์ผ๋Œ€๋‹ค์ผ ๊ฒฝ์šฐ ๋ฐ์ดํ„ฐ๊ฐ€ ๋‹ค ๊ธฐ์ค€์œผ๋กœ ๋Š˜์–ด๋‚จ(DB๋Š” ๊ทธ๋ƒฅ left join ์‹œ์ผœ๋ฒ„๋ฆฌ๊ธฐ ๋•Œ๋ฌธ์— record๊ฐ€ ๋Š˜์–ด๋‚  ์ˆ˜ ๋ฐ–์— ์—†์Œ)
    • distinct๋ฅผ ์“ฐ๋ฉด ๋œ๋‹ค(JPA๊ฐ€ reference ๊ฐ’์ด ๊ฐ™์€ ๊ฐ์ฒด๋Š” List์— ์ถ”๊ฐ€ํ•˜์ง€ ์•Š์Œ)
    • SQL์— distinct๋ฅผ ์ถ”๊ฐ€ํ•˜๊ณ  ๋”ํ•ด์„œ ๊ฐ™์€ ์—”ํ‹ฐํ‹ฐ(ref๊ฐ’์ด ๊ฐ™์€ JPA๋Š” id๊ฐ€ ๊ฐ™์œผ๋ฉด ref๊ฐ€ ๊ฐ™์Œ)๊ฐ€ ์กฐํšŒ๋˜๋ฉด ์ค‘๋ณต์„ ๊ฑธ๋Ÿฌ์คŒ
    • ๊ทผ๋ฐ ์ด๊ฑฐ ์“ฐ๋ฉด paging ๋ถˆ๊ฐ€

ํŽ˜์ด์ง• ๋ถˆ๊ฐ€

  • ์ผˆ๋ ‰์…˜์„ ํŽ˜์น˜ ์กฐ์ธํ•˜๋ฉด ์ผ๋Œ€๋‹ค ์กฐ์ธ์ด ๋ฐœ์ƒํ•˜๋ฏ€๋กœ ๋ฐ์ดํ„ฐ๊ฐ€ ์ฆ๊ฐ€ํ•œ๋‹ค.
  • ์ผ๋Œ€๋‹ค ๊ด€๊ณ„์—์„œ 1์„ ๊ธฐ์ค€์œผ๋กœ ํŽ˜์ด์ง•์„ ํ•˜๋Š” ๊ฒƒ์ด ๋ชฉ์ ์ด๋‹ค. ๊ทธ๋Ÿฐ๋ฐ ๋ฐ์ดํ„ฐ๋Š” ๋‹ค๋ฅผ ๊ธฐ์ค€์œผ๋กœ row๊ฐ€ ์ƒ์„ฑ๋œ๋‹ค.
  • Order๋ฅผ ๊ธฐ์ค€์œผ๋กœ ํŽ˜์ด์ง•์„ ํ•˜๊ณ  ์‹ถ์€๋ฐ, ๋‹ค(N)์ธ OrderItem์„ ์กฐ์ธํ•˜๋ฉด OrderItem์ด ๊ธฐ์ค€์ด ๋˜์–ด๋ฒ„๋ฆฐ๋‹ค.
  • setFirstResult, setMaxResult๋ฅผ ์“ฐ๋ฉด inmemory์—์„œ ํŽ˜์ด์ง•์„ ์ˆ˜ํ–‰
    • warning์„ ๋‚จ๊น€ ์“ฐ์ง€ ๋ง๊ฒƒ

DTO๋กœ ๊ฐ€์ ธ์˜ค๊ธฐ

1
2
3
4
5
6
   public List<SimpleOrderDto> findOrderDtos() {
      return em.createQuery("select new com.example.JPALecture2.domain.dto.SimpleOrderDto(o.id, m.name, o.orderDate, o.status, d.address)" +
               " from Order o " +
               " join o.member m" +
               " join o.delivery d", SimpleOrderDto.class
      ).getResultList();

์ฃผ์˜

  • fetch join๊ณผ ๊ธฐ๋ณธ์ ์œผ๋กœ ์„ฑ๋Šฅ์€ ๊ฐ™์œผ๋‚˜ DTO์— ๋”ฑ๋งž๊ฒŒ Query๊ฐ€ ์ž‘์„ฑ๋˜์–ด ์žฌ์‚ฌ์šฉ ๋ถˆ๊ฐ€

ํŽ˜์ด์ง• ํ•œ๊ณ„ ํ•ด์†Œ

  • ๋จผ์ € ToOne(OneToOne, ManyToOne) ๊ด€๊ณ„๋Š” ๋ชจ๋‘ fetch join ํ•œ๋‹ค.
    • ToOne ๊ด€๊ณ„๋Š” row๋ฅผ ์ฆ๊ฐ€์‹œํ‚ค์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.
  • ๋‚จ์€ ์ปฌ๋ ‰์…˜์€ ์ง€์—ฐ๋กœ๋”ฉ์œผ๋กœ ์กฐํšŒํ•œ๋‹ค.
  • ์ง€์—ฐ ๋กœ๋”ฉ ์„ฑ๋Šฅ ์ตœ์ ํ™”๋ฅผ ์œ„ํ•ด batch_fetch_size ํ˜น์€ @BatchSize๋ฅผ ์ ์šฉํ•œ๋‹ค.
    • hibernate.default_batch_fetch_size: ๊ธ€๋กœ๋ฒŒ ์„ค์ •
    • @BatchSize: ๊ฐœ๋ณ„ ์ตœ์ ํ™”
    • ์ด ์˜ต์…˜์„ ์‚ฌ์šฉํ•˜๋ฉด ๊ด€๋ จ๋œ ์ปฌ๋ ‰์…˜์ด๋‚˜ ํ”„๋ก์‹œ ๊ฐ์ฒด๋ฅผ ํ•œ๋ฒˆ์— ์„ค์ •ํ•œ size ๋งŒํผ in ์ฟผ๋ฆฌ๋กœ ๋ฏธ๋ฆฌ ๊ฐ€์ ธ์˜ด
This post is licensed under CC BY 4.0 by the author.

Functional programming1

Spring QueryDsl