Back-end/JPA

[JPA] ํ”„๋ก์‹œ์™€ ์—ฐ๊ด€๊ด€๊ณ„ ๊ด€๋ฆฌ

sw_develop 2022. 4. 28. 00:12

๐Ÿ“Œ ํ”„๋ก์‹œ

  • ํ”„๋ก์‹œ ๊ฐœ๋…์€ Hibernate์— ๊ตฌํ˜„๋˜์–ด ์žˆ๋Š” ๊ฒƒ์ด๋‹ค.
  • JPA ์ธํ„ฐํŽ˜์ด์Šค์˜ ๊ตฌํ˜„์ฒด๋กœ Hibernate๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ๋•Œ๋ฌธ์— ํ”„๋ก์‹œ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒƒ์ด๋‹ค.

 

EntityManager์˜ find() : ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ ํ†ตํ•ด ์‹ค์ œ ์—”ํ‹ฐํ‹ฐ ๊ฐ์ฒด๋ฅผ ์กฐํšŒ

EntityManager์˜ getReference() : ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์กฐํšŒ๋ฅผ ๋ฏธ๋ฃจ๋Š” ๊ฐ€์งœ(ํ”„๋ก์‹œ) ์—”ํ‹ฐํ‹ฐ ๊ฐ์ฒด ์กฐํšŒ

 

ํ”„๋ก์‹œ ๊ฐ์ฒด

 

โ–ถ๏ธ ํ”„๋ก์‹œ๊ฐ€ ํ•„์š”ํ•œ ์˜ˆ์‹œ ์ƒํ™ฉ

์œ„์™€ ๊ฐ™์€ ๊ตฌ์กฐ๋กœ ์„ค๊ณ„๊ฐ€ ๋˜์–ด์žˆ์„ ๋•Œ 'Member๋ฅผ ์กฐํšŒํ•  ๋•Œ Team๋„ ํ•จ๊ป˜ ์กฐํšŒํ•ด์•ผ ํ• ๊นŒ?' ๋ผ๋Š” ์˜๋ฌธ์—์„œ ํ”„๋ก์‹œ๋Š” ์‚ฌ์šฉ๋˜๊ฒŒ ๋˜์—ˆ๋‹ค.

Member์˜ ํ•„๋“œ๋งŒ ์กฐํšŒํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ์—๋Š” Team์„ ์กฐ์ธํ•ด์„œ ๊ฐ€์ ธ์˜ค์ง€ ์•Š์•„๋„ ๋˜๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

 

โ–ถ๏ธ ํ”„๋ก์‹œ์˜ ํŠน์ง•1

  • ์‹ค์ œ ํด๋ž˜์Šค๋ฅผ ์ƒ์† ๋ฐ›์•„์„œ ๋งŒ๋“ค์–ด์ง„๋‹ค.
  • ์‹ค์ œ ํด๋ž˜์Šค์™€ ๊ฒ‰ ๋ชจ์–‘์ด ๊ฐ™๋‹ค.

 

  • ํ”„๋ก์‹œ ๊ฐ์ฒด๋Š” ์‹ค์ œ ๊ฐ์ฒด์˜ ์ฐธ์กฐ(์œ„์—์„œ target)๋ฅผ ๋ณด๊ด€ํ•œ๋‹ค.
  • ํ”„๋ก์‹œ ๊ฐ์ฒด๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด ํ”„๋ก์‹œ ๊ฐ์ฒด๋Š” ์‹ค์ œ ๊ฐ์ฒด์˜ ๋ฉ”์†Œ๋“œ๋ฅผ ํ˜ธ์ถœํ•œ๋‹ค.

 

*์ถ”๊ฐ€ํ•  ๋‚ด์šฉ

Hibernate๊ฐ€ ์ž๋™์œผ๋กœ Entity๋ฅผ ์ƒ์† ๋ฐ›์€ Proxy ํด๋ž˜์Šค๋ฅผ ์ƒ์„ฑํ•ด์ค€๋‹ค. 

