๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
Spring/Spring Data JPA

Spring Data JPA ์ฆ‰์‹œ ๋กœ๋”ฉ(Eager Loading) & ์ง€์—ฐ ๋กœ๋”ฉ(Lazy Loading)

by ์ฃผ๋ฐœ2 2021. 6. 17.
๋ฐ˜์‘ํ˜•

๐Ÿ“Ž JPA - @ManyToOne ์ฆ‰์‹œ ๋กœ๋”ฉ๊ณผ ์ง€์—ฐ ๋กœ๋”ฉ(Eager Loading / Lazy Loading)

 

Spring Data JPA์—์„œ @ManyToOne(N:1)์œผ๋กœ ์—ฐ๊ด€๊ด€๊ณ„๊ฐ€ ์„ค์ •๋˜์–ด ์žˆ๋Š” 2๊ฐœ์˜ Entity๊ฐ€ ์กด์žฌํ•  ๋•Œ, 

 

๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์˜ ์ž…์žฅ์—์„œ ๋ณด๋ฉด join์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

 

์‹ค์ œ @ManyToOne์˜ ๊ฒฝ์šฐ FK์ชฝ์˜ ์—”ํ‹ฐํ‹ฐ๋ฅผ ๊ฐ€์ ธ์˜ฌ ๋•Œ PK์ชฝ์˜ ์—”ํ‹ฐํ‹ฐ๋„ ๊ฐ™์ด ๊ฐ€์ ธ์˜ค๊ฒŒ ๋˜๋Š”๋ฐ์š”, ์ด๋Ÿฌํ•œ ๊ณผ์ •์ด ๊ผญ ํ•„์š”ํ•œ๊ฑด์ง€, ํ•„์š”ํ•˜์ง€ ์•Š๋‹ค๋ฉด ์–ด๋–ป๊ฒŒ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋Š”์ง€ ์ฆ‰์‹œ ๋กœ๋”ฉ๊ณผ ์ง€์—ฐ ๋กœ๋”ฉ์— ๋Œ€ํ•ด ์˜ˆ์ œ๋ฅผ ํ†ตํ•ด ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

 

 

 

 

โŒจ๏ธ Board, Member ์—”ํ‹ฐํ‹ฐ

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.ToString;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.ManyToOne;

@Entity
@Getter
@ToString(exclude = "member")
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Board extends BaseEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long bno;

    @Column
    private String title;

    @Column
    private String content;

    @ManyToOne
    private Member member;
}



import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.ToString;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;

@Entity
@Getter
@ToString
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Member extends BaseEntity {

    @Id
    private String email;

    @Column
    private String password;

    @Column
    private String name;
}

์œ„ ์ฝ”๋“œ์—์„œ Board(๊ฒŒ์‹œ๊ธ€)์™€ Member(ํšŒ์›)์€ @ManyToOne(N:1)์˜ ๊ด€๊ณ„๋ฅผ ๋งบ๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

์ฆ‰ ํ•œ ๋ช…์˜ ํšŒ์›(1)์€ ์—ฌ๋Ÿฌ ๊ฒŒ์‹œ๊ธ€(N)์„ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

๋”ฐ๋ผ์„œ ๊ฒŒ์‹œ๊ธ€์˜ ๊ธฐ์ค€์œผ๋กœ ๋ดค์„๋•Œ ํšŒ์›๊ณผ์˜ ๊ด€๊ณ„๋Š” N:1์ด ๋ฉ๋‹ˆ๋‹ค.

 

๋งŒ์•ฝ Board์˜ ์—”ํ‹ฐํ‹ฐ๋ฅผ ์กฐํšŒํ•œ๋‹ค๋ฉด Member์˜ ์—”ํ‹ฐํ‹ฐ๋„ ํ•จ๊ป˜ ์กฐํšŒ๊ฐ€ ๋˜๋Š”๋ฐ์š”,

 

    @Test
    @DisplayName("์ฆ‰์‹œ ๋กœ๋”ฉ์€ ์—ฐ๊ด€๊ด€๊ณ„๋ฅผ ๋งบ๊ณ ์žˆ๋Š” ์—”ํ‹ฐํ‹ฐ๋Š” ๋ชจ๋‘ ์กฐํšŒ๊ฐ€ ๋œ๋‹ค")
    void testRead1() {
        /* given */
        Optional<Board> result = boardRepository.findById(100L);

        /* when */
        Board board = result.get();

        /* then */
        System.out.println(board);
    }

