JiwonDev

JPA #8 ํ”„๋ก์‹œ์™€ ์ง€์—ฐ๋กœ๋”ฉ (join fetch)

by JiwonDev

๐Ÿ“Œ ์ƒํ™ฉ์— ๋”ฐ๋ผ ๋‹ค๋ฅด๋‹ค.

Member๋Š” Team์˜ ์ฐธ์กฐ๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๋‹ค.

Member๋ฅผ ์กฐํšŒํ•  ๋•Œ Team๋„ ํ•œ๋ฒˆ์— ๊ฐ™์ด ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—์„œ ๊ฐ€์ ธ์˜ค๋Š”๊ฒŒ ํšจ์œจ์ ์ธ ๊ฒฝ์šฐ๋„ ์žˆ๊ณ 

๋จผ์ € Member๋งŒ ์กฐํšŒํ•ด๋‘๊ณ , ๋‚˜์ค‘์— ํ•„์š”ํ•  ๋•Œ Team์„ ๊ฐ€์ ธ์˜ค๋Š”๊ฒŒ ๋” ํšจ์œจ์ ์ธ ๊ฒฝ์šฐ๋„ ์žˆ์„ ๊ฒƒ์ด๋‹ค.

 

์ƒํ™ฉ์— ๋”ฐ๋ผ DB ์ฟผ๋ฆฌ๊ฐ€ ๋‹ค๋ฅด๊ฒŒ ๋‚˜๊ฐ€๋„๋ก ์ตœ์ ํ™”ํ•˜๋Š”๊ฒŒ ์ข‹์€๋ฐ, ์ด๋Š” JPA์—์„œ ์–ด๋–ป๊ฒŒ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์„๊นŒ?

 

 

๐Ÿ“Œ ํ”„๋ก์‹œ em.getReference()

๊ทธ๋ž˜์„œ JPA์—์„œ๋Š” ํ”„๋ก์‹œ๋ฅผ ์ œ๊ณตํ•œ๋‹ค.

  • em.find()๋Š” ์‹ค์ œ ์—”ํ‹ฐํ‹ฐ๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค๋ฉด
  • em.getReference()๋Š” ๊ฐ€์งœ ํ”„๋ก์‹œ ๊ฐ์ฒด๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

 

ํ”„๋ก์‹œ๋Š” ์‹ค์ œ ํด๋ž˜์Šค๋ฅผ ์ƒ์†๋ฐ›์•„์„œ ๋งŒ๋“  ๊ฐ์ฒด๋กœ, ์‹ค์ œ ๊ฐ์ฒด์˜ ์ฐธ์กฐ๊ฐ’์„ ๊ฐ€์ง€๊ณ  ์žˆ๋‹ค.

์‚ฌ์šฉํ•˜๋Š” ์‹œ์ ์— ์‹ค์ œ ๊ฐ์ฒด ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•œ๋‹ค. (์ง€์—ฐํ˜ธ์ถœ, ์œ„์ž„)

ํ”„๋ก์‹œ ๊ฐ์ฒด๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋ฐ์ดํ„ฐ์˜ ์ผ๋ถ€๋งŒ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ๋‹ค. ๊ฐ€์ ธ์˜ค์ง€ ์•Š์€ ๋ฐ์ดํ„ฐ๋Š” ์‚ฌ์šฉํ•˜๋Š”์‹œ์ ๊นŒ์ง€ DB์ฟผ๋ฆฌ๋ฅผ ๋Šฆ์ถ˜๋‹ค. 

// id ๊ฐ’๋งŒ ์กฐํšŒํ•˜๊ณ , ๋‚˜๋จธ์ง„ ์ฟผ๋ฆฌ๋ฅผ ๋‚ ๋ฆฌ์ง€ ์•Š๋Š”๋‹ค.
Member member = em.getReference(Member.class, member.getId());

member.getId(); // ์ด๋ฏธ ๊ฐ€์ ธ์˜จ ๋ฐ์ดํ„ฐ ์‚ฌ์šฉ. DB์ฟผ๋ฆฌ X

member.getName(); // ๊ฐ€์ ธ์˜จ์ ์ด ์—†๋Š” ๋ฐ์ดํ„ฐ. ์ด ์‹œ์ ์— ์ƒˆ๋กœ์šด DB์ฟผ๋ฆฌ ๋ฐœ์ƒ

์‹ค์ œ ๊ฐ์ฒด๋ฅผ ๋กœ๊ทธ๋กœ ์ฐ์–ด๋ณด๋ฉด, Member๊ฐ€ ์•„๋‹ˆ๋ผ Member์˜ ํ”„๋ก์‹œ ๊ฐ์ฒด๊ฐ€ ๋ฐ˜ํ™˜๋˜์—ˆ์Œ์„ ์•Œ ์ˆ˜ ์žˆ๋‹ค.