Entity ํด๋ž˜์Šค์˜ default ์ƒ์„ฑ์ž๊ฐ€ ํ•„์š”ํ•œ ์ด์œ  ์ •๋ฆฌํ•ด๋‘๊ธฐ!

 

โ–ถ๏ธ ํ”„๋ก์‹œ ๊ฐ์ฒด์˜ ์ดˆ๊ธฐํ™”

= ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ๋ฅผ ํ†ตํ•ด DB ์กฐํšŒ ํ›„ ์‹ค์ œ Entity๋ฅผ ์ƒ์„ฑํ•˜๋Š” ๊ฒƒ

 

์˜ˆ์‹œ ์ƒํ™ฉ

//์˜ˆ์‹œ ์ฝ”๋“œ
Member member = entityManager.getReference(Member.class, "id1");
member.getName();
  • ์œ„์—์„œ EntityManager์˜ getReference()๋กœ ํ”„๋ก์‹œ ์—”ํ‹ฐํ‹ฐ๋ฅผ ์กฐํšŒํ•œ๋‹ค.
  • member.getName(); ์ด ์‹คํ–‰๋  ๋•Œ ๋ฐœ์ƒํ•˜๋Š” ์ƒํ™ฉ์€ ์•„๋ž˜ ๊ทธ๋ฆผ๊ณผ ๊ฐ™๋‹ค.

 

(1) getName() ์ฝ”๋“œ๊ฐ€ ์‹คํ–‰๋˜๋ฉด, ํ”„๋ก์‹œ ๊ฐ์ฒด๋Š” ์‹ค์ œ ๊ฐ์ฒด์˜ getName()์„ ํ˜ธ์ถœํ•ด์•ผ ํ•˜๋ฏ€๋กœ,

(2) ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์— ์ดˆ๊ธฐํ™” ์š”์ฒญ์„ ํ•œ๋‹ค. (์ด๋•Œ๋Š” MemberProxy์˜ target์ด ์‹ค์ฒด ๊ฐ์ฒด๋ฅผ ์ฐธ์กฐํ•˜์ง€ ์•Š๊ณ  ์žˆ์œผ๋ฏ€๋กœ Null์ด๊ธฐ ๋•Œ๋ฌธ์—)

(3), (4) ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์—์„œ DB์— ์กฐํšŒ ์ฟผ๋ฆฌ๋ฅผ ๋‚ ๋ ค ์‹ค์ œ ๊ฐ์ฒด๋ฅผ ๊ฐ€์ ธ์™€ ์ƒ์„ฑํ•˜๊ณ , MemberProxy์˜ target์— ์ƒ์„ฑํ•œ ์‹ค์ œ Entity๋ฅผ ์—ฐ๊ฒฐํ•ด์ค€๋‹ค.

(5) ์‹ค์ œ ๊ฐ์ฒด์˜ getName() ํ˜ธ์ถœํ•œ๋‹ค.

 