์‹คํ–‰๋œ SQL ์ฟผ๋ฆฌ๋ฌธ์„ ํ™•์ธํ•ด๋ณด๋ฉด board ํ…Œ์ด๋ธ” ์™ธ์—๋„ member ํ…Œ์ด๋ธ”๋„ ํ•จ๊ป˜ ์กฐํšŒ๋ฅผ ํ•˜๊ณ  join์œผ๋กœ ์ฒ˜๋ฆฌ๋˜๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

๋งŒ์•ฝ Board๋ฅผ ์กฐํšŒํ•  ๋•Œ Member์„ ํ•จ๊ป˜ ์กฐํšŒํ•˜์ง€ ์•Š์œผ๋ ค๋ฉด ์–ด๋–ป๊ฒŒ ํ•ด์•ผํ• ๊นŒ์š”?

 

 

 

 

โŒจ๏ธ ์ฆ‰์‹œ ๋กœ๋”ฉ(Eager Loading)๊ณผ ์ง€์—ฐ ๋กœ๋”ฉ(Lazy Loading)

์œ„ ์ฟผ๋ฆฌ ๊ฒฐ๊ณผ์™€ ๊ฐ™์ด ํŠน์ • ์—”ํ‹ฐํ‹ฐ๋ฅผ ์กฐํšŒํ•  ๋•Œ ์—ฐ๊ด€๋œ ๋ชจ๋“  ์—”ํ‹ฐํ‹ฐ๋ฅผ ๊ฐ™์ด ๋กœ๋”ฉํ•˜๋Š” ๊ฒƒ์„ ์ฆ‰์‹œ ๋กœ๋”ฉ(Eager Loading)์ด๋ผ๊ณ  ํ•ฉ๋‹ˆ๋‹ค.

์ด์™€ ๊ฐ™์€ ์ฆ‰์‹œ ๋กœ๋”ฉ์€ ์—ฐ๊ด€๋œ ์—”ํ‹ฐํ‹ฐ๋ฅผ ๋ชจ๋‘ ๊ฐ€์ ธ์˜จ๋‹ค๋Š” ์žฅ์ ์ด ์žˆ์ง€๋งŒ,

์‹ค๋ฌด์—์„œ ์—”ํ‹ฐํ‹ฐ๊ฐ„์˜ ๊ด€๊ณ„๊ฐ€ ๋ณต์žกํ•ด์งˆ์ˆ˜๋ก ์กฐ์ธ์œผ๋กœ ์ธํ•œ ์„ฑ๋Šฅ ์ €ํ•˜๋ฅผ ํ”ผํ•  ์ˆ˜ ์—†๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

 

JPA์—์„œ ์—ฐ๊ด€๊ด€๊ณ„์˜ ๋ฐ์ดํ„ฐ๋ฅผ ์–ด๋–ป๊ฒŒ ๊ฐ€์ ธ์˜ฌ ๊ฒƒ์ธ๊ฐ€๋ฅผ fetch(ํŒจ์น˜)๋ผ๊ณ  ํ•˜๋Š”๋ฐ,

์—ฐ๊ด€๊ด€๊ณ„์˜ ์–ด๋…ธํ…Œ์ด์…˜ ์†์„ฑ์œผ๋กœ 'fetch'๋ชจ๋“œ๋ฅผ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค.

 

'์ฆ‰์‹œ ๋กœ๋”ฉ'์€ ๋ถˆํ•„์š”ํ•œ ์กฐ์ธ๊นŒ์ง€ ํฌํ•จํ•ด์„œ ์ฒ˜๋ฆฌํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋งŽ๊ธฐ ๋•Œ๋ฌธ์— '์ง€์—ฐ ๋กœ๋”ฉ'์˜ ์‚ฌ์šฉ์„ ๊ถŒ์žฅํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

(๋ณดํŽธ์ ์œผ๋กœ '์ง€์—ฐ ๋กœ๋”ฉ'์„ ๊ธฐ๋ณธ์œผ๋กœ ์‚ฌ์šฉํ•˜๊ณ , ์ƒํ™ฉ์— ๋งž๊ฒŒ ํ•„์š”ํ•œ ๋ฐฉ๋ฒ•์„ ์ฐพ๋Š”๊ฒƒ์ด ์ข‹์€ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค ๐Ÿ˜ƒ)

 

 