์ตœ์ดˆ์— getReference๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ์‹œ์ ์—๋Š”, ์‹ค์ œ ๊ฐ์ฒด์˜ ์ฐธ์กฐ๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์ง€์•Š๊ณ  ๊ทธ๋ƒฅ ํ”„๋ก์‹œ๋งŒ ๋งŒ๋“ ๋‹ค.

โœจ ๋‹จ ์ด๋ฏธ ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์— ์‹ค์ œ ๊ฐ์ฒด๊ฐ€ ์žˆ๋‹ค๋ฉด ๊ตณ์ด ํ”„๋ก์‹œ๋ฅผ ๋งŒ๋“ค์ง€ ์•Š๊ณ  ์‹ค์ œ ์—”ํ‹ฐํ‹ฐ๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

๊ทธ๋ฆฌ๊ณ  ์‹ค์ œ ๊ฐ’์ด ํ•„์š”ํ•  ๋•Œ ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ๋ฅผ ํ†ตํ•ด DB์—์„œ ์กฐํšŒํ•ด์„œ ์‹ค์ œ ๊ฐ์ฒด์˜ ์ฐธ์กฐ๊ฐ’์„ ๊ฐ€์ ธ์˜จ๋‹ค.

์ฐธ๊ณ ๋กœ ๋น„์˜์†, ์ค€์˜์† ์ƒํƒœ(detach)์ผ ๋•Œ ํ”„๋ก์‹œ๋ฅผ ์ดˆ๊ธฐํ™”ํ•˜๋ฉด ์˜ˆ์™ธ(hibernate.LazyInitializationException)๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.

์ฆ‰, ํ”„๋ก์‹œ ๊ฐ์ฒด๋Š” ๋”ฑ 1๋ฒˆ๋งŒ ์ดˆ๊ธฐํ™”๋˜๊ณ  ๊ณ„์† ์‚ฌ์šฉ๋œ๋‹ค. ์ค‘๊ฐ„์— ์‹ค์ œ ๊ฐ์ฒด๋กœ ๋ฐ”๋€Œ๋Š”๊ฒŒ ์•„๋‹ˆ๋‹ค.

์‚ฌ์šฉํ•˜๋Š” ์‹œ์ ์— DB์—์„œ ์กฐํšŒํ•˜๊ณ  [ ํ”„๋ก์‹œ ๊ฐ์ฒด์˜ ์ฐธ์กฐ๊ฐ’ ] ์— ์‹ค์ œ ๊ฐ์ฒด๊ฐ€ ๋“ค์–ด๊ฐ€๋Š” ๊ฑฐ๋‹ค.

 

โœ” ํ”„๋ก์‹œ๋Š” ์–ธ์ œ ์‚ฌ์šฉ๋ ์ง€ ๋ชจ๋ฅธ๋‹ค. ํƒ€์ž…๋น„๊ต๋Š” instance of

์—”ํ‹ฐํ‹ฐ์˜ ํƒ€์ž… ๋น„๊ตํ•  ๋•Œ์—๋Š” ๋ฐ˜๋“œ์‹œ instance of๋ฅผ ์‚ฌ์šฉํ•ด์•ผํ•œ๋‹ค.

JPA์—์„œ๋Š” ๊ฐ์ฒด์˜ ํƒ€์ž…์„ (==)๋กœ ๋น„๊ตํ•ด์„œ๋Š” ์•ˆ๋œ๋‹ค. ํ”„๋ก์‹œ๋ž‘ ์›๋ณธ ๊ฐ์ฒด๋ฅผ ๋น„๊ตํ•˜๋ฉด ํƒ€์ž…์ด ๊ฐ™์•„๋„ False๊ฐ€ ๋ฐ˜ํ™˜๋œ๋‹ค.

 

๋‹ค๋งŒ ํƒ€์ž…๋ง๊ณ  ๊ฐ์ฒด์˜ ๋™์ผ์„ฑ์€ (==)๋กœ ๋น„๊ตํ•ด๋„ ๋œ๋‹ค.

  • getReference()๋„ ์ด๋ฏธ ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์— ๊ฐ€์ ธ์˜จ ์‹ค์ œ ๊ฐ์ฒด๊ฐ€ ์žˆ๋‹ค๋ฉด, ํ”„๋ก์‹œ๊ฐ€ ์•„๋‹Œ ์‹ค์ œ Entity๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.
  • ๋™์ผ์„ฑ์„ ๋งž์ถ”๊ธฐ ์œ„ํ•ด์„œ ํ˜ธ์ถœ ์ˆœ์„œ์— ๋”ฐ๋ผ em.find()๋„ ํ”„๋ก์‹œ๋ฅผ ๋ฐ˜ํ™˜ํ•  ์ˆ˜๋„ ์žˆ๋‹ค.
// ์‹ค์ œ ๐Ÿ’ถEntity๊ฐ€ ๋ฐ˜ํ™˜๋จ
Member.findMember = entityManager.find(Member.class, member1.getId());

// ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์— Entity๊ฐ€ ์žˆ์Œ. ํ”„๋ก์‹œ๊ฐ€ ์•„๋‹Œ ์‹ค์ œ ๐Ÿ’ถEntity๊ฐ€ ๋ฐ˜ํ™˜๋จ
Member proxyMember = entityManager.getReference(Member.class, member1.getId());

// True, ๐Ÿ’ถEntity == ๐Ÿ’ถEntity
System.out.println(porxyMember == findMember);
// โœจProxy ๊ฐ์ฒด๊ฐ€ ๋ฐ˜ํ™˜๋จ
Member proxyMember = entityManager.getReference(Member.class, member1.getId());

// ์‹ค์ œ Entity๊ฐ€ ๋ฐ˜ํ™˜๋˜์–ด์•ผํ•˜์ง€๋งŒ, ์กฐํšŒํ•ด๋ณด๋ฉด โœจProxy ๊ฐ์ฒด๊ฐ€ ๋ฐ˜ํ™˜๋จ
Member.findMember = entityManager.find(Member.class, member1.getId());

// True, โœจProxy == โœจProxy
System.out.println(porxyMember == findMember);

 

 

๊ทธ๋ž˜์„œ JPA์—์„œ๋Š” ์•„๋ž˜์™€ ๊ฐ™์€ ๋ฉ”์„œ๋“œ๋ฅผ ์ œ๊ณตํ•ด์ค€๋‹ค.

// ํ”„๋ก์‹œ ์ธ์Šคํ„ด์Šค์˜ ์‹ค์ œ ๊ฐ์ฒด์ฐธ์กฐ ์ดˆ๊ธฐํ™” ์—ฌ๋ถ€ true / false
entityMangerFactory.PersistenceUnitUtil.isLoaded(Object proxyEntity)

// ํด๋ž˜์Šค ์ด๋ฆ„ ํ™•์ธ (ํด๋ž˜์Šค ๋ช…์œผ๋กœ ํ”„๋ก์‹œ ์—ฌ๋ถ€ํ™•์ธ)
entity.getClass().getName()

์‚ฌ์šฉํ•  ์ผ์€ ๊ฑฐ์˜ ์—†์ง€๋งŒ, ์‚ฌ์šฉ์—ฌ๋ถ€์™€ ์ƒ๊ด€์—†์ด ํ”„๋ก์‹œ์˜ ์‹ค์ œ ๊ฐ์ฒด ์ฐธ์กฐ๋ฅผ ๊ฐ•์ œ๋กœ ์ดˆ๊ธฐํ™”ํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด ์•„๋ž˜์™€ ๊ฐ™๋‹ค.

org.hibernate.Hibernate.initialize(proxyEntity);

 

 

๐Ÿ“Œ ๋‹ค๋งŒ getReference()๋ฅผ ์“ธ ์ผ์€ ๊ฑฐ์˜ ์—†๋‹ค.

ํ”„๋ก์‹œ์˜ ๋™์ž‘์„ ์ดํ•ดํ•˜๊ธฐ์œ„ํ•ด ์ด๋Ÿฐ ๋ฉ”์„œ๋“œ๋„ ์žˆ๋‹ค๋Š” ๊ฑธ ์•Œ๋ ค์ค€ ๊ฒƒ์ด๋‹ค.

์‹ค์ œ ์‚ฌ์šฉ์€ getReference()๋ฅผ ์“ฐ์ง€์•Š๊ณ , ์• ๋…ธํ…Œ์ด์…˜์„ ์˜ต์…˜์„ ํ†ตํ•œ ์ง€์—ฐ๋กœ๋”ฉ์„ ์‚ฌ์šฉํ•œ๋‹ค.

 

 

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

๋ฒˆ๊ฑฐ๋กญ๊ฒŒ getRefernce()๋กœ ํ”„๋ก์‹œ๋ฅผ ๊ด€๋ฆฌํ•  ํ•„์š”์—†์ด, ์•„๋ž˜์™€ ๊ฐ™์ดํ•˜๋ฉด ๋œ๋‹ค.

@Entity
public class Member {

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "TEAM_ID")
    private Team team;
}

์ฟผ๋ฆฌ๋ฅผ ์ฐ์–ด๋ณด๋ฉด em.find()๋กœ ์กฐํšŒํ•˜๋ฉด ์‹ค์ œ ๊ฐ์ฒด๊ฐ€ ์•„๋‹Œ ํ”„๋ก์‹œ๊ฐ€ ๋ฐ˜ํ™˜๋˜์—ˆ์Œ์„ ์•Œ ์ˆ˜ ์žˆ๋‹ค.