โ–ถ๏ธ ํ”„๋ก์‹œ์˜ ํŠน์ง•2

  • ํ”„๋ก์‹œ ๊ฐ์ฒด๋Š” ์ฒ˜์Œ ์‚ฌ์šฉํ•  ๋•Œ ํ•œ ๋ฒˆ๋งŒ ์ดˆ๊ธฐํ™”๊ฐ€ ์ด๋ฃจ์–ด์ง„๋‹ค.
  • ํ”„๋ก์‹œ ๊ฐ์ฒด๋ฅผ ์ดˆ๊ธฐํ™”ํ•  ๋•Œ, ํ”„๋ก์‹œ ๊ฐ์ฒด๊ฐ€ ์‹ค์ œ ์—”ํ‹ฐํ‹ฐ๋กœ ๋ณ€๊ฒฝ๋˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ, ํ”„๋ก์‹œ ๊ฐ์ฒด๋ฅผ ํ†ตํ•ด์„œ ์‹ค์ œ ์—”ํ‹ฐํ‹ฐ์— ์ ‘๊ทผ์ด ๊ฐ€๋Šฅํ•ด์ง„ ๊ฒƒ์ด๋‹ค. (์œ„์˜ ์˜ˆ์‹œ์—์„œ target ํ•„๋“œ)
  • ํ”„๋ก์‹œ ๊ฐ์ฒด๋Š” ์›๋ณธ ์—”ํ‹ฐํ‹ฐ๋ฅผ ์ƒ์†๋ฐ›์œผ๋ฏ€๋กœ ๊ฐ์ฒด ํƒ€์ž… ์ฒดํฌ์‹œ ์ฃผ์˜ํ•ด์•ผ ํ•œ๋‹ค.
    • ํ”„๋ก์‹œ or ์—”ํ‹ฐํ‹ฐ๊ฐ€ ๋„˜์–ด์˜ฌ ์ง€ ๋ชจ๋ฅด๊ธฐ ๋•Œ๋ฌธ์— == ๋น„๊ต ์‹คํŒจ, instance of ์‚ฌ์šฉ
  • ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์— ์ฐพ๋Š” ์—”ํ‹ฐํ‹ฐ๊ฐ€ ์ด๋ฏธ ์กด์žฌํ•œ๋‹ค๋ฉด, getReference()๋ฅผ ํ˜ธ์ถœํ•ด๋„ ํ”„๋ก์‹œ๊ฐ€ ์•„๋‹Œ ์‹ค์ œ ์—”ํ‹ฐํ‹ฐ๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.
  • ํ”„๋ก์‹œ๋Š” ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ๋ฅผ ํ†ตํ•ด ๊ฐ์ฒด ์ดˆ๊ธฐํ™”๋ฅผ ํ•˜๊ธฐ ๋•Œ๋ฌธ์—, ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๋Š” ์ค€์˜์† ์ƒํƒœ์ธ ๊ฒฝ์šฐ, ํ”„๋ก์‹œ ์ดˆ๊ธฐํ™”๋ฅผ ์š”์ฒญํ•˜๋ฉด ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค. (Hibernate๋Š” org.hibernate.LazyInitializationException ์˜ˆ์™ธ๋ฅผ ๋ฐœ์ƒ์‹œํ‚ด)

 

*์ถ”๊ฐ€ํ•  ๋‚ด์šฉ

==๊ณผ instance of์˜ ์ฐจ์ด์ ์€?

 

โ–ถ๏ธ ํ”„๋ก์‹œ ํ™•์ธํ•˜๋Š” ๋ฐฉ๋ฒ•

  • ํ”„๋ก์‹œ ์ธ์Šคํ„ด์Šค์˜ ์ดˆ๊ธฐํ™” ์—ฌ๋ถ€ ํ™•์ธ
    • PersistenceUnitUtil.isLoaded(Object entity)
  • ํ”„๋ก์‹œ ํด๋ž˜์Šค ํ™•์ธ
    • entity.getClass().getName()
  • ํ”„๋ก์‹œ ๊ฐ•์ œ ์ดˆ๊ธฐํ™”
    • org.hibernate.Hibernate.initialize(entity);
  • But, JPA ํ‘œ์ค€์€ ๊ฐ•์ œ ์ดˆ๊ธฐํ™”๊ฐ€ ์—†๊ณ , ํ•ด๋‹น ์—”ํ‹ฐํ‹ฐ์˜ ํ•„๋“œ์˜ getter ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค. (ํ”„๋ก์‹œ ๊ฐ์ฒด์ธ ๊ฒฝ์šฐ, ์ดˆ๊ธฐํ™”๊ฐ€ ๋˜๊ธฐ ๋•Œ๋ฌธ์—)

 

๐Ÿ“Œ ์ฆ‰์‹œ ๋กœ๋”ฉ๊ณผ ์ง€์—ฐ ๋กœ๋”ฉ

โ–ถ๏ธ ์ง€์—ฐ ๋กœ๋”ฉ(LAZY) 