์ง€์—ฐ ๋กœ๋”ฉ(Lazy Loading)์ด๋ž€, ๊ฐ€๋Šฅํ•œ ๊ฐ์ฒด์˜ ์ดˆ๊ธฐํ™”๋ฅผ ์ง€์—ฐ์‹œํ‚ค๋Š”๋ฐ ์‚ฌ์šฉํ•˜๋Š” ํŒจํ„ด์ž…๋‹ˆ๋‹ค.

 

 

๊ฐ ์—ฐ๊ด€๊ด€๊ณ„์˜ default ์†์„ฑ์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

@OneToMany: LAZY

@ManyToOne: EAGER

@ManyToMany: LAZY

@OneToOne: EAGER

https://stackoverflow.com/questions/26601032/default-fetch-type-for-one-to-one-many-to-one-and-one-to-many-in-hibernate

 

 

์ง€์—ฐ ๋กœ๋”ฉ์€ Board ์—”ํ‹ฐํ‹ฐ์˜ @ManyToOne ์–ด๋…ธํ…Œ์ด์…˜์— ์ ์šฉ์„ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.ToString;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.ManyToOne;

@Entity
@Getter
@ToString(exclude = "member")
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Board extends BaseEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long bno;

    @Column
    private String title;

    @Column
    private String content;

    // Lazy loading
    @ManyToOne(fetch = FetchType.LAZY)
    private Member member;
}

...

Board ์—”ํ‹ฐํ‹ฐ์— ์ง€์—ฐ ๋กœ๋”ฉ์„ ์ ์šฉํ•œ ํ›„ SQL ์ฟผ๋ฆฌ๋ฌธ์„ ํ™•์ธํ•ด๋ณด๋ฉด, ์ด์ „๊ณผ๋Š” ๋‹ค๋ฅด๊ฒŒ Board ํ…Œ์ด๋ธ”๋งŒ ์กฐํšŒ๊ฐ€ ๋ฉ๋‹ˆ๋‹ค.

 

 

ํ•˜์ง€๋งŒ ์ง€์—ฐ ๋กœ๋”ฉ์„ ์ ์šฉํ•œ ํ›„์—๋Š” Board์˜ Member์„ ์ ‘๊ทผํ•˜๋ ค๊ณ  ํ•˜๋ฉด ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•˜๋Š”๋ฐ์š”,

    @Transactional
    @Test
    void testRead1() {
        /* given */
        Optional<Board> result = boardRepository.findById(100L);

        /* when */
        Board board = result.get();

        /* then */
        System.out.println(board);
        System.out.println(board.getMember()); // Error
    }

์œ„ ์˜ค๋ฅ˜ ๋‚ด์šฉ์ธ proxy ~ 'no Session'์€ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์™€ ์ถ”๊ฐ€์ ์ธ ์—ฐ๊ฒฐ์ด ํ•„์š”ํ•˜๋‹ค๋Š” ๋ฉ”์‹œ์ง€ ์ž…๋‹ˆ๋‹ค.

 

์ง€์—ฐ ๋กœ๋”ฉ ๋ฐฉ์‹์œผ๋กœ ๋กœ๋”ฉํ•˜๊ธฐ ๋•Œ๋ฌธ์— board ํ…Œ์ด๋ธ”๋งŒ ๊ฐ€์ ธ์˜ค๋Š” ๊ฒƒ์€ ๋ฌธ์ œ๊ฐ€ ์—†์ง€๋งŒ, member ํ…Œ์ด๋ธ”์„ ๊ฐ€์ ธ์˜ฌ ๋•Œ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.

์ด๋Ÿฌํ•œ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์™€์˜ ์žฌ์—ฐ๊ฒฐ์ด ํ•„์š”ํ•œ๋ฐ, @Transactional ์–ด๋…ธํ…Œ์ด์…˜์„ ํ†ตํ•ด ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

@Transactional ์–ด๋…ธํ…Œ์ด์…˜์€ ํ•ด๋‹น ๋ฉ”์„œ๋“œ๋ฅผ ํ•˜๋‚˜์˜ 'ํŠธ๋žœ์žญ์…˜' ์œผ๋กœ ์ฒ˜๋ฆฌํ•˜๋ผ๋Š” ์˜๋ฏธ์ž…๋‹ˆ๋‹ค.