์ฆ‰ ์œ„์—์„œ ์„ค๋ช…ํ•œ๊ฒƒ๊ณผ ๋˜‘๊ฐ™์ด ์‹ค์ œ ๊ฐ์ฒด๋ฅผ ์กฐํšŒํ•˜๋Š” ์‹œ์  (getTeam)์—์„œ ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์— DB์ฟผ๋ฆฌ๊ฐ€ ๋‚ ๋ผ๊ฐ€๊ฒŒ๋˜๊ณ , ํ”„๋ก์‹œ์˜ ์ฐธ์กฐ๊ฐ€ ์ดˆ๊ธฐํ™”๋œ๋‹ค.

 

๋ฐ˜๋Œ€๋กœ Member์™€ Team์„ ํ•ญ์ƒ ๊ฐ™์ด ์‚ฌ์šฉํ•ด์„œ, ํ•œ๋ฒˆ์— ๋ชจ๋‘ ๊ฐ€์ ธ์˜ค๊ณ  ์‹ถ๋‹ค๋ฉด ์ฆ‰์‹œ๋กœ๋”ฉ์œผ๋กœ ์„ค์ •ํ•ด์ฃผ๋ฉด ๋œ๋‹ค.

JPA ๊ตฌํ˜„์ฒด(ํ•˜์ด๋ฒ„๋„ค์ดํŠธ)๋Š” ์ฆ‰์‹œ๋กœ๋”ฉ์„ ์‚ฌ์šฉํ•˜๋ฉด ์กฐ์ธ์„ ์ด์šฉํ•ด์„œ ํ•˜๋‚˜์˜ SQL๋ฌธ์— ์ตœ๋Œ€ํ•œ ๋งŽ์€ ํ…Œ์ด๋ธ”์„ ๊ฐ€์ ธ์˜จ๋‹ค.

@Entity
public class Member {

    @ManyToOne(fetch = FetchType.EAGER)
    @JoinColumn(name = "TEAM_ID")
    private Team team;
}

์ฟผ๋ฆฌ ํ•˜๋‚˜๋กœ Member์™€ Team์„ ๋‹ค ๊ฐ€์ ธ์˜จ๋‹ค.

 

โœ” ๋˜๋„๋ก์ด๋ฉด ๋ชจ๋“  ์—ฐ๊ด€๊ด€๊ณ„๋ฅผ ์ง€์—ฐ ๋กœ๋”ฉ(LAZY)๋กœ ์‚ฌ์šฉํ•˜์ž.

๋ชจ๋“  ์—ฐ๊ด€ ๊ด€๊ณ„๋Š” ๋˜๋„๋ก์ด๋ฉด Lazy๋กœ ์„ค์ •ํ•ด์„œ ์‚ฌ์šฉํ•˜์ž.

  • @ManyToOne, @OneToOne โžก ์ฆ‰์‹œ๋กœ๋”ฉ(Eager)๋ฅผ ๊ธฐ๋ณธ ๊ฐ’์œผ๋กœ ์ œ๊ณต
  • @OneToMany, @ManyToMany โžก ์ง€์—ฐ๋กœ๋”ฉ(Lazy)๋ฅผ ๊ธฐ๋ณธ ๊ฐ’์œผ๋กœ ์ œ๊ณต

์œ„์˜ ์ฟผ๋ฆฌ๋ฌธ์„ ๋ณด๋ฉด ์•Œ๊ฒ ์ง€๋งŒ, JPA ํŠน์„ฑ์ƒ ์ฆ‰์‹œ๋กœ๋”ฉ์€ ์ „ํ˜€ ์˜ˆ์ƒํ•˜์ง€ ๋ชปํ•œ SQL๋ฌธ์„ ๋ฐœ์ƒ์‹œํ‚จ๋‹ค.

๋งŒ์•ฝ ์ฐธ์กฐ๊ฐ€ 10๊ฐœ๋ผ๋ฉด, Member ํ•œ๋ฒˆ ์กฐํšŒ๋กœ ํ…Œ์ด๋ธ” 10๊ฐœ๋ฅผ ์กฐ์ธํ•˜๋Š” ์ •์‹ ๋‚˜๊ฐ„ ์ฟผ๋ฆฌ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋‹ค.

 

๋˜ํ•œ ์ฆ‰์‹œ๋กœ๋”ฉ์€ JPQL์„ ์‚ฌ์šฉํ•  ๋•Œ N+1 ๋ฌธ์ œ๋ฅผ ์ผ์œผํ‚ค๊ธฐ ์‰ฝ๋‹ค.