= JPA์˜ Entity์—์„œ ํ”„๋ก์‹œ ๊ฐ์ฒด๋ฅผ ์‚ฌ์šฉํ•˜๊ฒŒ ํ•˜๋Š” ์„ค์ •

 

์˜ˆ์‹œ ์ƒํ™ฉ

//์—”ํ‹ฐํ‹ฐ
@Entity
public class Member {
	
    @Id @GeneratedValue
    private Long id;
    
    private String name;
    
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "team_id")
    private Team team;
    
    ...
}
//์กฐํšŒ
Member member = entityManager.find(Member.class, 1L); // A

Team team = member.getTeam();
team.getName();	// B

 

A) Member ์—”ํ‹ฐํ‹ฐ๋Š” DB๋ฅผ ํ†ตํ•ด ์กฐํšŒํ•˜์ง€๋งŒ, Team์€ ํ”„๋ก์‹œ ๊ฐ์ฒด๋กœ ๊ฐ€์ ธ์˜จ๋‹ค.

B) ์‹ค์ œ Team ๊ฐ์ฒด๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์‹œ์ ์— ์ดˆ๊ธฐํ™”๋ฅผ ์ˆ˜ํ–‰ํ•œ๋‹ค. ์ด๋•Œ DB๋ฅผ ํ†ตํ•ด ์กฐํšŒํ•˜๋ฏ€๋กœ ์ฟผ๋ฆฌ๊ฐ€ ์ˆ˜ํ–‰๋œ๋‹ค.

 

โ–ถ๏ธ ์ฆ‰์‹œ ๋กœ๋”ฉ(EAGER)

= JPA์˜ Entity์—์„œ ํ”„๋ก์‹œ ๊ฐ์ฒด ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š” ์„ค์ •

 

์˜ˆ์‹œ ์ƒํ™ฉ - Member์™€ Team์„ ํ•จ๊ป˜ ์ž์ฃผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ

@Entity
public class Member {
	
    @Id @GeneratedValue
    private Long id;
    
    private String name;
    
    @ManyToOne(fetch = FetchType.EAGER)
    @JoinColumn(name = "team_id")
    private Team team;
    
    ...
}
//์กฐํšŒ
Member member = entityManager.find(Member.class, 1L); // A

A) Member๋ฅผ ์กฐํšŒํ•  ๋•Œ Join์„ ์‚ฌ์šฉํ•ด DB์—์„œ Team๋„ ํ•จ๊ป˜ ์กฐํšŒํ•ด์˜จ๋‹ค.

 