ํŠธ๋žœ์žญ์…˜์œผ๋กœ ์ฒ˜๋ฆฌํ•˜๋ฉด ์†์„ฑ์— ๋”ฐ๋ผ ๋‹ค๋ฅด๊ฒŒ ๋™์ž‘ํ•˜์ง€๋งŒ, ๊ธฐ๋ณธ์ ์œผ๋กœ๋Š” ํ•„์š”ํ•  ๋•Œ ๋‹ค์‹œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์™€์˜ ์—ฐ๊ฒฐ์ด ์ƒ์„ฑ๋˜๊ธฐ ๋•Œ๋ฌธ์— ์œ„ ํ…Œ์ŠคํŠธ๋Š” ์ •์ƒ์ ์œผ๋กœ ์‹คํ–‰์ด ๋ฉ๋‹ˆ๋‹ค.

 

 

 

 

โŒจ๏ธ ์ฆ‰์‹œ ๋กœ๋”ฉ(Eager Loading)๊ณผ ์ง€์—ฐ ๋กœ๋”ฉ(Lazy Loading)์˜ ์žฅ๋‹จ์ 

์ฆ‰์‹œ ๋กœ๋”ฉ(Earge Loading) ์žฅ์ 

  • ์ง€์—ฐ๋œ ์ดˆ๊ธฐํ™”์™€ ๊ด€๋ จํ•ด์„œ ์„ฑ๋Šฅ์ ์ธ ์˜ํ–ฅ์ด ์—†์Œ

์ฆ‰์‹œ ๋กœ๋”ฉ(Earge Loading) ๋‹จ์ 

  • ์ง€์—ฐ ๋กœ๋”ฉ๋ณด๋‹ค ๊ธด ์ดˆ๊ธฐ์˜ ๋กœ๋”ฉ ์‹œ๊ฐ„์ด ํ•„์š”ํ•จ
  • ๋ถˆํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ๋งŽ์ด ๋กœ๋”ฉํ•˜๋ฉด ์„ฑ๋Šฅ์— ์˜ํ–ฅ์„ ์ค„ ์ˆ˜ ์žˆ์Œ

 

์ง€์—ฐ ๋กœ๋”ฉ(Lazy Loading) ์žฅ์ 

  • ๋‹ค๋ฅธ ์ ‘๊ทผ ๋ฐฉ์‹๋ณด๋‹ค ํ›จ์”ฌ ์ ์€ ์ดˆ๊ธฐ์˜ ๋กœ๋”ฉ ์‹œ๊ฐ„
  • ๋‹ค๋ฅธ ์ ‘๊ทผ ๋ฐฉ์‹์— ๋น„ํ•ด ๋ฉ”๋ชจ๋ฆฌ ์†Œ๋น„๋Ÿ‰ ๊ฐ์†Œ

์ง€์—ฐ ๋กœ๋”ฉ(Lazy Loading) ๋‹จ์ 

  • ์ดˆ๊ธฐํ™”๊ฐ€ ์ง€์—ฐ๋˜๋ฉด ์›ํ•˜์ง€ ์•Š๋Š” ์ˆœ๊ฐ„ ์„ฑ๋Šฅ์— ์˜ํ–ฅ์„ ์ค„ ์ˆ˜ ์žˆ์Œ

 

 

 

โŒจ๏ธ ์ฆ‰์‹œ ๋กœ๋”ฉ(Eager Loading)๊ณผ ์ง€์—ฐ ๋กœ๋”ฉ(Lazy Loading)์˜ ์ฃผ์˜ํ•  ์ 

  • ๊ฐ€๊ธ‰์ ์ด๋ฉด ์ง€์—ฐ ๋กœ๋”ฉ(Lazy Loading)๋งŒ ์‚ฌ์šฉ(ํŠนํžˆ ์‹ค๋ฌด์—์„œ)
  • ์ฆ‰์‹œ ๋กœ๋”ฉ(Eager Loading)์„ ์ ์šฉํ•˜๋ฉด ์˜ˆ์ƒํ•˜์ง€ ๋ชปํ•œ SQL์ด ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์Œ
  • ์ฆ‰์‹œ ๋กœ๋”ฉ(Earge Loading)์€ JPQL์—์„œ N+1 ๋ฌธ์ œ๋ฅผ ์ผ์œผํ‚ด

 

 

 

 

References

 

๋ฐ˜์‘ํ˜•

๋Œ“๊ธ€