// 1. JPQL์„ ์‚ฌ์šฉํ•˜๋ฉด ์ฆ‰์‹œ flush๋˜์–ด Member๋ฅผ DB์—์„œ ๊ฐ€์ ธ์˜จ๋‹ค
em.createQurey("select m from Member m", Member.class);
// 2. ๊ทธ๋Ÿฐ๋ฐ Member์•ˆ์— ์žˆ๋Š” Team์ด ์ฆ‰์‹œ๋กœ๋”ฉ์ด๋‹ค. ๊ฐ’์„ ๋ฐ”๋กœ ๊ฐ€์ ธ์™€์•ผํ•œ๋‹ค.
// 3. ๊ฐ๊ฐ์˜ Member๋Š” ๋‹ค๋ฅธ Team ๊ฐ’์„ ๊ฐ€์ง„๋‹ค. ๋งŒ์•ฝ Member๊ฐ€ 10๊ฐœ๋ฉด Team ์ฟผ๋ฆฌ๋„ 10๊ฐœ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.
// SQL : select * from Team where TEAM_ID = xxx.. ์ฟผ๋ฆฌ๊ฐ€ ๋ฉค๋ฒ„ ๊ฐœ์ˆ˜๋งŒํผ ๋ฐœ์ƒ.
@Entity
public class Member {

    @ManyToOne(fetch = FetchType.EAGER)
    @JoinColumn
    private Team team;
}

์ฆ‰ ๋‚œ ์ฟผ๋ฆฌ1๋ฒˆ์„ ๋‚ ๋ ธ๋Š”๋ฐ, ์‹ค์ œ ์ฟผ๋ฆฌ๋Š” 1+N๋ฒˆ (Team์„ ์กฐํšŒํ•˜๋Š” ์ฟผ๋ฆฌ N๊ฐœ)๊ฐ€ ๋ฐœ์ƒํ•˜๋Š” ๊ฐœํŒ์ด ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋‹ค.

์ง€์—ฐ๋กœ๋”ฉ (Lazy)๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์ด๋Ÿฐ ์ผ์ด ์—†๋‹ค. Team์„ ์‚ฌ์šฉํ•˜๋Š” ์ˆœ๊ฐ„๊นŒ์ง€ ๋ฏธ๋ฃจ๋‹ค๊ฐ€ ์‚ฌ์šฉํ•˜๋Š” ์‹œ์ ์— ์ฟผ๋ฆฌ ํ•˜๋‚˜๋งŒ ๋‚˜๊ฐ„๋‹ค.

 

๋งŒ์•ฝ Member์™€ Team์„ ํ•จ๊ป˜ ๊ฐ€์ ธ์˜ค๊ณ  ์‹ถ๋‹ค๋ฉด JPA์˜ fetch join์„ ์ด์šฉํ•˜๋„๋ก ํ•˜์ž.

์ด๋Š” SQL๋ฌธ์— ์กด์žฌํ•˜๋Š” ๋ฌธ๋ฒ•์€ ์•„๋‹ˆ๊ณ , ์—ฐ๊ด€๋œ ์—”ํ‹ฐํ‹ฐ(์ปฌ๋ ‰์…˜)์„ ๊ฐ™์ด ์กฐํšŒํ•œ ๋’ค์— ๊ฐ์ฒด ํ•„๋“œ์— set์œผ๋กœ ์—ฐ๊ฒฐํ•˜๋Š” ๋ฐฉ๋ฒ•.

// join fetch ์กฐ์ธ๊ฒฝ๋กœ
em.createQuery("select m from Member m join fetch m.team", Member.class)

์ด๋ ‡๊ฒŒ Member๊ฐ€ ์‚ฌ์šฉํ•˜๋Š” Team์„ ๋ฏธ๋ฆฌ ํ•œ๊บผ๋ฒˆ์— ๊ฐ€์ ธ์˜จ๋‹ค.

ํŒจ์น˜ ์กฐ์ธ์„ ํ†ตํ•ด ์—ฐ๊ด€๋œ Team์„ ์ด๋ฏธ ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ๋กœ ๊ฐ€์ ธ์™”์œผ๋ฏ€๋กœ, ์ดํ›„ ํ˜ธ์ถœํ•˜๋”๋ผ๋„ ์ฟผ๋ฆฌ๊ฐ€ ๋ฐœ์ƒํ•˜์ง€ ์•Š๋Š”๋‹ค.

List<Member> list = 
    em.createQuery(jpql "select m from Member m join fetch m.team", Member.class).getResultList();

for(Member m : list){
    System.out.println(m.getTeam().getName()); // LAZY ๋กœ๋”ฉ ๋ฐœ์ƒ ์•ˆํ•จ
}

 

๊ทธ ์™ธ ์—”ํ‹ฐํ‹ฐ ๊ทธ๋ž˜ํ”„ ์• ๋…ธํ…Œ์ด์…˜์ด๋‚˜ ๋ฐฐ์น˜์‚ฌ์ด์ฆˆ๋ฅผ ์ด์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์ด ์žˆ๊ธดํ•œ๋ฐ, ์ด๋Š” ๋‚˜์ค‘์— ์•Œ์•„๋ณด๋„๋ก ํ•˜์ž.

 

 

๐Ÿ“Œ ์˜์†์„ฑ ์ „์ด, @OneToMany( casecade= ~ ) ์˜ต์…˜