๐Ÿ“Œ ํ”„๋ก์‹œ์™€ ์ฆ‰์‹œ๋กœ๋”ฉ ์ฃผ์˜

  • ์ฆ‰์‹œ ๋กœ๋”ฉ์„ ์„ค์ •ํ•˜๋ฉด ํ•œ๋ฒˆ์— ์กฐํšŒํ•ด์™€์•ผ ํ•˜๋ฏ€๋กœ ์˜ˆ์ƒํ•˜์ง€ ๋ชปํ•œ SQL์ด ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์–ด ๊ฐ€๊ธ‰์  ์ง€์—ฐ ๋กœ๋”ฉ๋งŒ ์‚ฌ์šฉํ•˜๋„๋ก ํ•œ๋‹ค.
  • ์ฆ‰์‹œ ๋กœ๋”ฉ์„ ์„ค์ •ํ•œ ๋’ค JPQL์„ ์‚ฌ์šฉํ•ด ์—”ํ‹ฐํ‹ฐ๋ฅผ ์กฐํšŒํ•˜๊ฒŒ ๋˜๋ฉด, ์ฆ‰์‹œ ๋กœ๋”ฉ์œผ๋กœ ์„ค์ •๋œ ์—”ํ‹ฐํ‹ฐ๋ฅผ ํ•จ๊ป˜ ์กฐํšŒํ•ด์˜ค๊ธฐ ์œ„ํ•ด N+1 ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.
    • N+1 = 1๋ฒˆ์˜ ์ฟผ๋ฆฌ์˜ ๊ฒฐ๊ณผ ๊ฐ’์ด N๊ฐœ ์ผ ๋•Œ, N๋ฒˆ์˜ ์ฟผ๋ฆฌ๊ฐ€ ์ถ”๊ฐ€ ์ˆ˜ํ–‰๋˜๋Š” ๊ฒƒ
    • ์šฐ์„  JPQL์€ SQL๋กœ ๋ฒˆ์—ญ๋˜์–ด ์‹คํ–‰๋˜๊ณ , JPA์—์„œ ์ตœ์ ํ™”๋ฅผ ์‹œ์ผœ์ฃผ์ง€ ๋ชปํ•˜๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.
      • ์˜ˆ์‹œ) ์œ„์˜ ์˜ˆ์‹œ์ธ Member์™€ Team์˜ ์ƒํ™ฉ์ด๊ณ  Member๊ฐ€ N๊ฐœ ์กด์žฌํ•  ๋•Œ, JPQL๋กœ Member๋ฅผ ์กฐํšŒํ•˜๋ฉด, N๊ฐœ์˜ Member ์—”ํ‹ฐํ‹ฐ๊ฐ€ ๋ฐ˜ํ™˜๋˜๊ณ , ๊ฐ Member์™€ ์ฆ‰์‹œ ๋กœ๋”ฉ์œผ๋กœ ์„ค์ •๋œ Team๋„ ์กฐํšŒํ•ด์˜ค๊ธฐ ์œ„ํ•ด Team์„ ์กฐํšŒํ•˜๋Š” N๋ฒˆ์˜ ์ฟผ๋ฆฌ๊ฐ€ ์ถ”๊ฐ€๋กœ ์ˆ˜ํ–‰๋˜๋Š” ์ƒํ™ฉ์ด ๋˜๋Š” ๊ฒƒ์ด๋‹ค. 

 

๐Ÿ“Œ ์˜์†์„ฑ ์ „์ด: CASCADE

์‚ฌ์šฉํ•˜๋Š” ์ด์œ 

  • ํŠน์ • ์—”ํ‹ฐํ‹ฐ๋ฅผ ์˜์† ์ƒํƒœ๋กœ ๋งŒ๋“ค ๋•Œ ์—ฐ๊ด€๋œ ์—”ํ‹ฐํ‹ฐ๋„ ํ•จ๊ป˜ ์˜์† ์ƒํƒœ๋กœ ๋งŒ๋“ค๊ณ  ์‹ถ์„ ๋•Œ ์‚ฌ์šฉํ•œ๋‹ค.

 

๐Ÿ“Œ ๊ณ ์•„ ๊ฐ์ฒด

  • ๊ณ ์•„ ๊ฐ์ฒด ์ œ๊ฑฐ : ๋ถ€๋ชจ ์—”ํ‹ฐํ‹ฐ์™€ ์—ฐ๊ด€๊ด€๊ณ„ ๋Š์–ด์ง„ ์ž์‹ ์—”ํ‹ฐํ‹ฐ๋ฅผ ์ž๋™์œผ๋กœ ์‚ญ์ œํ•œ๋‹ค.

 

์˜ˆ์‹œ ์ƒํ™ฉ

//๊ณ ์•„ ๊ฐ์ฒด ์„ค์ •
orphanRemoval = true

//์—ฐ๊ด€๊ด€๊ณ„ ๋Š๊ธฐ
Parent parent = entityManager.find(Parent.class, 1L);
parent.getChildren().remove(0); //์ž์‹ ์—”ํ‹ฐํ‹ฐ๋ฅผ ์ปฌ๋ ‰์…˜์—์„œ ์ œ๊ฑฐ