์ผ๋‹จ ์Šคํฌ๋ถ€ํ„ฐ ํ•˜์ž๋ฉด, ์˜์†์„ฑ ์ „์ด๋Š” ์—ฐ๊ด€๊ด€๊ณ„๋ฅผ ๋งคํ•‘ํ•˜๋Š” ๊ฒƒ๊ณผ ์•„๋ฌด๋Ÿฐ ๊ด€๊ณ„๊ฐ€ ์—†๋‹ค.

์—”ํ‹ฐํ‹ฐ๋ฅผ ์˜์†ํ™” ํ•  ๋•Œ, ์—ฐ๊ด€๋œ ๋‹ค๋ฅธ ๊ฐ์ฒด๋„ ํ•จ๊ป˜ ์˜์†ํ™”ํ•˜๋Š” ํŽธ์˜๋„๊ตฌ์ด๋‹ค. 

  • Parent์™€ Child์˜ ๋ผ์ดํ”„์‚ฌ์ดํด์ด ๊ฐ™์•„์„œ, ํ•œ๊บผ๋ฒˆ์— ํ†ต์œผ๋กœ ์ €์žฅ/ ์‚ญ์ œํ•˜๊ณ  ์‹ถ์„ ๋•Œ ์‚ฌ์šฉ.
  • ์†Œ์œ ์ž๊ฐ€ 1๊ฐœ (Parent - Child) ์—์„œ๋งŒ ์‚ฌ์šฉํ•˜๊ณ , ๋งŒ์•ฝ Child๋ฅผ ์“ฐ๋Š” ๊ณณ์ด ์—ฌ๋Ÿฌ ๊ณณ์ด๋ผ๋ฉด ์ ˆ๋Œ€ ์“ฐ๋ฉด ์•ˆ๋œ๋‹ค.
    ์ € ๋ฉ€๋ฆฌ์„œ ์ˆ˜์ •์„ ํ–ˆ๋Š”๋ฐ, ๋‚ด ์—”ํ‹ฐํ‹ฐ์— ๋ณ€๊ฒฝ์ด ์ „ํŒŒ๋˜๋ฉด ํฐ์ผ๋‚œ๋‹ค.

์—”ํ‹ฐํ‹ฐ๋ฅผ ์˜์†ํ™” ํ•  ๋•Œ, ์—ฐ๊ด€๋œ ์—”ํ‹ฐํ‹ฐ๋„ ํ•จ๊ป˜ ์˜์†ํ™”ํ•˜๋Š” ํŽธ์˜๋„๊ตฌ์ด๋‹ค. ์ข…๋ฅ˜๋Š” ์•„๋ž˜์™€ ๊ฐ™์ด 6๊ฐœ๊ฐ€ ์กด์žฌํ•œ๋‹ค.

  • ALL ๋ชจ๋‘ ์ ์šฉ - ๋ชจ๋“  ์ „ํŒŒ๋ฅผ ์ ์šฉ
  • PERSIST ์˜์†๋งŒ - ์ €์žฅํ•  ๋•Œ์—๋งŒ ์ „ํŒŒ ์‚ฌ์šฉ
    * cascade๋กœ ์˜์†ํ™”๋ฅผ ์‹œ๋„ํ•˜๋Š” ๊ฐ์ฒด๊ฐ€ ์ด๋ฏธ DB์— ํ‚ค๊ฐ’์ด ์กด์žฌํ•œ๋‹ค๋ฉด PersistentObjectException ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.

๋‹ค๋งŒ ์œ„ 2๊ฐ€์ง€๋งŒ ์ž์ฃผ ์‚ฌ์šฉํ•˜๊ณ , ์•„๋ž˜ 4๊ฐ€์ง€๋Š” ๊ฑฐ์˜ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š”๋‹ค.

  • REMOVE ์‚ญ์ œ๋งŒ - ๋ถ€๋ชจ๋ฅผ ์‚ญ์ œํ•˜๋ฉด ๋ฐ‘์— ์ž์‹๋„ ๋ชจ๋‘ ์‚ญ์ œ๊ฐ€ ์ „ํŒŒ๋จ
  • MERGE ๋ณ‘ํ•ฉ๋งŒ - ํŠธ๋žœ์žญ์…˜์ด ์ข…๋ฃŒ๋˜๊ณ  ์ค€์˜์† ์ƒํƒœ์—์„œ ์—”ํ‹ฐํ‹ฐ๋ฅผ ์ถ”๊ฐ€ํ•˜๊ฑฐ๋‚˜, ๋ถ€๋ชจ๊ฐ€ merge()๋ฅผ ์‚ฌ์šฉํ•œ ๊ฒฝ์šฐ
  • RERESH ๋ฆฌํ”„๋ ˆ์‹œ๋งŒ - ์—”ํ‹ฐํ‹ฐ๋ฅผ ์ƒˆ๋กœ ๊ณ ์น  ๋•Œ, ์ด ํ•„๋“œ์— ๋ณด์œ  ๋œ ์—”ํ‹ฐํ‹ฐ๋„ ์ƒˆ๋กœ๊ณ ์นจํ•จ.
  • DETACH ์ค€์˜์†๋งŒ - ๋ถ€๋ชจ๊ฐ€ detach()๋กœ ์ค€์˜์†์ƒํƒœ๊ฐ€ ๋˜๋ฉด, ์ž์‹๋„ ๋ชจ๋‘ ์ค€์˜์†ํ™”๋จ. 

 

์ด๋Š” ์˜ˆ์ œ๋ฅผ ๋ณด๋ฉด ์‰ฝ๊ฒŒ ์ดํ•ดํ•  ์ˆ˜ ์žˆ๋‹ค. ์•„๋ž˜ ์ฝ”๋“œ๋ฅผ ๋ณด์ž

@Entity
public class Child {

    @ManyToOne
    @JoinColumn("PARENT_ID")
    private Parent parent;

    public void setParent(Parent parent) {
        this.parent = parent;
    }
}
@Entity
public class Parent {

    @OneToMany(mappedBy = "parent")
    private List<Child> childList = new ArrayList<>();

    public void addChild(Child child) {
        childList.add(child);
        child.setParent(this); // ์—ฐ๊ด€๊ด€๊ณ„ ์„ค์ •
    }
}

 

์ด ์ฝ”๋“œ๋ฅผ ๊ธฐ์กด์˜ JPA๋กœ ์‚ฌ์šฉํ•˜๋ฉด ์•„๋ž˜์™€ ๊ฐ™์ด ํ•˜๋‚˜ํ•˜๋‚˜ persist() ํ•ด์ค˜์•ผ ํ•œ๋‹ค.

Child childOne = new Child();
Child childTwo = new Child();

Parent parent = new Parent();
parent.addChild(childOne);
parent.addChild(childTwo);

entityManager.persist(parent);
entityManager.persist(childOne);
entityManager.persist(childTwo);

 

์ด๋ฅผ Parent์—์„œ ์•„๋ž˜์™€ ๊ฐ™์ด Cascade๋ฅผ ALL๋กœ ์„ค์ •ํ•ด์ฃผ๋ฉด

@Entity
public class Parent {

    @OneToMany(mappedBy = "parent", cascade = CascadeType.ALL)
    private List<Child> childList = new ArrayList<>();
    ...
}

 

parent ๊ฐ€ ์ฐธ์กฐํ•˜๋Š” ๋ชจ๋“  ๊ฐ์ฒด๋Š” ํ•จ๊ป˜ ์˜์†ํ™”๋œ๋‹ค.

Child childOne = new Child();
Child childTwo = new Child();

Parent parent = new Parent();
parent.addChild(childOne);
parent.addChild(childTwo);

// cascade = ALL ์ด๋ผ์„œ parent ๋‚ด๋ถ€์—์žˆ๋Š” ๋ชจ๋“  child๋„ ํ•จ๊ป˜ ์˜์†ํ™”๋จ.
entityManager.persist(parent);
// entityManager.persist(childOne);
// entityManager.persist(childTwo);

์‹ค์ œ DB์—๋„ ์ž˜ ๋“ค์–ด๊ฐ€์žˆ๋‹ค.

 

์˜ˆ๋ฅผ ๋“ค์–ด ์ฃผ๋ฌธ๊ณผ ๋ฐฐ๋‹ฌ์ด ์žˆ์„ ๋•Œ, [์ฃผ๋ฌธ์ด ๋ฐฐ๋‹ฌ์˜ ์ƒ๋ช…์ฃผ๊ธฐ๋ฅผ ๊ด€๋ฆฌํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด] ์•„๋ž˜์™€ ๊ฐ™์ด ์„ค์ •ํ•ด์ค„ ์ˆ˜ ์žˆ๋‹ค.

๋ฌผ๋ก  ์ด๋Š” ๋น„์ฆˆ๋‹ˆ์Šค ์ƒํ™ฉ์— ๋”ฐ๋ผ์„œ ๋‹ค๋ฅด๋‹ค. ์ฃผ๋ฌธ์ด ์‚ญ์ œ๋˜์–ด๋„ ๋ณ„๋„์˜ ๋ฐฐ๋‹ฌ ๊ธฐ๋ก๋งŒ ๋‚จ๊ธฐ๊ณ  ์‹ถ์„ ์ˆ˜๋„ ์žˆ์œผ๋‹ˆ๊นŒ.

 // ์ด์ œ Order์™€ Delivery๋Š” ๊ฐ™์€ ์ƒ๋ช…์ฃผ๊ธฐ๋ฅผ ๊ณต์œ ํ•œ๋‹ค.
@OneToMany(mappedBy = "parent", cascade = ALL)
private List<Delivery> Deliverys = new ArrayList<>();

 

 