//DELETE ์ฟผ๋ฆฌ ์ˆ˜ํ–‰
DELETE FROM CHILD WHERE ID = ?
  • ์œ„์ฒ˜๋Ÿผ ๊ณ ์•„ ๊ฐ์ฒด ์„ค์ •์„ ํ•˜๋ฉด, @OneToMany ๊ด€๊ณ„์˜ ๋ฆฌ์ŠคํŠธ ์ปฌ๋ ‰์…˜์—์„œ ํ•ด๋‹น ์—”ํ‹ฐํ‹ฐ๋ฅผ ์ œ๊ฑฐํ•˜๊ธฐ๋งŒ ํ•ด๋„, DB์— DELETE ์ฟผ๋ฆฌ๊ฐ€ ์ž๋™์œผ๋กœ ๋‚˜๊ฐ€ ํ•ด๋‹น ์—”ํ‹ฐํ‹ฐ๊ฐ€ DB ํ…Œ์ด๋ธ”์—์„œ ์‚ญ์ œ๋œ๋‹ค.

 

โ–ถ๏ธ CASCADE์™€์˜ ์ฐจ์ด์ 

  • CASCADE : ๋ถ€๋ชจ ์—”ํ‹ฐํ‹ฐ๊ฐ€ ์‚ญ์ œ๋  ๋•Œ ์ž์‹ ์—”ํ‹ฐํ‹ฐ๋“ค๋„ ํ•จ๊ป˜ ์‚ญ์ œ
  • ๊ณ ์•„ ๊ฐ์ฒด ์„ค์ • : CASCADE ๊ธฐ๋Šฅ + ์—ฐ๊ด€๊ด€๊ณ„๊ฐ€ ๋Š์–ด์ง€๊ธฐ๋งŒ ํ•ด๋„ ์ž์‹ ์—”ํ‹ฐํ‹ฐ ์‚ญ์ œ

 

โ–ถ๏ธ ์ฃผ์˜ํ•  ์ 

  • ๋ถ€๋ชจ ์—”ํ‹ฐํ‹ฐ์—์„œ์˜ ์ฐธ์กฐ๊ฐ€ ์ œ๊ฑฐ๋œ ์—”ํ‹ฐํ‹ฐ๋Š” ๋‹ค๋ฅธ ๊ณณ์—์„œ ์ฐธ์กฐํ•˜์ง€ ์•Š๋Š” ๊ณ ์•„ ๊ฐ์ฒด๋กœ ํŒ๋‹จํ•˜๊ณ  ์‚ญ์ œํ•˜๊ฒŒ ๋œ๋‹ค.
  • ์ฐธ์กฐํ•˜๋Š” ๊ณณ์ด ํ•˜๋‚˜์ผ ๋•Œ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค. 

 

๐Ÿ“Œ ์˜์†์„ฑ ์ „์ด + ๊ณ ์•„ ๊ฐ์ฒด

  • CascadeType.ALL + orphanRemoval=true 2๊ฐ€์ง€ ์˜ต์…˜์„ ๋ชจ๋‘ ํ™œ์„ฑํ™”ํ•˜๋ฉด, ๋ถ€๋ชจ ์—”ํ‹ฐํ‹ฐ๋ฅผ ํ†ตํ•ด ์ž์‹์˜ ์ƒ๋ช… ์ฃผ๊ธฐ๋ฅผ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ๋„๋ฉ”์ธ ์ฃผ๋„ ์„ค๊ณ„์˜ Aggregate Root ๊ฐœ๋…์„ ๊ตฌํ˜„ํ•  ๋•Œ ์œ ์šฉํ•˜๋‹ค.

 

*์ถ”๊ฐ€ํ•  ๋‚ด์šฉ

DDD์˜ Aggregate Root ๊ฐœ๋…์ด ๋ฌด์—‡์ธ๊ฐ€?

@OneToOne์˜ ๋Œ€์ƒ ์—”ํ‹ฐํ‹ฐ(PK๊ฐ€ ์—†๋Š” ํ…Œ์ด๋ธ”)๋Š” FetchType.LAZY๋กœ ์„ค์ •ํ•ด๋„ ์กฐํšŒ ์‹œ ์ง€์—ฐ ๋กœ๋”ฉ์ด ์ ์šฉ๋˜์ง€ ์•Š๋Š”๋ฐ ๊ทธ ์ด์œ ๋Š”?