๐Ÿ“Œ @OneToMany(orphanRemoval = true) , ๊ณ ์•„ ๊ฐ์ฒด ์‚ญ์ œ

๋ง์ด ์ข€ ์Šฌํ”ˆ๋ฐ, ๋ถ€๋ชจ ์—”ํ‹ฐํ‹ฐ์™€ ๊ด€๊ณ„๊ฐ€ ๋Š์–ด์ง„ ์ž์‹ ์—”ํ‹ฐํ‹ฐ๋ฅผ ์‚ญ์ œํ•˜๋Š” ๊ธฐ๋Šฅ์ด๋‹ค.

  • ๋ฐ˜๋“œ์‹œ ์ฐธ์กฐํ•˜๋Š” ๊ณณ์ด ํ•˜๋‚˜์ผ ๋•Œ ์‚ฌ์šฉํ•ด์•ผํ•œ๋‹ค. GC์ฒ˜๋Ÿผ ์ฐธ์กฐ ์ถ”์ ํ•ด์ฃผ๋Š” ๊ธฐ๋Šฅ์€ ์—†๋‹ค.
  • @OneToOne, @OneToMany๋งŒ ๊ฐ€๋Šฅํ•˜๋ฉฐ ํŠน์ • ์—”ํ‹ฐํ‹ฐ๊ฐ€ ๊ฐœ์ธ ์†Œ์œ (ํ•œ ๊ฐ์ฒด๋งŒ ์ฐธ์กฐ)์ผ ๋•Œ ์‚ฌ์šฉํ•œ๋‹ค.
    * ์ฐธ๊ณ ๋กœ Parent๋ฅผ ํ†ต์œผ๋กœ ์‚ญ์ œํ•˜๋ฉด cascade = REMOVE ์ฒ˜๋Ÿผ ๋™์ž‘ํ•œ๋‹ค. ๋ชจ๋“  ์ž์‹ ๊ฐ์ฒด๋ฅผ ์‚ญ์ œํ•œ๋‹ค.
@Entity
public class Parent {

    @OneToMany(mappedBy = "parent", orphanRemoval = true) // ์„ค์ •
    private List<Child> childList = new ArrayList<>();
    ...
}
Parent parent1 = em.find(Parent.class, id); 
parent1.getChildren().remove(0); // Parent์—์„œ Child์˜ ์—ฐ๊ด€๊ด€๊ณ„๋ฅผ ๋Š์—ˆ๋‹ค.

// orphanRemoval = true์ธ ๊ฒฝ์šฐ, ๋ฐ”๋กœ Child๊ฐ€ DB์—์„œ ์‚ญ์ œ๋จ
DELETE FROM Child WHERE ID=0

 

โœ” ๊ฟ€ํŒ (๋ถ€๋ชจ๊ฐ€ ์ž์‹๊ฐ์ฒด์˜ ์ƒ๋ช…์ฃผ๊ธฐ ๊ด€๋ฆฌ)

์ฐธ๊ณ ๋กœ ์•„๋ž˜์™€ ๊ฐ™์ด ALL + orphanRemoval์„ ํ•จ๊ป˜ ์‚ฌ์šฉํ•˜๋ฉด ๋ถ€๋ชจ๊ฐ์ฒด๊ฐ€ ์ž์‹๊ฐ์ฒด์˜ ์ƒ๋ช…์ฃผ๊ธฐ๋ฅผ ์™„์ „ํžˆ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ค.

์ฆ‰ ์ด๋ ‡๊ฒŒํ•˜๋ฉด ๋ถ€๋ชจ ๊ฐ์ฒด๋งŒ Repository์— ์—ฐ๊ฒฐ๋˜์–ด์žˆ์œผ๋ฉด ์ž์‹ ๊ฐ์ฒด์šฉ Repository๋Š” ๊ตฌํ˜„ํ•  ํ•„์š”๊ฐ€ ์—†๋‹ค.

โžก ์ด๋Š” ๋„๋ฉ”์ธ ์ฃผ๋„ ์„ค๊ณ„(DDD)์—์„œ Aggregate Root ๊ฐœ๋…์„ ๊ตฌํ˜„ํ•  ๋•Œ ์œ ์šฉํ•˜๊ฒŒ ์‚ฌ์šฉ๋œ๋‹ค.
Repository๋Š” ์˜ค์ง Aggregate Root๋งŒ ์—ฐ๊ฒฐ๋˜์–ด์žˆ๊ณ , Root๊ฐ€ ๋ชจ๋“  ๊ฐ์ฒด์˜ ์ƒ๋ช…์ฃผ๊ธฐ๋ฅผ ๊ด€๋ฆฌํ•˜๋„๋ก ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค.

@OneToMany(mappedBy = "parent", cascade = CascadeType.ALL, orphanRemoval = true)
private List<Child> childList = new ArrayList<>();

 

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

JiwonDev

JiwonDev

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