hades

[JPA] JPA ๊ธฐ์ดˆ ๋ณธ๋ฌธ

๐Ÿƒ๐Ÿป‍โ™‚๏ธ ๊ธฐ๋ณธํ›ˆ๋ จ/Java

[JPA] JPA ๊ธฐ์ดˆ

hades1 2024. 8. 2. 09:49

JPA ์†Œ๊ฐœ

JPA

  • Java Persistnce API
  • Java ์ง„์˜์˜ ORM ํ‘œ์ค€

 

ORM

  • Object-relational mapping (๊ฐ์ฒด ๊ด€๊ณ„ ๋งคํ•‘)
  • ๊ฐ์ฒด๋Š” ๊ฐ์ฒด๋Œ€๋กœ ์„ค๊ณ„ํ•˜๊ณ , ๊ด€๊ณ„ํ˜• ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋Š” ๊ด€๊ณ„ํ˜• ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋Œ€๋กœ ์„ค๊ณ„ํ•˜๊ณ , ORM ํ”„๋ ˆ์ž„์›Œํฌ๊ฐ€ ๊ฐ์ฒด์™€ ๊ด€๊ณ„ํ˜• ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ ์ค‘๊ฐ„์—์„œ ๋งคํ•‘
  • ๋Œ€์ค‘์ ์ธ ์–ธ์–ด์—๋Š” ๋Œ€๋ถ€๋ถ„ ORM ๊ธฐ์ˆ ์ด ์กด์žฌ

 

JPA๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜๊ณผ JDBC ์‚ฌ์ด์—์„œ ๋™์ž‘

์ฐธ๊ณ ) JDBC(Java Database Connectivity)๋Š” ์ž๋ฐ”์—์„œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ ‘์†ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•˜๋Š” ์ž๋ฐ” API์ด๋‹ค.

ํŠธ๋žœ์žญ์…˜(Transaction)์€ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์˜ ์ƒํƒœ๋ฅผ ๋ณ€ํ™”์‹œํ‚ค๊ธฐ ํ•ด์„œ ์ˆ˜ํ–‰ํ•˜๋Š” ์ž‘์—…์˜ ๋‹จ์œ„์ด๋‹ค. ํ•˜๋‚˜์˜ ์ฟผ๋ฆฌ๋ฌธ์ด ์•„๋‹ˆ๋‹ค.

 

JPA ๋™์ž‘ - ์ €์žฅ

persist๋ฅผ ํ•œ๋‹ค๋ฉด, MemberDAO(EntityManager)๊ฐ€ Entity Object๋ฅผ JPA์— ์ „๋‹ฌํ•˜๊ณ , Entity Object๋Š” JPA๊ฐ€ ์•Œ์•„์„œ ๋ถ„์„ํ•˜๊ณ , INSERT๊นŒ์ง€ ํ•ด์ค€๋‹ค.

 

์ฐธ๊ณ ) DAO๋Š” Data Access Object๋กœ DB์— ์ ‘๊ทผํ•˜๋Š” ์—ญํ• ์„ ํ•˜๋Š” ๊ฐ์ฒด๋ฅผ ์˜๋ฏธํ•œ๋‹ค.

 

JPA ๋™์ž‘ - ์กฐํšŒ

MemberDAO(EntityManager)๊ฐ€ JPA์—๊ฒŒ id๋ฅผ ๋„˜๊ธฐ๋ฉด, JPA๊ฐ€ SELECT ํ•ด์„œ Entity Object๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

 

JPA๋Š” ํ‘œ์ค€ ๋ช…์„ธ

์šฐ๋ฆฌ๋Š” JPA๋ผ๋Š” ํ‘œ์ค€ ์ธํ„ฐํŽ˜์ด์Šค์— Hibernate๋ผ๋Š” ๊ตฌํ˜„์ฒด๋ฅผ ์“ด๋‹ค.

 

JPA์™€ CRUD

  • ์ €์žฅ: em.persist(member)
  • ์กฐํšŒ: Member member = em.find(Member.class, memberId)
  • ์ˆ˜์ •: member.setName(“๋ณ€๊ฒฝํ•  ์ด๋ฆ„”)
  • ์‚ญ์ œ: em.remove(member)

 

JPA์˜ ์„ฑ๋Šฅ ์ตœ์ ํ™” ๊ธฐ๋Šฅ

JPA๊ฐ€ ์ค‘๊ฐ„์— ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ๊ฐ€๋Šฅํ•œ ์ผ๋“ค์ด๋‹ค.

 

1์ฐจ ์บ์‹œ์™€ ๋™์ผ์„ฑ(identity) ๋ณด์žฅ

String memberId = "100";
Member m1 = jpa.find(Member.class, memberId); //SQL
Member m2 = jpa.find(Member.class, memberId); //์บ์‹œ
println(m1 == m2) //true

 

ํŠธ๋žœ์žญ์…˜์„ ์ง€์›ํ•˜๋Š” ์“ฐ๊ธฐ ์ง€์—ฐ - INSERT

1. ํŠธ๋žœ์žญ์…˜์„ ์ปค๋ฐ‹ํ•  ๋•Œ๊นŒ์ง€ INSERT SQL์„ ๋ชจ์•„์„œ ์ €์žฅ
2. JDBC BATCH SQL ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•ด์„œ ํ•œ๋ฒˆ์— SQL ์ „์†ก

transaction.begin(); // [ํŠธ๋žœ์žญ์…˜] ์‹œ์ž‘
em.persist(memberA);
em.persist(memberB);
em.persist(memberC);
//์—ฌ๊ธฐ๊นŒ์ง€ INSERT SQL์„ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ๋ณด๋‚ด์ง€ ์•Š๋Š”๋‹ค.
//์ปค๋ฐ‹ํ•˜๋Š” ์ˆœ๊ฐ„ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— INSERT SQL์„ ๋ชจ์•„์„œ ๋ณด๋‚ธ๋‹ค.
transaction.commit(); // [ํŠธ๋žœ์žญ์…˜] ์ปค๋ฐ‹

 

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

 

JPA ์‹œ์ž‘

JPA ๊ตฌ๋™ ๋ฐฉ์‹

 

์˜์†์„ฑ ๊ด€๋ฆฌ - ๋‚ด๋ถ€ ๋™์ž‘ ๋ฐฉ์‹

์˜์†์„ฑ ์ปจํ…์ŠคํŠธ

  • ์—”ํ‹ฐํ‹ฐ๋ฅผ ์˜๊ตฌ ์ €์žฅํ•˜๋Š” ํ™˜๊ฒฝ์ด๋ผ๋Š” ์˜๋ฏธ์ด๋‹ค.
  • em.persist(entity)๋Š” entity๋ฅผ DB๊ฐ€ ์•„๋‹ˆ๋ผ ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์— ์ €์žฅํ•˜๋Š” ๊ฒƒ์ด๋‹ค.
  • ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ๋Š” ๋…ผ๋ฆฌ์ ์ธ ๊ฐœ๋…์ด๋‹ค. 
  • ์—”ํ‹ฐํ‹ฐ ๋งค๋‹ˆ์ €๋ฅผ ํ†ตํ•ด์„œ ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์— ์ ‘๊ทผํ•œ๋‹ค.

 

์—”ํ‹ฐํ‹ฐ์˜ ์ƒ๋ช…์ฃผ๊ธฐ

  • ๋น„์˜์† (new/transient) : ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์™€ ์ „ํ˜€ ๊ด€๊ณ„๊ฐ€ ์—†๋Š” ์ƒˆ๋กœ์šด ์ƒํƒœ, JPA์™€ ๊ด€๊ณ„๊ฐ€ ์—†๋Š” ์ƒํƒœ์ด๋‹ค.
  • ์˜์† (managed) : ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์— ๊ด€๋ฆฌ๋˜๋Š” ์ƒํƒœ
  • ์ค€์˜์† (detached) : ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์— ์ €์žฅ๋˜์—ˆ๋‹ค๊ฐ€ ๋ถ„๋ฆฌ๋œ ์ƒํƒœ
  • ์‚ญ์ œ (removed) : ์‚ญ์ œ๋œ ์ƒํƒœ

 

๋น„์˜์†

// ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•˜๊ธฐ๋งŒ ํ•œ ์ƒํƒœ(๋น„์˜์†)
Member member = new Member();
member.setId("member1");
member.setUsername("ํšŒ์›1");

 

์˜์†

//๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•œ ์ƒํƒœ(๋น„์˜์†)
Member member = new Member();
member.setId("member1");
member.setUsername(“ํšŒ์›1”);

EntityManager em = emf.createEntityManager();
em.getTransaction().begin();

//๊ฐ์ฒด๋ฅผ ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์— ์ €์žฅํ•œ ์ƒํƒœ(์˜์†)
em.persist(member);

em.persist(member)๋ฅผ ํ–ˆ์„ ๋•Œ DB์— ์ฟผ๋ฆฌ๊ฐ€ ๋‚ ๋ผ๊ฐ€๋Š”๊ฒŒ ์•„๋‹ˆ๋ผ, ํŠธ๋žœ์žญ์…˜์ด ์ปค๋ฐ‹๋  ๋•Œ ์ฟผ๋ฆฌ๊ฐ€ ๋‚ ๋ผ๊ฐ„๋‹ค.

 

์ค€์˜์†

//์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์— ์žˆ๋Š” ์—”ํ‹ฐํ‹ฐ๋ฅผ ๋ถ„๋ฆฌํ•œ ์ƒํƒœ(์ค€์˜์†)
em.detach(member);

 

์‚ญ์ œ

//๊ฐ์ฒด๋ฅผ ์‚ญ์ œํ•œ ์ƒํƒœ(์‚ญ์ œ)
em.remove(member);

 

์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์˜ ์ด์ 

์—”ํ‹ฐํ‹ฐ ์กฐํšŒ, 1์ฐจ ์บ์‹œ

//์—”ํ‹ฐํ‹ฐ๋ฅผ ์ƒ์„ฑํ•œ ์ƒํƒœ(๋น„์˜์†)
Member member = new Member();
member.setId("member1");
member.setUsername("ํšŒ์›1");

//์—”ํ‹ฐํ‹ฐ๋ฅผ ์˜์†
em.persist(member);

EntityManager๋กœ ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์— ์ ‘๊ทผํ•˜์ง€๋งŒ, ์ž„์‹œ์ ์œผ๋กœ ๊ฐ™๋‹ค๊ณ  ์ทจ๊ธ‰ํ•œ๋‹ค.

 

1์ฐจ ์บ์‹œ์— ์žˆ๋Š” Entity๋ฅผ ์กฐํšŒํ•˜๋Š” ๊ฒฝ์šฐ

Member member = new Member();
member.setId("member1");
member.setUsername("ํšŒ์›1");

//1์ฐจ ์บ์‹œ์— ์ €์žฅ๋จ
em.persist(member);

//1์ฐจ ์บ์‹œ์—์„œ ์กฐํšŒ
Member findMember = em.find(Member.class, "member1");

์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์—๋Š” 1์ฐจ ์บ์‹œ๊ฐ€ ์žˆ์–ด์„œ find๋ฅผ ํ•˜๋ฉด, 1์ฐจ ์บ์‹œ์—์„œ ๋จผ์ € ์กฐํšŒํ•œ๋‹ค.

 

1์ฐจ ์บ์‹œ์— ์—†๋Š” Entity๋ฅผ ์กฐํšŒํ•˜๋Š” ๊ฒฝ์šฐ

Member findMember2 = em.find(Member.class, "member2");

1์ฐจ ์บ์‹œ์— ์—†๋Š” ๊ฒฝ์šฐ์—๋Š” ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—์„œ ์กฐํšŒํ•˜๊ณ , ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์˜ 1์ฐจ ์บ์‹œ์— ์ €์žฅํ•˜๊ณ  ๋ฐ˜ํ™˜ํ•œ๋‹ค.

๊ฐ™์€ ์—”ํ‹ฐํ‹ฐ๋ฅผ ์กฐํšŒํ•˜๋Š” ๊ฒฝ์šฐ, 1์ฐจ ์บ์‹œ์—์„œ ์กฐํšŒํ•˜๋ฏ€๋กœ ์ฒซ๋ฒˆ์งธ ๊ฒฝ์šฐ์™€ ๋‹ฌ๋ฆฌ ์ฟผ๋ฆฌ๋ฌธ์ด ๋‚ ๋ผ๊ฐ€์ง€ ์•Š๋Š”๋‹ค.

 

์ฐธ๊ณ ) 1์ฐจ ์บ์‹œ๋Š” ์‚ฌ์šฉ์ž ํ•˜๋‚˜(=ํŠธ๋žœ์žญ์…˜ ํ•˜๋‚˜)์—๋งŒ ์—ฐ๊ด€์ด ์žˆ๋‹ค.

 

์˜์†์„ฑ ์—”ํ‹ฐํ‹ฐ์˜ ๋™์ผ์„ฑ ๋ณด์žฅ

Member a = em.find(Member.class, "member1");
Member b = em.find(Member.class, "member1");

System.out.println(a == b); //๋™์ผ์„ฑ ๋น„๊ต true

 

ํŠธ๋žœ์žญ์…˜์„ ์ง€์›ํ•˜๋Š” ์“ฐ๊ธฐ ์ง€์—ฐ

EntityManager em = emf.createEntityManager();
EntityTransaction transaction = em.getTransaction();
//์—”ํ‹ฐํ‹ฐ ๋งค๋‹ˆ์ €๋Š” ๋ฐ์ดํ„ฐ ๋ณ€๊ฒฝ์‹œ ํŠธ๋žœ์žญ์…˜์„ ์‹œ์ž‘ํ•ด์•ผ ํ•œ๋‹ค.
transaction.begin(); // [ํŠธ๋žœ์žญ์…˜] ์‹œ์ž‘

em.persist(memberA);
em.persist(memberB);
//์—ฌ๊ธฐ๊นŒ์ง€ INSERT SQL์„ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ๋ณด๋‚ด์ง€ ์•Š๋Š”๋‹ค.

//์ปค๋ฐ‹ํ•˜๋Š” ์ˆœ๊ฐ„ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— INSERT SQL์„ ๋ณด๋‚ธ๋‹ค.
transaction.commit(); // [ํŠธ๋žœ์žญ์…˜] ์ปค๋ฐ‹

 

em.persist๋ฅผ ํ•˜๋ฉด, 1์ฐจ ์บ์‹œ์— ์—”ํ‹ฐํ‹ฐ๋ฅผ ์ €์žฅํ•  ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ, INSERT SQL์„ ์ƒ์„ฑํ•˜์—ฌ ์“ฐ๊ธฐ ์ง€์—ฐ SQL ์ €์žฅ์†Œ์— ์ €์žฅํ•œ๋‹ค. ๊ทธ ํ›„, transaction.commit()์ด ์ผ์–ด๋‚œ ๊ฒฝ์šฐ์— DB๋กœ ์ฟผ๋ฆฌ๋ฌธ์„ ๋‚ ๋ฆฐ๋‹ค. ๋งค๋ฒˆ DB๋กœ ์ฟผ๋ฆฌ๋ฌธ์„ ๋‚ ๋ฆฌ์ง€ ์•Š์œผ๋ฏ€๋กœ ์ตœ์ ํ™”์˜ ์žฅ์ ์ด ์žˆ๋‹ค.

 

์—”ํ‹ฐํ‹ฐ ์ˆ˜์ • - ๋ณ€๊ฒฝ ๊ฐ์ง€ = ๋”ํ‹ฐ ์ฒดํ‚น

EntityManager em = emf.createEntityManager();
EntityTransaction transaction = em.getTransaction();
transaction.begin(); // [ํŠธ๋žœ์žญ์…˜] ์‹œ์ž‘

// ์˜์† ์—”ํ‹ฐํ‹ฐ ์กฐํšŒ
Member memberA = em.find(Member.class, "memberA");

// ์˜์† ์—”ํ‹ฐํ‹ฐ ๋ฐ์ดํ„ฐ ์ˆ˜์ •
memberA.setUsername("hi");
memberA.setAge(10);

//em.update(member) ๋˜๋Š” em.persist(member)๊ฐ€ ์—†์–ด๋„ ๋ณ€๊ฒฝ๋‚ด์šฉ์ด ๋ฐ˜์˜๋จ
transaction.commit(); // [ํŠธ๋žœ์žญ์…˜] ์ปค๋ฐ‹

 

JPA๋Š” commit์ด ํ˜ธ์ถœ๋˜๋ฉด, flush()๊ฐ€ ํ˜ธ์ถœ๋˜๊ณ , ์—”ํ‹ฐํ‹ฐ์™€ ์Šค๋ƒ…์ƒท์„ ๋น„๊ตํ•œ๋‹ค. ๋น„๊ตํ•œ ๋‚ด์šฉ์„ ๋ฐ”ํƒ•์œผ๋กœ UPDATE ์ฟผ๋ฆฌ๋ฌธ์„ ์“ฐ๊ธฐ ์ง€์—ฐ SQL ์ €์žฅ์†Œ์— ์ €์žฅํ•œ๋‹ค. ์“ฐ๊ธฐ ์ง€์—ฐ SQL ์ €์žฅ์†Œ์— ์žˆ๋Š” ์ฟผ๋ฆฌ๋“ค์„ flushํ•˜๊ณ , commitํ•œ๋‹ค.

 

์ฐธ๊ณ ) ์Šค๋ƒ…์ƒท์€ ์ฝ์–ด์˜จ ์‹œ์ ์˜ ์ƒํƒœ๋ฅผ ์˜๋ฏธํ•œ๋‹ค.

 

ํ”Œ๋Ÿฌ์‹œ

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

 

์ฐธ๊ณ ) flush๋Š” ์ฟผ๋ฆฌ๋ฅผ ์ „์†กํ•˜๋Š” ์—ญํ• ์ด๊ณ  commit์€ ๋‚ด๋ถ€์ ์œผ๋กœ flush๋ฅผ ์ˆ˜ํ–‰ํ•œ ๋’ค ํŠธ๋žœ์žญ์…˜์„ ๋๋‚ด๋Š” ์—ญํ• 

 

์˜์†์„ฑ ์ปจํ…์ŠคํŠธ๋ฅผ ํ”Œ๋Ÿฌ์‹œํ•˜๋Š” ๋ฐฉ๋ฒ•

  • em.flush() - ์ง์ ‘ ํ˜ธ์ถœ
  • ํŠธ๋žœ์žญ์…˜ ์ปค๋ฐ‹ - ํ”Œ๋Ÿฌ์‹œ ์ž๋™ ํ˜ธ์ถœ
  • JPQL ์ฟผ๋ฆฌ ์‹คํ–‰ - ํ”Œ๋Ÿฌ์‹œ ์ž๋™ ํ˜ธ์ถœ

 

์ค€์˜์† ์ƒํƒœ๋กœ ๋งŒ๋“œ๋Š” ๋ฐฉ๋ฒ•

  • em.detach(entity) : ํŠน์ • ์—”ํ‹ฐํ‹ฐ๋งŒ ์ค€์˜์† ์ƒํƒœ๋กœ ์ „ํ™˜
  • em.clear() : ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ๋ฅผ ์™„์ „ํžˆ ์ดˆ๊ธฐํ™”
  • em.close() : ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ๋ฅผ ์ข…๋ฃŒ

 

์—”ํ‹ฐํ‹ฐ ๋งคํ•‘

๊ฐ์ฒด์™€ ํ…Œ์ด๋ธ” ๋งคํ•‘

@Entity

  • @Entity๊ฐ€ ๋ถ™์€ ํด๋ž˜์Šค๋Š” JPA๊ฐ€ ๊ด€๋ฆฌํ•˜๋Š” ์—”ํ‹ฐํ‹ฐ์ด๋‹ค. ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์˜ ํ…Œ์ด๋ธ”๊ณผ ๋งคํ•‘๋œ๋‹ค.
  • ํŒŒ๋ผ๋ฏธํ„ฐ๊ฐ€ ์—†๋Š” public ๋˜๋Š” protected ๊ธฐ๋ณธ ์ƒ์„ฑ์ž๊ฐ€ ํ•„์ˆ˜์ด๋‹ค.
  • final, enum, interface, inner ํด๋ž˜์Šค ์‚ฌ์šฉ ๋ถˆ๊ฐ€

 

@Table

  • @Table์€ ์—”ํ‹ฐํ‹ฐ์™€ ๋งคํ•‘ํ•  ํ…Œ์ด๋ธ” ์ง€์ •

 

๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์Šคํ‚ค๋งˆ ์ž๋™ ์ƒ์„ฑ

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

์ฐธ๊ณ ) DDL(Data Definition Language, ๋ฐ์ดํ„ฐ ์ •์˜์–ด)๋Š” ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๊ตฌ์กฐ ์ •์˜์— ์‚ฌ์šฉํ•˜๋Š” ์–ธ์–ด๋กœ, ํ…Œ์ด๋ธ”์ด๋‚˜ ์ปฌ๋Ÿผ ๋“ฑ์„ ์ƒ์„ฑ, ์ˆ˜์ •, ์‚ญ์ œํ•œ๋‹ค.

 

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

  • ์šด์˜ ์žฅ๋น„์—๋Š” ์ ˆ๋Œ€ create, create-drop, update ์‚ฌ์šฉํ•˜๋ฉด ์•ˆ๋œ๋‹ค.
  • ๊ฐœ๋ฐœ ์ดˆ๊ธฐ ๋‹จ๊ณ„๋Š” create ๋˜๋Š” update
  • ํ…Œ์ŠคํŠธ ์„œ๋ฒ„๋Š” update ๋˜๋Š” validate
  • ์Šคํ…Œ์ด์ง•๊ณผ ์šด์˜ ์„œ๋ฒ„๋Š” validate ๋˜๋Š” none

 

DDL ์ƒ์„ฑ ๊ธฐ๋Šฅ

  • @Column(nullable = false, length = 10) == ์ œ์•ฝ์กฐ๊ฑด ์ถ”๊ฐ€: ํ•„์ˆ˜, 10์ž ์ดˆ๊ณผX
  • DDL ์ƒ์„ฑ ๊ธฐ๋Šฅ์€ DDL์„ ์ž๋™ ์ƒ์„ฑํ•  ๋•Œ๋งŒ ์‚ฌ์šฉ๋˜๊ณ , JPA์˜ ์‹คํ–‰ ๋กœ์ง์—๋Š” ์˜ํ–ฅ์„ ์ฃผ์ง€ ์•Š๋Š”๋‹ค.

 

ํ•„๋“œ์™€ ์ปฌ๋Ÿผ ๋งคํ•‘

๋งคํ•‘ ์• ๋…ธํ…Œ์ด์…˜ ์ •๋ฆฌ

 

@Column

unique ๋Œ€์‹  @Table์—์„œ uniqueConstraints๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.

 

@Enumarated

USER, ADMIN๋งŒ ์žˆ๋‹ค๊ฐ€ ๋งจ ์•ž์— GUEST๋ฅผ ์ถ”๊ฐ€ํ•˜๋Š” ๊ฒฝ์šฐ, USER์™€ GUEST๊ฐ€ ๊ฐ™๊ฒŒ ๋˜๋Š” ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค. ๋”ฐ๋ผ์„œ, ๋ฌด์กฐ๊ฑด EnumType.STRING์„ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค.

 

@Temporal

๋‚ ์งœ ํƒ€์ž…(java.util.Date, java.util.Calendar)์„ ๋งคํ•‘ํ•  ๋•Œ ์‚ฌ์šฉ

 

์ฐธ๊ณ ) LocalDate, LocalDateTime์„ ์‚ฌ์šฉํ•˜๋ฉด, ์ตœ์‹  ํ•˜์ด๋ฒ„๋„ค์ดํŠธ ์•Œ์•„์„œ ๋‚ ์งœ ํƒ€์ž…์œผ๋กœ ๋งคํ•‘ํ•ด์ฃผ๊ธฐ ๋•Œ๋ฌธ์— @Temporal์€ ์š”์ฆ˜์€ ์“ฐ์ง€ ์•Š๋Š”๋‹ค.

 

@Lob

  • ๋Œ€ํ˜• ์˜ค๋ธŒ์ ํŠธ์— ์‚ฌ์šฉํ•œ๋‹ค.
  • @Lob์—๋Š” ์ง€์ •ํ•  ์ˆ˜ ์žˆ๋Š” ์†์„ฑ์ด ์—†๋‹ค.
  • ๋งคํ•‘ํ•˜๋Š” ํ•„๋“œ ํƒ€์ž…์ด ๋ฌธ์ž๋ฉด ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—์„œ CLOB๊ณผ ๋งคํ•‘๋˜๊ณ , ๋‚˜๋จธ์ง€๋Š” BLOB ๋งคํ•‘๋œ๋‹ค.
  • CLOB: String, char[], java.sql.CLOB
  • BLOB: byte[], java.sql. BLOB

 

๊ธฐ๋ณธ ํ‚ค ๋งคํ•‘

์ง์ ‘ ํ• ๋‹น

setId๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์›ํ•˜๋Š” ๊ฐ’์œผ๋กœ ์ง์ ‘ ํ• ๋‹น

 

์ž๋™ ์ƒ์„ฑ(@GeneratedValue)

  • IDENTITY: ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ๋งก๊ธด๋‹ค. MySQL์—์„œ๋Š” ์ฃผ๋กœ AUTO_INCREMENT๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค. IDENTITY์˜ ๋ฌธ์ œ๋Š” id๊ฐ’์˜ ์ƒ์„ฑ์„ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ๋งก๊ธฐ๊ธฐ ๋•Œ๋ฌธ์—, em.persist๋ฅผ ํ•ด์„œ 1์ฐจ ์บ์‹œ์— ์ €์žฅ์„ ํ•˜๋ ค๊ณ  ํ•ด๋„ id๊ฐ’์„ ๋ชจ๋ฅธ๋‹ค. ๊ทธ๋ž˜์„œ ์˜ˆ์™ธ์ ์œผ๋กœ IDENTITY ๊ฒฝ์šฐ์—๋งŒ em.persist๋ฅผ ํ•  ๋•Œ, ์ถ”๊ฐ€์ ์œผ๋กœ flush๋ฅผ ํ•ด์„œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—์„œ ์ƒ์„ฑ๋œ id ๊ฐ’์„ ๊ฐ€์ง€๊ณ  1์ฐจ ์บ์‹œ์— ์ €์žฅํ•œ๋‹ค.
@Entity
public class Member {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    ...
}
System.out.println("=================");
em.persist(member);
System.out.println("=================");

 

  • SEQUENCE: ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์‹œํ€€์Šค๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค. ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์‹œํ€€์Šค๋Š” ์œ ์ผํ•œ ๊ฐ’์„ ์ˆœ์„œ๋Œ€๋กœ ์ƒ์„ฑํ•˜๋Š” ํŠน๋ณ„ํ•œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์˜ค๋ธŒ์ ํŠธ์ด๋‹ค. @SequenceGenerator๊ฐ€ ํ•„์š”ํ•˜๊ณ , generator๋ฅผ ๋งคํ•‘ํ•œ๋‹ค. ORACLE ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—์„œ ์ฃผ๋กœ ์‚ฌ์šฉํ•œ๋‹ค.
@Entity
@SequenceGenerator(
        name = "MEMBER_SEQ_GENERATOR",
        sequenceName = "MEMBER_SEQ", //๋งคํ•‘ํ•  ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์‹œํ€€์Šค ์ด๋ฆ„
        initialValue = 1, allocationSize = 1)
public class Member {
    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "MEMBER_SEQ_GENERATOR")
    private Long id;

    ...
}

SEQUENCE๋„ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์‹œํ€€์Šค๋ฅผ ์ด์šฉํ•˜๊ธฐ ๋•Œ๋ฌธ์— 1์ฐจ ์บ์‹œ์— ์ €์žฅํ•˜๋ ค๊ณ  ํ•  ๋•Œ id๋ฅผ ๋ชจ๋ฅธ๋‹ค. flush๊นŒ์ง„ ์•„๋‹ˆ๊ณ , ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์‹œํ€€์Šค์—์„œ id๊ฐ’๋งŒ ๋ฐ›์•„์˜ค๊ณ  1์ฐจ ์บ์‹œ์— ์ €์žฅํ•œ๋‹ค. 

 

initialValue = 1, allocationSize = 50์ด๋ผ๋ฉด, ์ˆœ์„œ๋ฒˆํ˜ธ 50๊ฐœ๋ฅผ ๋ฏธ๋ฆฌ ๋ฐ›์•„์„œ ๋ฉ”๋ชจ๋ฆฌ์— ์ €์žฅ ํ›„ ํ•˜๋‚˜์”ฉ ์‚ฌ์šฉํ•œ๋‹ค.

 

์ฐธ๊ณ ) ddl-auto: create์—์„œ ์‚ญ์ œ๋˜๊ณ  ์ƒˆ๋กœ ์ƒ์„ฑ๋˜๋Š” ๊ฒƒ์€ ํ…Œ์ด๋ธ”์ด๋‹ค.

 

  • TABLE: ํ‚ค ์ƒ์„ฑ์šฉ ํ…Œ์ด๋ธ” ์‚ฌ์šฉ, ๋ชจ๋“  DB์—์„œ ์‚ฌ์šฉ, @TableGenerator ํ•„์š”
  • AUTO: ๋ฐฉ์–ธ์— ๋”ฐ๋ผ ์ž๋™ ์ง€์ •, ๊ธฐ๋ณธ๊ฐ’

 

๊ถŒ์žฅํ•˜๋Š” ์‹๋ณ„์ž ์ „๋žต

  • ๊ธฐ๋ณธ ํ‚ค ์ œ์•ฝ ์กฐ๊ฑด: null์ด ์•„๋‹ˆ์–ด์•ผ ํ•จ, ์œ ์ผ, ๋ณ€ํ•˜๋ฉด ์•ˆ๋œ๋‹ค.
  • ๋ฏธ๋ž˜๊นŒ์ง€ ์ด ์กฐ๊ฑด์„ ๋งŒ์กฑํ•˜๋Š” ์ž์—ฐํ‚ค๋Š” ์ฐพ๊ธฐ ์–ด๋ ต๋‹ค. ๋Œ€๋ฆฌํ‚ค(๋Œ€์ฒดํ‚ค)๋ฅผ ์‚ฌ์šฉํ•˜์ž.
  • ์˜ˆ๋ฅผ ๋“ค์–ด ์ฃผ๋ฏผ๋“ฑ๋ก๋ฒˆํ˜ธ๋„ ๊ธฐ๋ณธ ํ‚ค๋กœ ์ ์ ˆํ•˜๊ธฐ ์•Š๋‹ค.
  • ๊ถŒ์žฅ: ํƒ€์ž…์€ Longํ˜• + ๋Œ€์ฒดํ‚ค + ํ‚ค ์ƒ์„ฑ์ „๋žต ์‚ฌ์šฉ

๊ฒฐ๋ก ์€ Auto Increment, ์‹œํ€€์Šค ์˜ค๋ธŒ์ ํŠธ, uuid ์‚ฌ์šฉ ๊ถŒ์žฅ

 

์‹ค์ „ ์˜ˆ์ œ 1 - ์š”๊ตฌ์‚ฌํ•ญ ๋ถ„์„๊ณผ ๊ธฐ๋ณธ ๋งคํ•‘

๋„๋ฉ”์ธ ๋ชจ๋ธ ๋ถ„์„

  • ํšŒ์›๊ณผ ์ฃผ๋ฌธ์˜ ๊ด€๊ณ„ : ํ•œ ํšŒ์›์€ ์—ฌ๋Ÿฌ ๊ฐœ์˜ ์ฃผ๋ฌธ์„ ํ•  ์ˆ˜ ์žˆ๋‹ค. (์ผ๋Œ€๋‹ค)
  • ์ฃผ๋ฌธ๊ณผ ์ƒํ’ˆ์˜ ๊ด€๊ณ„ : ํ•œ ์ฃผ๋ฌธ์—๋Š” ์—ฌ๋Ÿฌ ๊ฐœ์˜ ์ƒํ’ˆ์ด ํฌํ•จ๋  ์ˆ˜ ์žˆ๋‹ค. ํ•œ ์ƒํ’ˆ์€ ์—ฌ๋Ÿฌ ์ฃผ๋ฌธ์— ํฌํ•จ๋  ์ˆ˜ ์žˆ๋‹ค. (๋‹ค๋Œ€๋‹ค) ์ธ๋ฐ, ์ฃผ๋ฌธ์ƒํ’ˆ์ด๋ผ๋Š” ๊ฒƒ์„ ๋„์ž…ํ•˜์—ฌ ์ผ๋Œ€๋‹ค, ๋‹ค๋Œ€์ผ๋กœ ํ’€์–ด๋‚ธ๋‹ค.

 

์—”ํ‹ฐํ‹ฐ ์„ค๊ณ„

 

ํ…Œ์ด๋ธ” ์„ค๊ณ„

 

ํ…Œ์ด๋ธ”์˜ ์™ธ๋ž˜ํ‚ค๋ฅผ ๊ฐ์ฒด์— ๊ทธ๋Œ€๋กœ ๊ฐ€์ ธ์˜ค๋Š” ๊ฒƒ์€ ์ข‹์€ ๊ฐ์ฒด ์ง€ํ–ฅ ์„ค๊ณ„ ๋ฐฉ๋ฒ•์ด ์•„๋‹ˆ๋‹ค. ๊ฐ์ฒด์—์„œ ๋ฐ”๋กœ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒƒ์ด ์ข‹๋‹ค.

 

์—ฐ๊ด€๊ด€๊ณ„ ๋งคํ•‘ ๊ธฐ์ดˆ

์˜ˆ์ œ ์‹œ๋‚˜๋ฆฌ์˜ค

  • ํšŒ์›๊ณผ ํŒ€์ด ์žˆ๋‹ค.
  • ํšŒ์›์€ ํ•˜๋‚˜์˜ ํŒ€์—๋งŒ ์†Œ์†๋  ์ˆ˜ ์žˆ๋‹ค.
  • ํšŒ์›๊ณผ ํŒ€์€ ๋‹ค๋Œ€์ผ ๊ด€๊ณ„๋‹ค.

 

๊ฐ์ฒด๋ฅผ ํ…Œ์ด๋ธ”์— ๋งž์ถ”์–ด ๋ชจ๋ธ๋ง (๊ฐ์ฒด ์—ฐ๊ด€๊ด€๊ณ„ ์—†์Œ)

ํšŒ์›๊ณผ ํŒ€์ด ๋‹ค๋Œ€์ผ ๊ด€๊ณ„์ผ ๋•Œ, ํšŒ์›์ด ์†ํ•œ ํŒ€์€ ํ•˜๋‚˜์ด๋ฏ€๋กœ, ํšŒ์›์—, ๋‹ค์‹œ ๋งํ•ด์„œ ๋‹ค ์ชฝ์— ์™ธ๋ž˜ํ‚ค๋ฅผ ๋‘๋Š” ๊ฒƒ์ด ์˜ณ๋‹ค.

 

Team team = new Team();
team.setName("๋ ˆ์•Œ ๋งˆ๋“œ๋ฆฌ๋“œ");
em.persist(team);

Member member = new Member();
member.setUsername("ํ˜ธ๋‚ ๋‘");
member.setTeamId(team.getId());
em.persist(member);

Member findMember = em.find(Member.class, member.getId());
Long findTeamId = findMember.getTeamId();
Team findTeam = em.find(Team.class, findTeamId);

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

 

๋‹จ๋ฐฉํ–ฅ ์—ฐ๊ด€๊ด€๊ณ„

๊ฐ์ฒด ์ง€ํ–ฅ ๋ชจ๋ธ๋ง (๊ฐ์ฒด ์—ฐ๊ด€๊ด€๊ณ„ ์‚ฌ์šฉ)

@Entity
public class Member {
    @Id
    @GeneratedValue
    @Column(name = "MEMBER_ID")
    private Long id;

    @Column(name = "USERNAME")
    private String username;

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

    ...
}
Team team = new Team();
team.setName("๋–ก์žŽ๋งˆ์„๋ฐฉ๋ฒ”๋Œ€");
em.persist(team);   // TEAM_ID๋ฅผ ์–ป์€ ์ƒํƒœ

Member member = new Member();
member.setUsername("์‹ ์งฑ๊ตฌ");
member.setTeam(team);   // MEMBER์˜ TEAM_ID์— team์˜ id๋ฅผ ์™ธ๋ž˜ํ‚ค๋กœ ์ €์žฅ
em.persist(member); // MEMBER_ID๋ฅผ ์–ป์€ ์ƒํƒœ

Member findMember = em.find(Member.class, member.getId());

System.out.println(findMember.getTeam().getName());

tx.commit();

Member์˜ team์„ ์ง€์ •ํ•˜๋ฉด TEAM์˜ TEAM_ID์™€ ๋ฐ”๋กœ ๋งคํ•‘๋˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋‹ค. MEMBER์˜ TEAM_ID์™€ ๋งคํ•‘๋˜๊ณ , ๊ทธ TEAM_ID๊ฐ€ TEAM์˜ TEAM_ID์™€ ๋งคํ•‘๋œ๋‹ค.

 

Member์˜ team์„ ๋ฐ”๊พธ๋ฉด, ๊ทธ team์˜ TEAM_ID๋กœ MEMBER์˜ TEAM_ID (FK)๊ฐ€ ๋ณ€๊ฒฝ๋œ๋‹ค.

 

์–‘๋ฐฉํ–ฅ ์—ฐ๊ด€๊ด€๊ณ„์™€ ์—ฐ๊ด€๊ด€๊ณ„์˜ ์ฃผ์ธ

์–‘๋ฐฉํ–ฅ ์—ฐ๊ด€๊ด€๊ณ„์™€ mappedBy

Member์—์„œ team์„ ๋ฐ”๊พธ๋Š” ๊ฒฝ์šฐ, MEMBER ํ…Œ์ด๋ธ”์—์„œ๋Š” ํ‚ค ๊ฐ’๋งŒ ๋ณ€๊ฒฝํ•˜๋ฉด ๋˜์ง€๋งŒ, ๊ฐ์ฒด์—์„œ๋Š” Member์—์„œ team์„ ๋ฐ”๊พผ๋‹ค๊ณ  ํ•ด์„œ Team์˜ members๊ฐ€ ๋ณ€ํ™”ํ•˜์ง€ ์•Š๋Š”๋‹ค. ์ถ”๊ฐ€ ์„ค์ •์ด ํ•„์š”ํ•˜๋‹ค. ์ด ์ถ”๊ฐ€ ์„ค์ •์ด mappedBy์ด๋‹ค.

 

@Entity
public class Team {

    @Id
    @GeneratedValue
    @Column(name = "TEAM_ID")
    private Long id;

    private String name;

    @OneToMany(mappedBy = "team")	// Member์—์„œ ๋งคํ•‘๋  ๋ณ€์ˆ˜๋ช…
    private List<Member> members = new ArrayList<>();
	
    ...
}
Team team = new Team();
team.setName("๋–ก์žŽ๋งˆ์„๋ฐฉ๋ฒ”๋Œ€");
em.persist(team);   // TEAM_ID๋ฅผ ์–ป์€ ์ƒํƒœ

Member member = new Member();
member.setUsername("์‹ ์งฑ๊ตฌ");
member.setTeam(team);   // MEMBER์˜ TEAM_ID์— team์˜ id๋ฅผ ์™ธ๋ž˜ํ‚ค๋กœ ์ €์žฅ
em.persist(member); // MEMBER_ID๋ฅผ ์–ป์€ ์ƒํƒœ

em.flush();
em.clear();

Member findMember = em.find(Member.class, member.getId());
List<Member> members = findMember.getTeam().getMembers();

for (Member m : members) {
    System.out.println("m = " + m.getUsername());
}

tx.commit();

 

๊ฐ์ฒด์™€ ํ…Œ์ด๋ธ”์ด ๊ด€๊ณ„๋ฅผ ๋งบ๋Š” ์ฐจ์ด

  • ๊ฐ์ฒด ์—ฐ๊ด€๊ด€๊ณ„ = 2๊ฐœ : ํšŒ์› -> ํŒ€ ์—ฐ๊ด€๊ด€๊ณ„ 1๊ฐœ(๋‹จ๋ฐฉํ–ฅ), ํŒ€ -> ํšŒ์› ์—ฐ๊ด€๊ด€๊ณ„ 1๊ฐœ(๋‹จ๋ฐฉํ–ฅ)
  • ํ…Œ์ด๋ธ” ์—ฐ๊ด€๊ด€๊ณ„ = 1๊ฐœ : ํšŒ์› <-> ํŒ€์˜ ์—ฐ๊ด€๊ด€๊ณ„ 1๊ฐœ(์–‘๋ฐฉํ–ฅ), MEMBER.TEAM_ID ์™ธ๋ž˜ ํ‚ค ํ•˜๋‚˜๋กœ ์–‘๋ฐฉํ–ฅ ์—ฐ๊ด€๊ด€๊ณ„๋ฅผ ๊ฐ€์ง„๋‹ค. (์–‘์ชฝ์œผ๋กœ ์กฐ์ธํ•  ์ˆ˜ ์žˆ๋‹ค)
SELECT *
FROM MEMBER M
JOIN TEAM T ON M.TEAM_ID = T.TEAM_ID

SELECT *
FROM TEAM T
JOIN MEMBER M ON T.TEAM_ID = M.TEAM_ID

 

 

์—ฐ๊ด€๊ด€๊ณ„์˜ ์ฃผ์ธ = ์–‘๋ฐฉํ–ฅ ๋งคํ•‘ ๊ทœ์น™

  • ๊ฐ์ฒด์˜ ๋‘ ๊ด€๊ณ„ ์ค‘ ํ•˜๋‚˜๋ฅผ ์—ฐ๊ด€๊ด€๊ณ„์˜ ์ฃผ์ธ์œผ๋กœ ์ง€์ •
  • ์—ฐ๊ด€๊ด€๊ณ„์˜ ์ฃผ์ธ๋งŒ์ด ์™ธ๋ž˜ ํ‚ค๋ฅผ ๊ด€๋ฆฌ(๋“ฑ๋ก, ์ˆ˜์ •)
  • ์ฃผ์ธ์ด ์•„๋‹Œ ์ชฝ์— mappedBy ์†์„ฑ์œผ๋กœ ์ฃผ์ธ์„ ์ง€์ •ํ•˜๊ณ , ์ฃผ์ธ์ด ์•„๋‹Œ ์ชฝ์€ ์ฝ๊ธฐ๋งŒ ๊ฐ€๋Šฅํ•˜๋‹ค.
  • ์™ธ๋ž˜ ํ‚ค๊ฐ€ ์žˆ๋Š” ๊ณณ, ๋‹ค ์ชฝ์˜ ๊ด€๋ จ ๋ฉค๋ฒ„๋ฅผ ์ฃผ์ธ์œผ๋กœ ์ •ํ•ด๋ผ. ์—ฌ๊ธฐ์„œ๋Š” Member.team

 

์–‘๋ฐฉํ–ฅ ๋งคํ•‘์‹œ ๊ฐ€์žฅ ๋งŽ์ด ํ•˜๋Š” ์‹ค์ˆ˜ (์—ฐ๊ด€๊ด€๊ณ„์˜ ์ฃผ์ธ์— ๊ฐ’์„ ์ž…๋ ฅํ•˜์ง€ ์•Š์Œ)

์—ฐ๊ด€๊ด€๊ณ„์˜ ์ฃผ์ธ์ด ์•„๋‹Œ ์ชฝ์— ๊ฐ’์„ ๋„ฃ์œผ๋ ค๊ณ  ํ•˜๋ฉด, ์—ฐ๊ด€๊ด€๊ณ„ ์ฃผ์ธ์ด ์•„๋‹Œ ์ชฝ์€ ์ฝ๊ธฐ ์ „์šฉ์ด๊ธฐ ๋•Œ๋ฌธ์— ๋ฐ˜์˜๋˜์ง€ ์•Š๋Š”๋‹ค. 

Team team = new Team();
team.setName("TeamA");
em.persist(team);

Member member = new Member();
member.setName("member1");

//์—ญ๋ฐฉํ–ฅ(์ฃผ์ธ์ด ์•„๋‹Œ ๋ฐฉํ–ฅ)๋งŒ ์—ฐ๊ด€๊ด€๊ณ„ ์„ค์ •
team.getMembers().add(member);

em.persist(member);

 

์ˆœ์ˆ˜ํ•œ ๊ฐ์ฒด ๊ด€๊ณ„๋ฅผ ๊ณ ๋ คํ•˜๋ฉด, ์–‘์ชฝ ๋‹ค ๊ฐ’์„ ์ž…๋ ฅํ•˜๋Š” ๊ฒƒ์ด ์ข‹๋‹ค

Team team = new Team();
team.setName("๋–ก์žŽ๋งˆ์„๋ฐฉ๋ฒ”๋Œ€");
em.persist(team);   // TEAM_ID๋ฅผ ์–ป์€ ์ƒํƒœ

Member member = new Member();
member.setUsername("์‹ ์งฑ๊ตฌ");

member.setTeam(team);   // MEMBER์˜ TEAM_ID์— team์˜ id๋ฅผ ์™ธ๋ž˜ํ‚ค๋กœ ์ €์žฅ
team.getMembers().add(member);

em.persist(member); // MEMBER_ID๋ฅผ ์–ป์€ ์ƒํƒœ

//em.flush();
//em.clear();

Member findMember = em.find(Member.class, member.getId());
List<Member> members = findMember.getTeam().getMembers();
  • ์ค‘๊ฐ„์— em.flush()๋ฅผ ํ•˜์ง€ ์•Š์„ ๊ฒฝ์šฐ, 1์ฐจ ์บ์‹œ์—์„œ๋Š” ์—ฐ๊ด€๊ด€๊ณ„ ๋งคํ•‘์ด ์™„๋ฒฝํ•˜๊ฒŒ ์ด๋ฃจ์–ด์ง€์ง€ ์•Š์•˜์œผ๋ฏ€๋กœ, em.find()๋กœ 1์ฐจ ์บ์‹œ์—์„œ ํ˜ธ์ถœํ•  ๋•Œ, Team์—๋Š” member๋“ค์ด ์—†๋Š” ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค. em.find๋กœ ์ƒˆ๋กœ ๋ฐ›์•„์™€์•ผ ํ•œ๋‹ค.
  • ์—ฐ๊ด€๊ด€๊ณ„ ๋ฉ”์†Œ๋“œ๋ฅผ ์ž‘์„ฑํ•˜๋Š” ๊ฒƒ์ด ์ข‹๋‹ค. Setter์™€ ๊ตฌ๋ณ„ํ•˜๊ธฐ ์œ„ํ•ด ์ด๋ฆ„์—์„œ change, add ๋“ฑ์„ ์‚ฌ์šฉํ•œ๋‹ค.
public void changeTeam(Team team) {
    this.team = team;
    team.getMembers().add(this);
}
  • ์–‘๋ฐฉํ–ฅ ๋งคํ•‘์‹œ์— ๋ฌดํ•œ ๋ฃจํ”„๋ฅผ ์กฐ์‹ฌํ•˜์ž.
    • toString() (= ๊ฐ์ฒด๋ฅผ toString ํ˜•์‹์œผ๋กœ ์ถœ๋ ฅํ•จ) + lombok -> toString์—์„œ ์—ฐ๊ด€๊ด€๊ณ„์— ์žˆ๋Š” ํ•„๋“œ๋“ค ์ œ๊ฑฐ
    • JSON ์ƒ์„ฑ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ -> @Controller์—์„œ ์—”ํ‹ฐํ‹ฐ๋ฅผ ์ง์ ‘ ๋ฐ˜ํ™˜ํ•˜์ง€ ์•Š๊ณ , DTO๋กœ ๋ณ€๊ฒฝํ•ด์„œ ๋ฐ˜ํ™˜ํ•  ๊ฒƒ

 

์–‘๋ฐฉํ–ฅ ๋งคํ•‘ ์ •๋ฆฌ

  • ๋‹จ๋ฐฉํ–ฅ ๋งคํ•‘์„ ์šฐ์„ ์ ์œผ๋กœ ์ž˜ ํ•˜๊ณ , ์–‘๋ฐฉํ–ฅ์€ ํ•„์š”ํ•  ๋•Œ(๋ฐ˜๋Œ€๋ฐฉํ–ฅ์œผ๋กœ ์กฐํšŒ๊ฐ€ ํ•„์š”ํ•  ๋•Œ) ์ถ”๊ฐ€ํ•ด๋„ ๋จ(ํ…Œ์ด๋ธ”์—๋Š” ์˜ํ–ฅ์ด ์—†์Œ)
  • JPQL์—์„œ ์—ญ๋ฐฉํ–ฅ์œผ๋กœ ํƒ์ƒ‰ํ•  ์ผ์ด ๋งŽ์Œ

 

์‹ค์ „ ์˜ˆ์ œ2 - ์—ฐ๊ด€๊ด€๊ณ„ ๋งคํ•‘ ์‹œ์ž‘

ํ…Œ์ด๋ธ” ๊ตฌ์กฐ

 

๊ฐ์ฒด ๊ตฌ์กฐ

(Member๋Š” Order๋ฅผ ์ฝ๊ธฐ๋งŒ ํ•˜๊ธฐ ๋•Œ๋ฌธ์—?) ์‹ค์ œ๋กœ๋Š” Member์— orders๋ฅผ ๋„ฃ๋Š” ๊ฒƒ์€ ์ข‹์ง€ ์•Š๋‹ค. ORDERS์— Member์— ๋Œ€ํ•œ FK๊ฐ€ ์žˆ์œผ๋ฏ€๋กœ, ์ฟผ๋ฆฌ๋ฌธ์„ ํ†ตํ•ด orders๋ฅผ ๊ตฌํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

 

์‹ค์ œ๋กœ List๋Š” ์—†์–ด๋„ ๋œ๋‹ค. ๋‹จ๋ฐฉํ–ฅ ์—ฐ๊ด€๊ด€๊ณ„๋งŒ ๋งŒ์กฑํ•ด๋„ ๋œ๋‹ค. ํŽธ๋ฆฌ๋ฅผ ์œ„ํ•ด ์„ค์ •ํ•ด๋‘” ๊ฒƒ ๋ฟ์ด๋‹ค. ํ•˜์ง€๋งŒ, JPQL ์ž‘์„ฑ์„ ์œ„ํ•ด์„œ ์–‘๋ฐฉํ–ฅ์„ ํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋งŽ๋‹ค.

 

Member

@Entity
public class Member {

    @Id
    @GeneratedValue
    @Column(name = "MEMBER_ID")
    private Long id;

    private String name;

    private String city;

    private String street;

    private String zipcode;

    @OneToMany(mappedBy = "member")
    private List<Order> orders = new ArrayList<>();

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getCity() {
        return city;
    }

    public void setCity(String city) {
        this.city = city;
    }

    public String getStreet() {
        return street;
    }

    public void setStreet(String street) {
        this.street = street;
    }

    public String getZipcode() {
        return zipcode;
    }

    public void setZipcode(String zipcode) {
        this.zipcode = zipcode;
    }

    public List<Order> getOrders() {
        return orders;
    }

    public void setOrders(List<Order> orders) {
        this.orders = orders;
    }
}

 

Order

@Entity
@Table(name = "ORDERS")
public class Order {

    @Id
    @GeneratedValue
    @Column(name = "ORDER_ID")
    private Long id;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "MEMBER_ID")
    private Member member;

    @OneToMany(mappedBy = "order")
    private List<OrderItem> orderItems = new ArrayList<>();

    private LocalDateTime orderDate;

    @Enumerated(EnumType.STRING)
    private OrderStatus status;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public Member getMember() {
        return member;
    }

    public void setMember(Member member) {
        this.member = member;
        member.getOrders().add(this);
    }

    public List<OrderItem> getOrderItems() {
        return orderItems;
    }

    public LocalDateTime getOrderDate() {
        return orderDate;
    }

    public void setOrderDate(LocalDateTime orderDate) {
        this.orderDate = orderDate;
    }

    public OrderStatus getStatus() {
        return status;
    }

    public void setStatus(OrderStatus status) {
        this.status = status;
    }

    // ์—ฐ๊ด€๊ด€๊ณ„ ๋ฉ”์†Œ๋“œ
    public void addOrderItem(OrderItem orderItem){
        orderItems.add(orderItem);
        orderItem.setOrder(this);
    }
}

 

OrderItem

@Entity
public class OrderItem {

    @Id
    @GeneratedValue
    @Column(name = "ORDER_ITEM_ID")
    private Long id;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "ITEM_ID")
    private Item item;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "ORDER_ID")
    private Order order;

    private int orderPrice;

    private int count;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public Item getItem() {
        return item;
    }

    public void setItem(Item item) {
        this.item = item;
    }

    public Order getOrder() {
        return order;
    }

    public void setOrder(Order order) {
        this.order = order;
    }

    public int getOrderPrice() {
        return orderPrice;
    }

    public void setOrderPrice(int orderPrice) {
        this.orderPrice = orderPrice;
    }

    public int getCount() {
        return count;
    }

    public void setCount(int count) {
        this.count = count;
    }
}

 

Item

@Entity
public class Item {

    @Id
    @GeneratedValue
    @Column(name = "ITEM_ID")
    private Long id;

    private String name;

    private int price;

    private int stockQuantity;
}

 

๊ฒฐ๋ก ์€ ๋‹จ๋ฐฉํ–ฅ ์—ฐ๊ด€๊ด€๊ณ„๋กœ ์ž‘์„ฑํ•˜๊ณ  ํ•„์š”ํ•˜๋ฉด ์–‘๋ฐฉํ–ฅ์„ ์ถ”๊ฐ€ํ•˜์ž.

 

๋‹ค์–‘ํ•œ ์—ฐ๊ด€๊ด€๊ณ„ ๋งคํ•‘

์—ฐ๊ด€๊ด€๊ณ„ ๋งคํ•‘ ์‹œ ๊ณ ๋ ค์‚ฌํ•ญ 3๊ฐ€์ง€

  • ๋‹ค์ค‘์„ฑ : @ManyToOne, @OneToMany, @OneToOne, @ManyToMany
  • ๋‹จ๋ฐฉํ–ฅ, ์–‘๋ฐฉํ–ฅ : ํ…Œ์ด๋ธ”์€ ์™ธ๋ž˜ ํ‚ค ํ•˜๋‚˜๋กœ ์–‘์ชฝ ์กฐ์ธ์ด ๊ฐ€๋Šฅํ•˜๋ฏ€๋กœ ๋ฐฉํ–ฅ์ด๋ผ๋Š” ๊ฐœ๋…์ด ์—†๋‹ค. ๊ฐ์ฒด๋Š” ์ฐธ์กฐ์šฉ ํ•„๋“œ๊ฐ€ ์žˆ๋Š” ์ชฝ์œผ๋กœ๋งŒ ์ฐธ์กฐ๊ฐ€ ๊ฐ€๋Šฅํ•œ๋ฐ, ํ•œ์ชฝ๋งŒ ์ฐธ์กฐํ•˜๋ฉด ๋‹จ๋ฐฉํ–ฅ, ์–‘์ชฝ์ด ์„œ๋กœ ์ฐธ์กฐํ•˜๋ฉด ์–‘๋ฐฉํ–ฅ(๋‹จ๋ฐฉํ–ฅ 2๊ฐœ)์ด๋‹ค.
  • ์—ฐ๊ด€๊ด€๊ณ„ ์ฃผ์ธ: ์™ธ๋ž˜ ํ‚ค๋ฅผ ๊ด€๋ฆฌํ•˜๋Š” ์ฐธ์กฐ, ์ฃผ์ธ์˜ ๋ฐ˜๋Œ€ํŽธ์€ ์™ธ๋ž˜ ํ‚ค์— ์˜ํ–ฅ์„ ์ฃผ์ง€ ์•Š๊ณ  ์ฝ๊ธฐ๋งŒ ๊ฐ€๋Šฅํ•˜๋‹ค.

 

๋‹ค๋Œ€์ผ [N:1]

๋‹ค๋Œ€์ผ ๋‹จ๋ฐฉํ–ฅ

@Entity
public class Member {
	...

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

 

๋‹ค๋Œ€์ผ ์–‘๋ฐฉํ–ฅ

@Entity
public class Member {
	...

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

    @OneToMany(mappedBy = "team")   // Member์—์„œ ๋งคํ•‘๋  ๋ณ€์ˆ˜๋ช…
    private List<Member> members = new ArrayList<>();
	
    ...
}

team์—์„œ member๋ฅผ ์ฐพ๋Š” ๋กœ์ง์ด ๋งŽ์„ ๋•Œ, ์œ„ ์ฝ”๋“œ๋ฅผ ์ถ”๊ฐ€ํ•˜์—ฌ ์–‘๋ฐฉํ–ฅ ๊ด€๊ณ„ ๋งคํ•‘ ์„ค์ •ํ•œ๋‹ค.

 

์ผ๋Œ€๋‹ค [1:N]

์—ฐ๊ด€๊ด€๊ณ„ ์ฃผ์ธ์ด ๋‹ค ์ชฝ์ด ์•„๋‹Œ ์ผ ์ชฝ์— ์žˆ๋‹ค๋Š” ์˜๋ฏธ์ด๋‹ค. ๋‹ค๋Œ€์ผ์„ ์‚ฌ์šฉํ•˜์ž.

 

์ผ๋Œ€์ผ [1:1]

  • ์ผ๋Œ€์ผ ๊ด€๊ณ„๋Š” ๊ทธ ๋ฐ˜๋Œ€๋„ ์ผ๋Œ€์ผ
  • ์ฃผ ํ…Œ์ด๋ธ”์ด๋‚˜ ๋Œ€์ƒ ํ…Œ์ด๋ธ” ์ค‘์— ์™ธ๋ž˜ ํ‚ค๋ฅผ ์ €์žฅํ•  ๊ณณ ์„ ํƒ ๊ฐ€๋Šฅ
  • ์™ธ๋ž˜ ํ‚ค์— ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์œ ๋‹ˆํฌ(UNI) ์ œ์•ฝ์กฐ๊ฑด ์ถ”๊ฐ€

์ฐธ๊ณ ) ์ฃผ ํ…Œ์ด๋ธ”์€ ์ฃผ๋กœ ์ ‘๊ทผํ•˜๋Š” ํ…Œ์ด๋ธ”

 

์ผ๋Œ€์ผ: ์ฃผ ํ…Œ์ด๋ธ”์— ์™ธ๋ž˜ ํ‚ค ๋‹จ๋ฐฉํ–ฅ

@Entity
public class Member {
	...

    @OneToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "LOCKER_ID")
    private Locker locker;
    
    ...
}

 

์ผ๋Œ€์ผ: ์ฃผ ํ…Œ์ด๋ธ”์— ์™ธ๋ž˜ ํ‚ค ์–‘๋ฐฉํ–ฅ

@Entity
public class Member {
	...

    @OneToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "LOCKER_ID")
    private Locker locker;
    
    ...
}
@Entity
public class Locker {
	...
    
    @OneToOne(fetch = FetchType.LAZY, mappedBy = "locker")
    private Member member;
}
  • ๋‹ค๋Œ€์ผ ์–‘๋ฐฉํ–ฅ ๋งคํ•‘ ์ฒ˜๋Ÿผ ์™ธ๋ž˜ ํ‚ค๊ฐ€ ์žˆ๋Š” ๊ณณ์ด ์—ฐ๊ด€๊ด€๊ณ„์˜ ์ฃผ์ธ
  • ๋ฐ˜๋Œ€ํŽธ์€ mappedBy ์ ์šฉ

 

์™ธ๋ž˜ ํ‚ค ์œ„์น˜์— ๋”ฐ๋ฅธ ๋น„๊ต

  •  ์ฃผ ํ…Œ์ด๋ธ”์— ์™ธ๋ž˜ ํ‚ค
    • ์ฃผ ๊ฐ์ฒด๊ฐ€ ๋Œ€์ƒ ๊ฐ์ฒด์˜ ์ฐธ์กฐ๋ฅผ ๊ฐ€์ง€๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ์ฃผ ํ…Œ์ด๋ธ”์— ์™ธ๋ž˜ ํ‚ค๋ฅผ ๋‘๊ณ  ๋Œ€์ƒ ํ…Œ์ด๋ธ”์„ ์ฐพ์Œ
    • ๊ฐ์ฒด์ง€ํ–ฅ ๊ฐœ๋ฐœ์ž๋“ค์ด ์„ ํ˜ธ
    • JPA ๋งคํ•‘ ํŽธ๋ฆฌ
    • ์žฅ์  : ์ฃผ ํ…Œ์ด๋ธ”๋งŒ ์กฐํšŒํ•ด๋„ ๋Œ€์ƒ ํ…Œ์ด๋ธ”์— ๋ฐ์ดํ„ฐ๊ฐ€ ์žˆ๋Š”์ง€ ํ™•์ธ ๊ฐ€๋Šฅ
    • ๋‹จ์  : ๊ฐ’์ด ์—†์œผ๋ฉด ์™ธ๋ž˜ ํ‚ค์— null ํ—ˆ์šฉ
  • ๋Œ€์ƒ ํ…Œ์ด๋ธ”์— ์™ธ๋ž˜ ํ‚ค
    • ๋Œ€์ƒ ํ…Œ์ด๋ธ”์— ์™ธ๋ž˜ํ‚ค๊ฐ€ ์กด์žฌ
    • ์ „ํ†ต์ ์ธ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๊ฐœ๋ฐœ์ž๊ฐ€ ์„ ํ˜ธ
    • ์žฅ์  : ์ฃผ ํ…Œ์ด๋ธ”๊ณผ ๋Œ€์ƒ ํ…Œ์ด๋ธ”์„ ์ผ๋Œ€์ผ์—์„œ ์ผ๋Œ€๋‹ค ๊ด€๊ณ„๋กœ ๋ณ€๊ฒฝํ•  ๋•Œ ํ…Œ์ด๋ธ” ๊ตฌ์กฐ ์œ ์ง€
    • ๋‹จ์  : ๋Œ€์ƒ ํ…Œ์ด๋ธ”์—์„œ ๊ฐ’์„ ์ฐพ์•„์•ผ ํ•จ. ํ”„๋ก์‹œ ๊ธฐ๋Šฅ์˜ ํ•œ๊ณ„๋กœ ์ง€์—ฐ๋กœ๋”ฉ์œผ๋กœ ์„ค์ •ํ•ด๋„ ํ•ญ์ƒ ์ฆ‰์‹œ ๋กœ๋”ฉ๋จ

 

๋‹ค๋Œ€๋‹ค [N:N]

์—ฐ๊ฒฐ ํ…Œ์ด๋ธ”์„ ์ถ”๊ฐ€ํ•ด์„œ ์ผ๋Œ€๋‹ค + ๋‹ค๋Œ€์ผ๋กœ ๋ฐ”๊พธ๋Š” ๊ฒƒ์ด ์ข‹๋‹ค.

 

@JoinColumn

์™ธ๋ž˜ ํ‚ค๋ฅผ ๋งคํ•‘ํ•  ๋•Œ ์‚ฌ์šฉํ•œ๋‹ค.

 

๊ณ ๊ธ‰ ๋งคํ•‘

์ƒ์†๊ด€๊ณ„ ๋งคํ•‘

  • ๊ด€๊ณ„ํ˜• ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋Š” ์ƒ์† ๊ด€๊ณ„๊ฐ€ ์—†์Œ
  • ์Šˆํผํƒ€์ž… ์„œ๋ธŒํƒ€์ž… ๊ด€๊ณ„๋ผ๋Š” ๋ชจ๋ธ๋ง ๊ธฐ๋ฒ•์ด ๊ฐ์ฒด ์ƒ์†๊ณผ ์œ ์‚ฌ
  • ์ƒ์†๊ด€๊ณ„ ๋งคํ•‘: ๊ฐ์ฒด์˜ ์ƒ์† ๊ตฌ์กฐ์™€ DB์˜ ์Šˆํผํƒ€์ž… ์„œ๋ธŒํƒ€์ž… ๊ด€๊ณ„๋ฅผ ๋งคํ•‘

 

์ฃผ์š” ์• ๋…ธํ…Œ์ด์…˜

  • @Inheritance(strategy=InheritanceType.XXX)
    • JOINED: ์กฐ์ธ ์ „๋žต
    • SINGLE_TABLE: ๋‹จ์ผ ํ…Œ์ด๋ธ” ์ „๋žต
    • TABLE_PER_CLASS: ๊ตฌํ˜„ ํด๋ž˜์Šค๋งˆ๋‹ค ํ…Œ์ด๋ธ” ์ „๋žต
  • @DiscriminatorColumn(name=“DTYPE”) : DB์—์„œ ์ž‘์—…์„ ๊ณ ๋ คํ•˜์—ฌ ๊ตฌ๋ถ„ ํ–‰ ์ƒ์„ฑ
  • @DiscriminatorValue(“XXX”) : DB์—์„œ ์ž‘์—…์„ ๊ณ ๋ คํ•˜์—ฌ ๊ตฌ๋ถ„ํ•  ๋•Œ ์‚ฌ์šฉํ•  ๊ฐ’ ์ง€์ •

 

์กฐ์ธ ์ „๋žต

  • ๊ฐ๊ฐ ํ…Œ์ด๋ธ”๋กœ ๋ณ€ํ™˜
  • ์žฅ์ 
    • ํ…Œ์ด๋ธ” ์ •๊ทœํ™”
    • ์™ธ๋ž˜ ํ‚ค ์ฐธ์กฐ ๋ฌด๊ฒฐ์„ฑ ์ œ์•ฝ์กฐ๊ฑด ํ™œ์šฉ ๊ฐ€๋Šฅ, ID๋ฅผ ์ด์šฉ ๊ฐ€๋Šฅ
    • ์ €์žฅ๊ณต๊ฐ„ ํšจ์œจํ™”
  • ๋‹จ์ 
    • ์กฐํšŒ์‹œ ์กฐ์ธ์„ ๋งŽ์ด ์‚ฌ์šฉํ•˜์—ฌ ์„ฑ๋Šฅ ์ €ํ•˜
    • ์กฐํšŒ ์ฟผ๋ฆฌ๊ฐ€ ๋ณต์žกํ•ฉ
    • ๋ฐ์ดํ„ฐ ์ €์žฅ์‹œ INSERT SQL 2๋ฒˆ ํ˜ธ์ถœ

@Entity
@Inheritance(strategy = InheritanceType.JOINED)
public abstract class Item {

    @Id
    @GeneratedValue
    private Long id;

    private String name;

    private int price;

}

ITEM, ALBUM, MOVIE, BOOK ํ…Œ์ด๋ธ” ๋ชจ๋‘ ์ƒ์„ฑ๋œ๋‹ค.

 

๋‹จ์ผ ํ…Œ์ด๋ธ” ์ „๋žต

  • ํ•œ ํ…Œ์ด๋ธ”์— ๋ชจ๋‘ ์ €์žฅํ•˜๊ณ , DTYPE์œผ๋กœ ๊ตฌ๋ถ„
  • ์žฅ์  
    • ์กฐ์ธ์ด ํ•„์š” ์—†์œผ๋ฏ€๋กœ ์ผ๋ฐ˜์ ์œผ๋กœ ์กฐํšŒ ์„ฑ๋Šฅ์ด ๋น ๋ฆ„
    • ์กฐํšŒ ์ฟผ๋ฆฌ๊ฐ€ ๋‹จ์ˆœํ•จ
  • ๋‹จ์ 
    • ์ž์‹ ์—”ํ‹ฐํ‹ฐ๊ฐ€ ๋งคํ•‘ํ•œ ์ปฌ๋Ÿผ์€ ๋ชจ๋‘ null ํ—ˆ์šฉ
    • ๋‹จ์ผ ํ…Œ์ด๋ธ”์— ๋ชจ๋“  ๊ฒƒ์„ ์ €์žฅํ•˜๋ฏ€๋กœ ํ…Œ์ด๋ธ”์ด ์ปค์งˆ ์ˆ˜ ์žˆ๊ณ , ์ƒํ™ฉ์— ๋”ฐ๋ผ์„œ ์กฐํšŒ ์„ฑ๋Šฅ์ด ์˜คํžˆ๋ ค ๋Š๋ ค์งˆ ์ˆ˜ ์žˆ๋‹ค.

@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn
public abstract class Item {

    @Id
    @GeneratedValue
    private Long id;

    private String name;

    private int price;

}

ITEM ํ…Œ์ด๋ธ”๋งŒ ์ƒ์„ฑ๋œ๋‹ค. @DiscriminatorColumn์ด ์—†์–ด๋„ ํ•„์š”ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ž๋™์œผ๋กœ ์ƒ์„ฑ๋œ๋‹ค.

 

๊ตฌํ˜„ ํด๋ž˜์Šค๋งˆ๋‹ค ํ…Œ์ด๋ธ” ์ „๋žต

์žฅ์ ์ด ์—†๋‹ค. ์‚ฌ์šฉํ•˜๋ฉด ์•ˆ๋œ๋‹ค.

@Entity
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class Item {

    @Id
    @GeneratedValue
    private Long id;

    private String name;

    private int price;
}

ITEM์ด ์ƒ์„ฑ๋˜์ง€ ์•Š๊ณ , MOVIE, ALBUM, BOOK๋งŒ ์ƒ์„ฑ๋œ๋‹ค.

Item.class๋กœ findํ•  ๊ฒฝ์šฐ์— ์„ธ ํ…Œ์ด๋ธ”์„ ๋ชจ๋‘ ํƒ์ƒ‰ํ•ด์•ผ ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์„ฑ๋Šฅ์ด ์ข‹์ง€ ์•Š๋‹ค.

 

@MappedSuperclass

id, name ๊ฐ™์€ ๊ณตํ†ต ๋งคํ•‘ ์ •๋ณด๊ฐ€ ํ•„์š”ํ•  ๋•Œ ์‚ฌ์šฉ

public class Member extends BaseEntity{
  • ์ƒ์†๊ด€๊ณ„ ๋งคํ•‘๊ณผ ๊ด€๋ จ์ด ์—†๋‹ค.
  • ์—”ํ‹ฐํ‹ฐ๊ฐ€ ์•„๋‹ˆ๊ธฐ ๋•Œ๋ฌธ์— ํ…Œ์ด๋ธ”๊ณผ ๋งคํ•‘๋˜์ง€ ์•Š๋Š”๋‹ค.
  • ๋ถ€๋ชจ ํด๋ž˜์Šค๋ฅผ ์ƒ์† ๋ฐ›๋Š” ์ž์‹ ํด๋ž˜์Šค์— ๋งคํ•‘ ์ •๋ณด๋งŒ ์ œ๊ณต๋œ๋‹ค.
  • ์กฐํšŒ, ๊ฒ€์ƒ‰ ๋ถˆ๊ฐ€ํ•˜๋‹ค, ์˜ˆ๋ฅผ ๋“ค์–ด em.find(BaseEntity) ๋ถˆ๊ฐ€๋Šฅ
  • ์ง์ ‘ ์ƒ์„ฑํ•ด์„œ ์‚ฌ์šฉํ•  ์ผ์ด ์—†์œผ๋ฏ€๋กœ ์ถ”์ƒ ํด๋ž˜์Šค๋กœ ์ƒ์„ฑํ•˜๋Š” ๊ฒƒ์ด ์ข‹๋‹ค.
  • ์ฃผ๋กœ ๋“ฑ๋ก์ผ, ์ˆ˜์ •์ผ, ๋“ฑ๋ก์ž, ์ˆ˜์ •์ž ๊ฐ™์€ ์ „์ฒด ์—”ํ‹ฐํ‹ฐ์—์„œ ๊ณตํ†ต์œผ๋กœ ์ ์šฉํ•˜๋Š” ์ •๋ณด๋ฅผ ์ œ๊ณตํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉํ•œ๋‹ค.
  • @Entity ํด๋ž˜์Šค๋Š” ์—”ํ‹ฐํ‹ฐ๋‚˜ @MappedSuperclass๋กœ ์ง€์ •ํ•œ ํด๋ž˜์Šค๋งŒ ์ƒ์† ๊ฐ€๋Šฅํ•˜๋‹ค.

 

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

ํ”„๋ก์‹œ

  • em.find() : 1์ฐจ ์บ์‹œ์— ์—†๋Š” ๊ฒฝ์šฐ, ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ ํ†ตํ•ด์„œ ์‹ค์ œ ์—”ํ‹ฐํ‹ฐ ๊ฐ์ฒด ์กฐํšŒ
  • em.getReference() : 1์ฐจ ์บ์‹œ์— ์—†๋Š” ๊ฒฝ์šฐ, ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์กฐํšŒ๋ฅผ ๋ฏธ๋ฃจ๋Š” ๊ฐ€์งœ(ํ”„๋ก์‹œ) ์—”ํ‹ฐํ‹ฐ ์กฐํšŒ

 

ํ”„๋ก์‹œ ํŠน์ง•

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

 

ํ”„๋ก์‹œ ๊ฐ์ฒด์˜ ์ดˆ๊ธฐํ™”

Member member = em.getReference(Member.class, "id1");
member.getName();

getName์„ ํ•˜๋ฉด, ํ”„๋ก์‹œ ๊ฐ์ฒด๊ฐ€ ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์— ์ดˆ๊ธฐํ™”๋ฅผ ์š”์ฒญํ•˜๊ณ , DB๋ฅผ ์กฐํšŒํ•ด์„œ ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์— ์‹ค์ œ ์—”ํ‹ฐํ‹ฐ๋ฅผ ์ƒ์„ฑํ•˜๊ณ  target์— ์—ฐ๊ฒฐํ•œ๋‹ค.

 

ํ”„๋ก์‹œ ํ™•์ธ

  • ํ”„๋ก์‹œ ์ธ์Šคํ„ด์Šค์˜ ์ดˆ๊ธฐํ™” ์—ฌ๋ถ€ ํ™•์ธ : emf.getPersistenceUnitUtil.isLoaded(PROXY)
  • ํ”„๋ก์‹œ ํด๋ž˜์Šค ํ™•์ธ ๋ฐฉ๋ฒ• : entity.getClass() ์ถœ๋ ฅ(..javasist.. or HibernateProxy…)
  • ํ”„๋ก์‹œ ๊ฐ•์ œ ์ดˆ๊ธฐํ™” : org.hibernate.Hibernate.initialize(PROXY);
  • ์ฐธ๊ณ : JPA ํ‘œ์ค€์€ ๊ฐ•์ œ ์ดˆ๊ธฐํ™” ์—†์Œ. ๊ฐ•์ œ ํ˜ธ์ถœ: member.getName() 

 

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

์ง€์—ฐ ๋กœ๋”ฉ

@Entity
public class Member extends BaseEntity{
	...

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

	...
}
Team team = new Team();
team.setName("1๋ฐ•2์ผ");
em.persist(team);

Member member = new Member();
member.setUsername("๊ฐ•ํ˜ธ๋™");
member.setTeam(team);
em.persist(member);

em.flush();
em.clear();

Member findMember = em.find(Member.class, member.getId());
System.out.println("=====================");
System.out.println(findMember.getTeam().getClass());
System.out.println("=====================");
System.out.println(findMember.getTeam().getName());
System.out.println(emf.getPersistenceUnitUtil().isLoaded(findMember.getTeam()));
System.out.println(findMember.getTeam().getClass());
System.out.println("=====================");

tx.commit();

์ง€์—ฐ ๋กœ๋”ฉ์œผ๋กœ ์„ค์ •ํ•œ ํ•„๋“œ์˜ ํด๋ž˜์Šค๋ฅผ ์กฐํšŒํ•˜๋ฉด, Proxy๋ผ๋Š” ๊ฒƒ์„ ์•Œ ์ˆ˜ ์žˆ๋‹ค.

์‹ค์ œ team์„ ์‚ฌ์šฉํ•˜๋Š” ์‹œ์ ์— ์ฟผ๋ฆฌ๋ฌธ์ด ๋‚ ๋ผ๊ฐ„๋‹ค.

์‹ค์ œ ๊ฐ์ฒด๋ฅผ ์ฐธ์กฐํ•ด๋„ Proxy ํƒ€์ž…์ด ์—ฌ์ „ํžˆ ์œ ์ง€๋œ๋‹ค๋Š” ๊ฒƒ์— ์œ ์˜ํ•˜์ž.

 

์ฆ‰์‹œ ๋กœ๋”ฉ

 

Team team = new Team();
team.setName("1๋ฐ•2์ผ");
em.persist(team);

Member member = new Member();
member.setUsername("๊ฐ•ํ˜ธ๋™");
member.setTeam(team);
em.persist(member);

em.flush();
em.clear();

Member findMember = em.find(Member.class, member.getId());
System.out.println("=====================");
System.out.println(findMember.getTeam().getClass());
System.out.println(findMember.getTeam().getName());
System.out.println("=====================");

tx.commit();

์‹ค์ œ team์„ ์‚ฌ์šฉํ•˜๊ธฐ ์ „์— ์ด๋ฏธ ์‹ค์ œ ๊ฐ์ฒด๋กœ ์ดˆ๊ธฐํ™”๋˜์–ด ์žˆ๋‹ค.

 

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

  • ๊ฐ€๊ธ‰์  ์ง€์—ฐ ๋กœ๋”ฉ๋งŒ ์‚ฌ์šฉ(ํŠนํžˆ ์‹ค๋ฌด์—์„œ), ์ฆ‰์‹œ ๋กœ๋”ฉ์„ ํ•˜๊ฒŒ ๋˜๋ฉด JOIN์„ ๋ฌด์ˆ˜ํžˆ ๋งŽ์ด ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์„ฑ๋Šฅ์ด ์•ˆ์ข‹์•„์ง.
  • ์ฆ‰์‹œ ๋กœ๋”ฉ์„ ์ ์šฉํ•˜๋ฉด ์˜ˆ์ƒํ•˜์ง€ ๋ชปํ•œ SQL์ด ๋ฐœ์ƒ
  • ์ฆ‰์‹œ ๋กœ๋”ฉ์€ JPQL์—์„œ N+1 ๋ฌธ์ œ๋ฅผ ์ผ์œผํ‚จ๋‹ค.
List<Member> members = em.createQuery("select m from Member m", Member.class).getResultList();

SQL๋ฌธ select * from Member์ด ์‹คํ–‰๋˜๊ณ , ์ฆ‰์‹œ ๋กœ๋”ฉ์— ์˜ํ•ด ๊ฐ member์˜ team๋“ค์ด ์ฑ„์›Œ์ ธ์•ผ ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ถ”๊ฐ€์ ์œผ๋กœ SQL๋ฌธ์ด ์‹คํ–‰๋œ๋‹ค. ์œ„ ์ฟผ๋ฆฌ ํ•˜๋‚˜๋กœ ์–ป์€ member๋“ค ๊ฐ๊ฐ์˜ team์„ ๋กœ๋”ฉํ•˜๊ธฐ ์œ„ํ•œ N๊ฐœ์˜ ์ฟผ๋ฆฌ๋ฌธ์ด ํ•„์š”ํ•˜๋‹ค๋Š” ์˜๋ฏธ์ด๋‹ค.

  • @ManyToOne, @OneToOne์€ ๊ธฐ๋ณธ์ด ์ฆ‰์‹œ ๋กœ๋”ฉ -> LAZY๋กœ ์„ค์ •
  • @OneToMany, @ManyToMany๋Š” ๊ธฐ๋ณธ์ด ์ง€์—ฐ ๋กœ๋”ฉ

 

์ฐธ๊ณ ) JOIN : ๋‘ ๊ฐœ์˜ ํ…Œ์ด๋ธ”์„ ์—ฎ์–ด์„œ ์›ํ•˜๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ์ถ”์ถœํ•  ๋•Œ ์‚ฌ์šฉํ•œ๋‹ค.

 

์˜์†์„ฑ ์ „์ด : CASCADE

  • ํŠน์ • ์—”ํ‹ฐํ‹ฐ๋ฅผ ์˜์† ์ƒํƒœ๋กœ ๋งŒ๋“ค ๋•Œ ์—ฐ๊ด€๋œ ์—”ํ‹ฐํ‹ฐ๋„ ํ•จ๊ป˜ ์˜์† ์ƒํƒœ๋กœ ๋งŒ๋“ค๊ณ  ์‹ถ์„ ๋•Œ ์‚ฌ์šฉํ•œ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด ๋ถ€๋ชจ ์—”ํ‹ฐํ‹ฐ๋ฅผ ์ €์žฅํ•  ๋•Œ ์ž์‹ ์—”ํ‹ฐํ‹ฐ๋„ ํ•จ๊ป˜ ์ €์žฅํ•˜๊ณ  ์‹ถ์„ ๋•Œ ์‚ฌ์šฉํ•œ๋‹ค.
  • ์˜์†์„ฑ ์ „์ด๋Š” ์—ฐ๊ด€๊ด€๊ณ„๋ฅผ ๋งคํ•‘ํ•˜๋Š” ๊ฒƒ๊ณผ๋Š” ์•„๋ฌด ๊ด€๋ จ์ด ์—†๋‹ค. ์—”ํ‹ฐํ‹ฐ๋ฅผ ์˜์†ํ™”ํ•  ๋•Œ ์—ฐ๊ด€๋œ ์—”ํ‹ฐํ‹ฐ๋„ ํ•จ๊ป˜ ์˜์†ํ™”ํ•˜๋Š” ํŽธ๋ฆฌํ•จ์„ ์ œ๊ณตํ•  ๋ฟ์ด๋‹ค.
  • ์˜ต์…˜์—์„œ ALL๊ณผ PERSIST๋ฅผ ์ฃผ๋กœ ์‚ฌ์šฉํ•œ๋‹ค.
  • ์†Œ์œ ์ž๊ฐ€ ํ•˜๋‚˜์ผ ๋•Œ, ์˜ˆ๋ฅผ ๋“ค์–ด Child๊ฐ€ Parent์™€๋งŒ ๊ด€๋ จ์ด ์žˆ์„ ๋•Œ๋งŒ ์‚ฌ์šฉํ•œ๋‹ค.
Child child1 = new Child();
Child child2 = new Child();

Parent parent = new Parent();
parent.addChild(child1);
parent.addChild(child2);

//            em.persist(child1);
//            em.persist(child2);
em.persist(parent);

List<Child> childList = em.find(Parent.class, parent.getId()).getChildList();
for (Child child : childList) {
    System.out.println("child = " + child);
}

tx.commit();

์œ„ ์ฝ”๋“œ์—์„œ parent ๊ฐ์ฒด์—๋Š” child ๊ฐ์ฒด๋“ค์ด ์ €์žฅ๋˜์–ด ์žˆ์ง€๋งŒ, em.find๋กœ ๊ฐ€์ ธ์˜จ ๊ฐ์ฒด์—๋Š” ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—์„œ๋Š” child๊ฐ€ ์—†์œผ๋ฏ€๋กœ ๋ฐ˜์˜๋˜์ง€ ์•Š๋Š”๋‹ค.

 

@Entity
public class Parent {
	...

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

    // ์—ฐ๊ด€๊ด€๊ณ„ ๋ฉ”์†Œ๋“œ
    public void addChild(Child child){
        child.setParent(this);
        childList.add(child);
    }
    
    ...
}

cascade๋ฅผ ์„ค์ •ํ•˜๋ฉด, parent๋งŒ persistํ•ด๋„ ์ปฌ๋ ‰์…˜์— ์žˆ๋Š” child๋“ค๊นŒ์ง€ persist ๋œ๋‹ค.

 

๊ณ ์•„ ๊ฐ์ฒด

  • ๋ถ€๋ชจ ์—”ํ‹ฐํ‹ฐ์™€ ์—ฐ๊ด€๊ด€๊ณ„๊ฐ€ ๋Š์–ด์ง„ ์ž์‹ ์—”ํ‹ฐํ‹ฐ๋ฅผ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—์„œ ์ž๋™ ์‚ญ์ œ์‹œํ‚จ๋‹ค.
  • ์ฐธ์กฐํ•˜๋Š” ๊ณณ์ด ํ•˜๋‚˜์ผ ๋•Œ ์‚ฌ์šฉํ•ด์•ผ ํ•จ, ์ฆ‰ ํŠน์ • ์—”ํ‹ฐํ‹ฐ๊ฐ€ ๊ฐœ์ธ ์†Œ์œ ํ•  ๋•Œ ์‚ฌ์šฉ
  • @OneToOne, @OneToMany๋งŒ ๊ฐ€๋Šฅ
@Entity
public class Parent {
	...
    
    @OneToMany(mappedBy = "parent", cascade = CascadeType.ALL, orphanRemoval = true)
    private List<Child> childList = new ArrayList<>();
	
    ...
}
Parent findParent = em.find(Parent.class, parent.getId());
findParent.getChildList().remove(0);

 

  • em.remove๋กœ ๋ถ€๋ชจ๋ฅผ ์‚ญ์ œํ•˜๋ฉด, ์ž์‹๋„ ์‚ญ์ œ๋œ๋‹ค.

 

์˜์†์„ฑ ์ „์ด + ๊ณ ์•„ ๊ฐ์ฒด

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

 

๊ฐ’ ํƒ€์ž…

JPA์˜ ๋ฐ์ดํ„ฐ ํƒ€์ž… ๋ถ„๋ฅ˜

  • ์—”ํ‹ฐํ‹ฐ ํƒ€์ž…
    • @Entity๋กœ ์ •์˜ํ•˜๋Š” ๊ฐ์ฒด
    • ๋ฐ์ดํ„ฐ๊ฐ€ ๋ณ€ํ•ด๋„ ์‹๋ณ„์ž(id)๋กœ ์ง€์†ํ•ด์„œ ์ถ”์  ๊ฐ€๋Šฅ
  • ๊ฐ’ ํƒ€์ž…
    • int, String์ฒ˜๋Ÿผ ๋‹จ์ˆœํžˆ ๊ฐ’์œผ๋กœ ์‚ฌ์šฉํ•˜๋Š” ์ž๋ฐ” ๊ธฐ๋ณธ ํƒ€์ž…์ด๋‚˜ ๊ฐ์ฒด
    • ์‹๋ณ„์ž๊ฐ€ ์—†๊ณ  ๊ฐ’๋งŒ ์žˆ์œผ๋ฏ€๋กœ ๋ณ€๊ฒฝ ์‹œ ์ถ”์  ๋ถˆ๊ฐ€, ์˜ˆ๋ฅผ ๋“ค์–ด ์ด๋ฆ„์„ ๋ณ€๊ฒฝํ•˜๋ฉด ์™„์ „ํžˆ ๋‹ค๋ฅธ ๊ฐ’์œผ๋กœ ๋Œ€์ฒด

 

๊ฐ’ ํƒ€์ž… ๋ถ„๋ฅ˜

  • ๊ธฐ๋ณธ๊ฐ’ ํƒ€์ž…
    • ์ž๋ฐ” ๊ธฐ๋ณธ ํƒ€์ž…(int, double)
    • ๋ž˜ํผ ํด๋ž˜์Šค(Integer, Long)
    • String
  • ์ž„๋ฒ ๋””๋“œ ํƒ€์ž…(embedded type, ๋ณตํ•ฉ๊ฐ’ ํƒ€์ž…) : ์ขŒํ‘œ, ์ฃผ์†Œ ๊ฐ’์ด ๋ฌถ์–ด์„œ ์“ฐ๊ณ  ์‹ถ์„ ๋•Œ
  • ์ปฌ๋ ‰์…˜ ๊ฐ’ ํƒ€์ž… : ์ž๋ฐ” ์ปฌ๋ ‰์…˜

 

๊ธฐ๋ณธ๊ฐ’ ํƒ€์ž…

  • ์ƒ๋ช… ์ฃผ๊ธฐ๋ฅผ ์—”ํ‹ฐํ‹ฐ์— ์˜์กด, ์˜ˆ๋ฅผ ๋“ค์–ด ํšŒ์›์„ ์‚ญ์ œํ•˜๋ฉด ์ด๋ฆ„, ๋‚˜์ด ํ•„๋“œ๋„ ํ•จ๊ป˜ ์‚ญ์ œ
  • ๊ฐ’ ํƒ€์ž…์€ ๊ณต์œ ํ•˜๋ฉด ์•ˆ๋จ, ์˜ˆ๋ฅผ ๋“ค์–ด ํšŒ์› ์ด๋ฆ„ ๋ณ€๊ฒฝ ์‹œ ๋‹ค๋ฅธ ํšŒ์›์˜ ์ด๋ฆ„๋„ ํ•จ๊ป˜ ๋ณ€๊ฒฝํ•˜๋ฉด ์•ˆ๋จ
  • ์ž๋ฐ” ๊ธฐ๋ณธ ํƒ€์ž…์€ ๊ณต์œ ๋˜์ง€ ์•Š๊ณ , ๊ฐ’์„ ๋ณต์‚ฌํ•จ, ํ•˜์ง€๋งŒ, ๋ž˜ํผ ํด๋ž˜์Šค๋‚˜ String ๊ฐ™์€ ํŠน์ˆ˜ํ•œ ํด๋ž˜์Šค๋Š” ๊ณต์œ  ๊ฐ€๋Šฅํ•œ ๊ฐ์ฒด์ด์ง€๋งŒ ๋ณ€๊ฒฝ ๋ถˆ๊ฐ€ 

 

์ž„๋ฒ ๋””๋“œ ํƒ€์ž…

  • ์ƒˆ๋กœ์šด ๊ฐ’ ํƒ€์ž…์„ ์ง์ ‘ ์ •์˜ํ•  ์ˆ˜ ์žˆ์Œ
  • ์ฃผ๋กœ ๊ธฐ๋ณธ ๊ฐ’ ํƒ€์ž…์„ ๋ชจ์•„์„œ ๋งŒ๋“ค์–ด์„œ ๋ณตํ•ฉ ๊ฐ’ ํƒ€์ž…์ด๋ผ๊ณ ๋„ ํ•จ
  • int, String๊ณผ ๊ฐ™์€ ๊ฐ’ ํƒ€์ž…, ๊ฐ’์œผ๋กœ ์‚ฌ์šฉํ•จ
  • @Embeddable๋กœ ์ •์˜ํ•˜๊ณ , @Embedded๋กœ ์‚ฌ์šฉ

 

์ž„๋ฒ ๋””๋“œ ํƒ€์ž…์˜ ์žฅ์ 

  • ์žฌ์‚ฌ์šฉ
  • ๋†’์€ ์‘์ง‘๋„
  • Period.isWork()์ฒ˜๋Ÿผ ํ•ด๋‹น ๊ฐ’ ํƒ€์ž…๋งŒ ์‚ฌ์šฉํ•˜๋Š” ์˜๋ฏธ ์žˆ๋Š” ๋ฉ”์†Œ๋“œ๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์žˆ์Œ.
  • ์ž„๋ฒ ๋””๋“œ ํƒ€์ž…์„ ํฌํ•จํ•œ ๋ชจ๋“  ๊ฐ’ ํƒ€์ž…์€, ๊ฐ’ ํƒ€์ž…์„ ์†Œ์œ ํ•œ ์—”ํ‹ฐํ‹ฐ์— ์ƒ๋ช…์ฃผ๊ธฐ๋ฅผ ์˜์กดํ•จ.

 

์ž„๋ฒ ๋””๋“œ ํƒ€์ž…๊ณผ ํ…Œ์ด๋ธ” ๋งคํ•‘

 

์ž„๋ฒ ๋””๋“œ ํƒ€์ž…๊ณผ ํ…Œ์ด๋ธ” ๋งคํ•‘

  • ์ž„๋ฒ ๋””๋“œ ํƒ€์ž…์€ ์—”ํ‹ฐํ‹ฐ์˜ ๊ฐ’์ผ ๋ฟ์ด๋‹ค.
  • ์ž„๋ฒ ๋””๋“œ ํƒ€์ž…์„ ์‚ฌ์šฉํ•˜๊ธฐ ์ „๊ณผ ํ›„์— ๋งคํ•‘ํ•˜๋Š” ํ…Œ์ด๋ธ”์€ ๊ฐ™๋‹ค.
  • ๊ฐ์ฒด์™€ ํ…Œ์ด๋ธ”์„ ์•„์ฃผ ์„ธ๋ฐ€ํ•˜๊ฒŒ ๋งคํ•‘ํ•˜๋Š” ๊ฒƒ์ด ๊ฐ€๋Šฅํ•˜๋‹ค.
  • ์ž˜ ์„ค๊ณ„ํ•œ ORM ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์€ ๋งคํ•‘ํ•œ ํ…Œ์ด๋ธ”์˜ ์ˆ˜๋ณด๋‹ค ํด๋ž˜์Šค์˜ ์ˆ˜๊ฐ€ ๋” ๋งŽ๋‹ค.
  • ์ž„๋ฒ ๋””๋“œ ํƒ€์ž… ํ•„๋“œ๋ฅผ null๋กœ ์ •์˜ํ•˜๋ฉด, ๊ทธ ์•ˆ์— ์žˆ๋Š” ํ•„๋“œ๋“ค๋„ null๋กœ ๋˜์–ด ํ…Œ์ด๋ธ”์— ๋ฐ˜์˜๋œ๋‹ค.

 

์ž„๋ฒ ๋””๋“œ ํƒ€์ž…๊ณผ ์—ฐ๊ด€๊ด€๊ณ„

 

@AttributeOverride: ์†์„ฑ ์žฌ์ •์˜

  • ํ•œ ์—”ํ‹ฐํ‹ฐ์—์„œ ์ž„๋ฒ ๋””๋“œ ํƒ€์ž…์„ ๋‘ ๋ฒˆ ์ด์ƒ ์‚ฌ์šฉํ•˜๋ฉด ์ค‘๋ณต๋˜๊ธฐ ๋•Œ๋ฌธ์— ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.
  • @AttributeOverrides, @AttributeOverride๋ฅผ ์‚ฌ์šฉํ•ด์„œ ์ปฌ๋Ÿผ ๋ช… ์†์„ฑ์„ ์žฌ์ •์˜ํ•˜๋ฏ€๋กœ์จ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋‹ค.
@Embedded
private Address homeAddress;

@Embedded
@AttributeOverrides({
        @AttributeOverride(name="city",
                column=@Column(name = "SCHOOL_CITY")),
        @AttributeOverride(name="street",
                column=@Column(name = "SCHOOL_STREET")),
        @AttributeOverride(name="zipcode",
                column=@Column(name = "SCHOOL_ZIPCODE"))})
private Address schoolAddress;

 

๊ฐ’ ํƒ€์ž…๊ณผ ๋ถˆ๋ณ€ ๊ฐ์ฒด

๊ฐ’ ํƒ€์ž… ๊ณต์œ  ์ฐธ์กฐ

  • ์ž„๋ฒ ๋””๋“œ ํƒ€์ž… ๊ฐ™์€ ๊ฐ’ ํƒ€์ž…์„ ์—ฌ๋Ÿฌ ์—”ํ‹ฐํ‹ฐ์—์„œ ๊ณต์œ ํ•˜๋ฉด ์œ„ํ—˜ํ•˜๋‹ค.

Address address = new Address("๋งˆ์‚ฐ", "๊ฑฐ๋ฆฌ1", "1000");

Member member1 = new Member();
member1.setUsername("๊ฐ•ํ˜ธ๋™");
member1.setHomeAddress(address);
em.persist(member1);

Member member2 = new Member();
member2.setUsername("์ด์ˆ˜๊ทผ");
member2.setHomeAddress(address);
em.persist(member2);

member1.getHomeAddress().setCity("์„œ์šธ");

  • ๊ฐ’ ํƒ€์ž…์˜ ์‹ค์ œ ์ธ์Šคํ„ด์Šค๋ฅผ ๊ณต์œ ํ•˜๋Š” ๊ฒƒ์€ ์œ„ํ—˜ํ•˜๋‹ค.
  • ์ƒˆ๋กœ์šด ์ธ์Šคํ„ด์Šค๋ฅผ ๋งŒ๋“ค๊ณ , ๊ฐ’์„ ๋ณต์‚ฌํ•ด์„œ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค.

 

๊ฐ์ฒด ํƒ€์ž…์˜ ํ•œ๊ณ„

  • ๊ณต์œ  ์ฐธ์กฐ๋กœ ์ธํ•ด ๋ฐœ์ƒํ•˜๋Š” ๋ถ€์ž‘์šฉ์„ ํ”ผํ•˜๊ธฐ ์œ„ํ•ด ํ•ญ์ƒ ๊ฐ’์„ ๋ณต์‚ฌํ•ด์„œ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค.
  • ๋ฌธ์ œ๋Š” ์ž„๋ฒ ๋””๋“œ ํƒ€์ž…์ฒ˜๋Ÿผ ์ง์ ‘ ์ •์˜ํ•œ ๊ฐ’ ํƒ€์ž…์€ ์ž๋ฐ”์˜ ๊ธฐ๋ณธ ํƒ€์ž…์ด ์•„๋‹ˆ๋ผ ๊ฐ์ฒด ํƒ€์ž…์ด๋‹ค.
  • ๊ฐ์ฒด ํƒ€์ž…์€ ์ฐธ์กฐ ๊ฐ’์„ ์ง์ ‘ ๋Œ€์ž…ํ•˜๋Š” ๊ฒƒ์„ ๋ง‰์„ ๋ฐฉ๋ฒ•์ด ์—†๋‹ค. ๊ฐ์ฒด์˜ ๊ณต์œ  ์ฐธ์กฐ๋Š” ํ”ผํ•  ์ˆ˜ ์—†๋‹ค.

 

๋ถˆ๋ณ€ ๊ฐ์ฒด

  • ๊ฐ์ฒด ํƒ€์ž…์„ ์ˆ˜์ •ํ•  ์ˆ˜ ์—†๊ฒŒ ๋งŒ๋“ค๋ฉด ๋ถ€์ž‘์šฉ์„ ์›์ฒœ ์ฐจ๋‹จํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ๊ฐ’ ํƒ€์ž…์€ ๋ถˆ๋ณ€ ๊ฐ์ฒด(immutable object)๋กœ ์„ค๊ณ„ํ•ด์•ผ ํ•œ๋‹ค. ๋ถˆ๋ณ€ ๊ฐ์ฒด๋ž€ ์ƒ์„ฑ ์‹œ์  ์ดํ›„ ์ ˆ๋Œ€ ๊ฐ’์„ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์—†๋Š” ๊ฐ์ฒด
  • ์ƒ์„ฑ์ž๋กœ ์ƒ์„ฑ ์‹œ์ ์—๋งŒ ๊ฐ’์„ ์„ค์ •ํ•˜๊ณ , Setter๋ฅผ ๋งŒ๋“ค์ง€ ์•Š๊ฑฐ๋‚˜ private์œผ๋กœ ์ƒ์„ฑํ•ด์•ผ ํ•œ๋‹ค. ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด, ๊ฐ’์„ ๋ฐ”๊พธ๊ณ  ์‹ถ์–ด๋„ ๋ฐ”๊พธ์ง€ ๋ชปํ•˜๋ฏ€๋กœ ์ƒˆ๋กœ ์ธ์Šคํ„ด์Šค๋ฅผ ๋งŒ๋“ค๊ณ , ๊ฐ’์„ ๋ณต์‚ฌํ•˜์—ฌ ์›ํ•˜๋Š” ๋ถ€๋ถ„๋งŒ ๋ฐ”๊พธ๊ณ  ๋„ฃ์–ด์ฃผ์–ด์•ผ ํ•œ๋‹ค.
Address address = new Address("๋งˆ์‚ฐ", "๊ฑฐ๋ฆฌ1", "1000");

Member member1 = new Member();
member1.setUsername("๊ฐ•ํ˜ธ๋™");
member1.setHomeAddress(address);
em.persist(member1);

Address newAddress = new Address("์„œ์šธ", member1.getHomeAddress().getStreet(), member1.getHomeAddress().getZipcode());
member1.setHomeAddress(newAddress);
  • ์ฐธ๊ณ ) Integer, String์€ ์ž๋ฐ”๊ฐ€ ์ œ๊ณตํ•˜๋Š” ๋Œ€ํ‘œ์ ์ธ ๋ถˆ๋ณ€ ๊ฐ์ฒด

 

๊ฐ’ ํƒ€์ž…์˜ ๋น„๊ต

  • ๋™์ผ์„ฑ(identity) ๋น„๊ต: ์ธ์Šคํ„ด์Šค์˜ ์ฐธ์กฐ ๊ฐ’์„ ๋น„๊ต, == ์‚ฌ์šฉ
  • ๋™๋“ฑ์„ฑ(equivalence) ๋น„๊ต: ์ธ์Šคํ„ด์Šค์˜ ๊ฐ’์„ ๋น„๊ต, ํด๋ž˜์Šค์—์„œ equals()๋ฅผ overrideํ•ด์„œ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค.
  • ๊ฐ’ ํƒ€์ž…์€ a.equals(b)๋ฅผ ์‚ฌ์šฉํ•ด์„œ ๋™๋“ฑ์„ฑ ๋น„๊ต๋ฅผ ํ•ด์•ผ ํ•จ

 

๊ฐ’ ํƒ€์ž… ์ปฌ๋ ‰์…˜ 

  • ์“ฐ์ง€ ์•Š๋Š” ๊ฒƒ์ด ์ข‹๋‹ค.
  • ๊ด€๊ณ„ํ˜• ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—๋Š” ์ปฌ๋ ‰์…˜์„ ์ €์žฅํ•  ์ˆ˜ ์—†๋‹ค. ๋”ฐ๋ผ์„œ ์ปฌ๋ ‰์…˜์„ ๋”ฐ๋กœ ํ…Œ์ด๋ธ”๋กœ ๋งŒ๋“ค์–ด์•ผ ํ•œ๋‹ค.
  • ๊ฐ’ ํƒ€์ž…์„ ํ•˜๋‚˜ ์ด์ƒ ์ €์žฅํ•  ๋•Œ ์‚ฌ์šฉํ•œ๋‹ค.
  • @ElementCollection, @CollectionTable ์‚ฌ์šฉํ•œ๋‹ค.
  • ๊ธฐ๋ณธ์ ์œผ๋กœ ์ง€์—ฐ ๋กœ๋”ฉ ์ „๋žต์„ ์‚ฌ์šฉํ•œ๋‹ค.

@ElementCollection
@CollectionTable(name = "FAVORITE_FOOD",
        joinColumns = @JoinColumn(name = "MEMBER_ID"))
@Column(name = "FOOD_NAME")
private Set<String> favoriteFoods = new HashSet<>();

@ElementCollection
@CollectionTable(name = "ADDRESS",
        joinColumns = @JoinColumn(name = "MEMBER_ID"))
private List<Address> addressHistory = new ArrayList<>();

 

๊ฐ’ ํƒ€์ž… ์ปฌ๋ ‰์…˜๋„ ๊ฐ’ ํƒ€์ž…์ด๋ฏ€๋กœ ์—”ํ‹ฐํ‹ฐ์˜ ์ƒ๋ช…์ฃผ๊ธฐ์™€ ์ผ์น˜ํ•œ๋‹ค. ๊ฐ’ ํƒ€์ž… ์ปฌ๋ ‰์…˜์€ ์˜์†์„ฑ ์ „์—(Cascade) + ๊ณ ์•„ ๊ฐ์ฒด ์ œ๊ฑฐ ๊ธฐ๋Šฅ์„ ํ•„์ˆ˜๋กœ ๊ฐ€์ง„๋‹ค๊ณ  ๋ณผ ์ˆ˜ ์žˆ๋‹ค.

 

๊ฐ’ ํƒ€์ž… ์ปฌ๋ ‰์…˜ ์ˆ˜์ •

findMember.getFavoriteFoods().remove("์น˜ํ‚จ");
findMember.getFavoriteFoods().add("์กฑ๋ฐœ");

findMember.getAddressHistory().remove(new Address("old1", "street1", "zipcode1"));
findMember.getAddressHistory().add(new Address("new1", "street1", "zipcode1"));

 

๊ฐ’ ํƒ€์ž… ์ปฌ๋ ‰์…˜์˜ ์ œ์•ฝ์‚ฌํ•ญ

  • ๊ฐ’ ํƒ€์ž…์€ ์—”ํ‹ฐํ‹ฐ์™€ ๋‹ค๋ฅด๊ฒŒ ์‹๋ณ„์ž ๊ฐœ๋…์ด ์—†๋‹ค.
  • ๊ฐ’์€ ๋ณ€๊ฒฝํ•˜๋ฉด ์ถ”์ ์ด ์–ด๋ ต๋‹ค.
  • ๊ฐ’ ํƒ€์ž… ์ปฌ๋ ‰์…˜์— ๋ณ€๊ฒฝ ์‚ฌํ•ญ์ด ๋ฐœ์ƒํ•˜๋ฉด, ์ฃผ์ธ ์—”ํ‹ฐํ‹ฐ์™€ ์—ฐ๊ด€๋œ ๋ชจ๋“  ๋ฐ์ดํ„ฐ๋ฅผ ์‚ญ์ œํ•˜๊ณ , ๊ฐ’ ํƒ€์ž… ์ปฌ๋ ‰์…˜์— ์žˆ๋Š” ํ˜„์žฌ ๊ฐ’์„ ๋ชจ๋‘ ๋‹ค์‹œ ์ €์žฅํ•œ๋‹ค. -> ๊ฒฐ๋ก ์ ์œผ๋กœ ์“ฐ๋ฉด ์•ˆ๋œ๋‹ค.
  • ๊ฐ’ ํƒ€์ž… ์ปฌ๋ ‰์…˜์„ ๋งคํ•‘ํ•˜๋Š” ํ…Œ์ด๋ธ”์€ ๋ชจ๋“  ์ปฌ๋Ÿผ์„ ๋ฌถ์–ด์„œ ๊ธฐ๋ณธ ํ‚ค๋ฅผ ๊ตฌ์„ฑํ•ด์•ผ ํ•œ๋‹ค: null ์ž…๋ ฅX, ์ค‘๋ณต ์ €์žฅX

 

๊ฐ’ ํƒ€์ž… ์ปฌ๋ ‰์…˜ ๋Œ€์•ˆ

  • ์‹ค๋ฌด์—์„œ๋Š” ์ƒํ™ฉ์— ๋”ฐ๋ผ ๊ฐ’ ํƒ€์ž… ์ปฌ๋ ‰์…˜ ๋Œ€์‹ ์— ์ผ๋Œ€๋‹ค ๊ด€๊ณ„๋ฅผ ๊ณ ๋ ค
  • ์ผ๋Œ€๋‹ค ๊ด€๊ณ„๋ฅผ ์œ„ํ•œ ์—”ํ‹ฐํ‹ฐ๋ฅผ ๋งŒ๋“ค๊ณ , ์—ฌ๊ธฐ์—์„œ ๊ฐ’ ํƒ€์ž…์„ ์‚ฌ์šฉ
  • ์˜์†์„ฑ ์ „์ด(Cascade) + ๊ณ ์•„ ๊ฐ์ฒด ์ œ๊ฑฐ๋ฅผ ์‚ฌ์šฉํ•ด์„œ ๊ฐ’ ํƒ€์ž… ์ปฌ๋ ‰์…˜ ์ฒ˜๋Ÿผ ์‚ฌ์šฉ
  • EX) AddressEntity
package hellojpa;

import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
import jakarta.persistence.Table;

@Entity
@Table(name = "ADDRESS")
public class AddressEntity {

    @Id
    @GeneratedValue
    private Long id;

    private Address address;

    public AddressEntity() {
    }

    public AddressEntity(String city, String street, String zipcode) {
        this.address = new Address(city, street, zipcode);
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public Address getAddress() {
        return address;
    }

    public void setAddress(Address address) {
        this.address = address;
    }
}

 

JPQL

JPA์˜ ์ฟผ๋ฆฌ ๋ฐฉ๋ฒ•

  • JPQL
    • JPA๊ฐ€ ์ œ๊ณตํ•˜๋Š” SQL์„ ์ถ”์ƒํ™”ํ•œ ๊ฐ์ฒด ์ง€ํ–ฅ ์ฟผ๋ฆฌ ์–ธ์–ด, SQL๊ณผ ๋ฌธ๋ฒ• ์œ ์‚ฌํ•˜๋‹ค.
    • JPQL์€ ์—”ํ‹ฐํ‹ฐ ๊ฐ์ฒด๋ฅผ ๋Œ€์ƒ์œผ๋กœ ์ฟผ๋ฆฌ, SQL์€ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ํ…Œ์ด๋ธ”์„ ๋Œ€์ƒ์œผ๋กœ ์ฟผ๋ฆฌํ•œ๋‹ค.
    • JPQL์€ ๊ฒฐ๊ตญ SQL๋กœ ๋ณ€ํ™˜๋œ๋‹ค.
List<Member> members = em.createQuery("select m from Member m where m.username like '%kim%'", Member.class).getResultList();

 

  • QueryDSL
  • SpringJdbcTemplate
    • JPA๋ฅผ ์šฐํšŒํ•ด์„œ SQL์„ ์‹คํ–‰ํ•  ๋•Œ๋Š” ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ๊ฐ€ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ๋ฐ˜์˜๋˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ๊ทธ ์ „์— ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ๋ฅผ ์ ์ ˆํ•œ ์‹œ์ ์— ๊ฐ•์ œ๋กœ ํ”Œ๋Ÿฌ์‹œํ•˜๋Š” ๊ฒƒ์ด ํ•„์š”ํ•˜๋‹ค.

 

JPQL

Java Persistence Query Language

 

JPQL ๋ฌธ๋ฒ•

  • select m from Member as m where m.age > 18
  • ์—”ํ‹ฐํ‹ฐ(Member)์™€ ์†์„ฑ(age)์€ ๋Œ€์†Œ๋ฌธ์ž๋ฅผ ๊ตฌ๋ถ„ํ•œ๋‹ค.
  • JPQL(SELECT, FROM, where) ํ‚ค์›Œ๋“œ๋Š” ๋Œ€์†Œ๋ฌธ์ž๋ฅผ ๊ตฌ๋ถ„ํ•˜์ง€ ์•Š๋Š”๋‹ค.
  • ํ…Œ์ด๋ธ” ์ด๋ฆ„์ด ์•„๋‹ˆ๋ผ ์—”ํ‹ฐํ‹ฐ ์ด๋ฆ„(Member)์„ ์‚ฌ์šฉํ•œ๋‹ค. ๊ธฐ๋ณธ์ ์œผ๋กœ ํด๋ž˜์Šค ์ด๋ฆ„์ด๋‹ค.
  • ๋ณ„์นญ(m)์€ ํ•„์ˆ˜ (as๋Š” ์ƒ๋žต๊ฐ€๋Šฅ)

 

์ง‘ํ•ฉ๊ณผ ์ •๋ ฌ

select
	COUNT(m), //ํšŒ์›์ˆ˜
	SUM(m.age), //๋‚˜์ด ํ•ฉ
	AVG(m.age), //ํ‰๊ท  ๋‚˜์ด
	MAX(m.age), //์ตœ๋Œ€ ๋‚˜์ด
	MIN(m.age) //์ตœ์†Œ ๋‚˜์ด
from Member m

GROUP BY, HAVING, ORDER BY

 

TypeQuery, Query

  • TypeQuery: ๋ฐ˜ํ™˜ ํƒ€์ž…์ด ๋ช…ํ™•ํ•  ๋•Œ ์‚ฌ์šฉ
TypedQuery<Member> query = em.createQuery("SELECT m FROM Member m", Member.class);
  • Query: ๋ฐ˜ํ™˜ ํƒ€์ž…์ด ๋ช…ํ™•ํ•˜์ง€ ์•Š์„ ๋•Œ ์‚ฌ์šฉ
Query query = em.createQuery("SELECT m.username, m.age from Member m");

 

 

๊ฒฐ๊ณผ ์กฐํšŒ API

  • query.getResultList(): ๊ฒฐ๊ณผ๊ฐ€ ํ•˜๋‚˜ ์ด์ƒ์ผ ๋•Œ, ๋ฆฌ์ŠคํŠธ ๋ฐ˜ํ™˜, ๊ฒฐ๊ณผ๊ฐ€ ์—†์œผ๋ฉด ๋นˆ ๋ฆฌ์ŠคํŠธ ๋ฐ˜ํ™˜
  • query.getSingleResult(): ๊ฒฐ๊ณผ๊ฐ€ ์ •ํ™•ํžˆ ํ•˜๋‚˜, ๋‹จ์ผ ๊ฐ์ฒด ๋ฐ˜ํ™˜
    • ๊ฒฐ๊ณผ๊ฐ€ ์—†์œผ๋ฉด: jakarta.persistence.NoResultException
    • ๋‘˜ ์ด์ƒ์ด๋ฉด: jakarta.persistence.NonUniqueResultException

 

ํ”„๋กœ์ ์…˜ (SELECT)

  • SELECT ์ ˆ์— ์กฐํšŒํ•  ๋Œ€์ƒ์„ ์ง€์ •ํ•˜๋Š” ๊ฒƒ
  • ํ”„๋กœ์ ์…˜ ๋Œ€์ƒ: ์—”ํ‹ฐํ‹ฐ, ์ž„๋ฒ ๋””๋“œ ํƒ€์ž…, ์Šค์นผ๋ผ ํƒ€์ž…(์ˆซ์ž, ๋ฌธ์ž๋“ฑ ๊ธฐ๋ณธ ๋ฐ์ดํ„ฐ ํƒ€์ž…)
  • SELECT m FROM Member m -> m์ด Member์ด๋ฏ€๋กœ ์—”ํ‹ฐํ‹ฐ ํ”„๋กœ์ ์…˜
  • SELECT m.team FROM Member m -> m๊ณผ ์—ฐ๊ด€๋œ team์ด ์—”ํ‹ฐํ‹ฐ์ด๋ฏ€๋กœ, ์—”ํ‹ฐํ‹ฐ ํ”„๋กœ์ ์…˜
  • SELECT m.address FROM Member m -> ์ž„๋ฒ ๋””๋“œ ํƒ€์ž… ํ”„๋กœ์ ์…˜
  • SELECT m.username, m.age FROM Member m -> ์Šค์นผ๋ผ ํƒ€์ž… ํ”„๋กœ์ ์…˜
  • DISTINCT๋กœ ์ค‘๋ณต ์ œ๊ฑฐํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ์กฐํšŒํ•œ ๊ฒฐ๊ณผ๋Š” ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ๋กœ ๊ด€๋ฆฌ๋œ๋‹ค.

 

ํ”„๋กœ์ ์…˜ - ์—ฌ๋Ÿฌ ๊ฐ’ ์กฐํšŒ

SELECT m.username, m.age FROM Member m

  • 1. Query ํƒ€์ž…์œผ๋กœ ์กฐํšŒ
  • 2. Object[] ํƒ€์ž…์œผ๋กœ ์กฐํšŒ
  • 3. new ๋ช…๋ น์–ด๋กœ ์กฐํšŒ
    • ๋‹จ์ˆœ ๊ฐ’์„ DTO๋กœ ๋ฐ”๋กœ ์กฐํšŒ
    • ํŒจํ‚ค์ง€ ๋ช…์„ ํฌํ•จํ•œ ์ „์ฒด ํด๋ž˜์Šค ๋ช… ์ž…๋ ฅ
    • ์ˆœ์„œ์™€ ํƒ€์ž…์ด ์ผ์น˜ํ•˜๋Š” ์ƒ์„ฑ์ž ํ•„์š”
List<MemberDto> resultList = em.createQuery("select new jpql.MemberDto(m.username, m.age) from Member m", MemberDto.class).getResultList();

 

ํŽ˜์ด์ง•

  • setFirstResult(int startPosition) : ์กฐํšŒ ์‹œ์ž‘ ์œ„์น˜ (0๋ถ€ํ„ฐ ์‹œ์ž‘)
  • setMaxResults(int maxResult) : ์กฐํšŒํ•  ๋ฐ์ดํ„ฐ ์ˆ˜
List<Member> resultList = em.createQuery("select m from Member m order by m.age desc", Member.class).setFirstResult(1).setMaxResults(10).getResultList();

 

์กฐ์ธ

id๊ฐ€ ๊ธฐ๋ณธ ์กฐ๊ฑด

  • ๋‚ด๋ถ€ ์กฐ์ธ: ๋‘ ํ…Œ์ด๋ธ”์„ ์กฐ์ธํ•  ๋•Œ, ๋‘ ํ…Œ์ด๋ธ”์— ๋ชจ๋‘ ์ง€์ •ํ•œ ์—ด์˜ ๋ฐ์ดํ„ฐ๊ฐ€ ์žˆ์–ด์•ผ ํ•œ๋‹ค. ์—†์œผ๋ฉด ๊ฒฐ๊ณผ์— ํฌํ•จ๋˜์ง€ ์•Š๋Š”๋‹ค.
SELECT m FROM Member m [INNER] JOIN m.team t

 

  • ์™ธ๋ถ€ ์กฐ์ธ: OUTER JOIN(์™ธ๋ถ€ ์กฐ์ธ)์€ ๋‘ ํ…Œ์ด๋ธ”์„ ์กฐ์ธํ•  ๋•Œ, 1๊ฐœ์˜ ํ…Œ์ด๋ธ”์—๋งŒ ๋ฐ์ดํ„ฐ๊ฐ€ ์žˆ์–ด๋„ ๊ฒฐ๊ณผ๊ฐ€ ๋‚˜์˜จ๋‹ค. 
SELECT m FROM Member m LEFT [OUTER] JOIN m.team t
  • ์„ธํƒ€ ์กฐ์ธ: ๊ณฑ์ง‘ํ•ฉ
select count(m) from Member m, Team t where m.username = t.name

 

์กฐ์ธ - ON ์ ˆ

  • ์กฐ์ธ ๋Œ€์ƒ ํ•„ํ„ฐ๋ง

์˜ˆ) ํšŒ์›๊ณผ ํŒ€์„ ์กฐ์ธํ•˜๋ฉด์„œ, ํŒ€ ์ด๋ฆ„์ด A์ธ ํŒ€๋งŒ ์กฐ์ธ

SELECT m, t FROM Member m LEFT JOIN m.team t on t.name = 'A'	//JPQL
SELECT m.*, t.* FROM Member m LEFT JOIN Team t ON m.TEAM_ID=t.id and t.name='A'	//SQL
  • ์—ฐ๊ด€๊ด€๊ณ„ ์—†๋Š” ์—”ํ‹ฐํ‹ฐ ์™ธ๋ถ€ ์กฐ์ธ ๊ฐ€๋Šฅ

์˜ˆ) ํšŒ์›์˜ ์ด๋ฆ„๊ณผ ํŒ€์˜ ์ด๋ฆ„์ด ๊ฐ™์€ ๋Œ€์ƒ ์™ธ๋ถ€ ์กฐ์ธ

SELECT m, t FROM Member m LEFT JOIN Team t on m.username = t.name	//JPQL
SELECT m.*, t.* FROM Member m LEFT JOIN Team t ON m.username = t.name	//SQL

 

์„œ๋ธŒ ์ฟผ๋ฆฌ

  • ๋‚˜์ด๊ฐ€ ํ‰๊ท ๋ณด๋‹ค ๋งŽ์€ ํšŒ์›
 select m from Member m where m.age > (select avg(m2.age) from Member m2)

 

  • ํ•œ ๊ฑด์ด๋ผ๋„ ์ฃผ๋ฌธํ•œ ๊ณ ๊ฐ
select m from Member m where (select count(o) from Order where o.member = m) > 0

 

์„œ๋ธŒ ์ฟผ๋ฆฌ ์ง€์› ํ•จ์ˆ˜

  • [NOT] EXISTS (subquery): ์„œ๋ธŒ์ฟผ๋ฆฌ์˜ ๊ฒฐ๊ณผ๊ฐ€ ์กด์žฌํ•˜๋ฉด ์ฐธ
  • {ALL | ANY | SOME} (subquery)
  • ALL ๋ชจ๋‘ ๋งŒ์กฑํ•˜๋ฉด ์ฐธ
  • ANY, SOME: ๊ฐ™์€ ์˜๋ฏธ, ์กฐ๊ฑด์„ ํ•˜๋‚˜๋ผ๋„ ๋งŒ์กฑํ•˜๋ฉด ์ฐธ
  • [NOT] IN (subquery): ์„œ๋ธŒ์ฟผ๋ฆฌ์˜ ๊ฒฐ๊ณผ ์ค‘ ํ•˜๋‚˜๋ผ๋„ ๊ฐ™์€ ๊ฒƒ์ด ์žˆ์œผ๋ฉด ์ฐธ

 

์˜ˆ์ œ

  • ํŒ€A ์†Œ์†์ธ ํšŒ์›
select m from Member m where exists (select t from m.team t where t.name = 'ํŒ€A')

 

  • ์ „์ฒด ์ƒํ’ˆ ๊ฐ๊ฐ์˜ ์žฌ๊ณ ๋ณด๋‹ค ์ฃผ๋ฌธ๋Ÿ‰์ด ๋งŽ์€ ์ฃผ๋ฌธ๋“ค
select o from Order o where o.orderAmount > ALL (select p.stockAmount from Product p)

 

  • ์–ด๋–ค ํŒ€์ด๋“  ํŒ€์— ์†Œ์†๋œ ํšŒ์›
select m from Member m where m.team = ANY (select t from Team t)

 

JPA ์„œ๋ธŒ ์ฟผ๋ฆฌ ํ•œ๊ณ„

FROM ์ ˆ์—์„œ ์„œ๋ธŒ ์ฟผ๋ฆฌ ๋ถˆ๊ฐ€๋Šฅ

 

JPQL ํƒ€์ž… ํ‘œํ˜„๊ณผ ๊ธฐํƒ€์‹

  • ๋ฌธ์ž: ‘HELLO’, ‘She’’s’
  • ์ˆซ์ž: 10L(Long), 10D(Double), 10F(Float)
  • Boolean: TRUE, FALSE
  • ENUM: jpabook.MemberType.Admin (ํŒจํ‚ค์ง€๋ช… ํฌํ•จ)
  • ์—”ํ‹ฐํ‹ฐ ํƒ€์ž…: TYPE(m) = Member (์ƒ์† ๊ด€๊ณ„์—์„œ ์‚ฌ์šฉ) ex) type(i) = Book
select m.username, 'Hello', TRUE from Member m where m.type jpql.MemberType.ADMIN

 

  • EXISTS, IN
  • AND, OR, NOT
  • =, >, >=, <, <=, <>
  • BETWEEN, LIKE, IS NULL

 

์กฐ๊ฑด์‹ - CASE ์‹

select
	case when m.age <= 10 then 'ํ•™์ƒ์š”๊ธˆ'
		when m.age >= 60 then '๊ฒฝ๋กœ์š”๊ธˆ'
		else '์ผ๋ฐ˜์š”๊ธˆ'
	end
from Member m
select
	case t.name
	when 'ํŒ€A' then '์ธ์„ผํ‹ฐ๋ธŒ110%'
	when 'ํŒ€B' then '์ธ์„ผํ‹ฐ๋ธŒ120%'
	else '์ธ์„ผํ‹ฐ๋ธŒ105%'
	end
from Team t

 

  • COALESCE : ํ•˜๋‚˜์”ฉ ์กฐํšŒํ•ด์„œ null์ด ์•„๋‹ˆ๋ฉด ์ฒซ ๋ฒˆ์งธ ๊ฐ’ ๋ฐ˜ํ™˜, null์ด๋ฉด ๋‘ ๋ฒˆ์งธ ๊ฐ’ ๋ฐ˜ํ™˜
select coalesce(m.username,'์ด๋ฆ„ ์—†๋Š” ํšŒ์›') from Member m
  • NULLIF : ๋‘ ๊ฐ’์ด ๊ฐ™์œผ๋ฉด null ๋ฐ˜ํ™˜, ๋‹ค๋ฅด๋ฉด ์ฒซ ๋ฒˆ์งธ ๊ฐ’ ๋ฐ˜ํ™˜
select NULLIF(m.username, '๊ด€๋ฆฌ์ž') from Member m

 

JPQL ๊ธฐ๋ณธ ํ•จ์ˆ˜

๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ƒ๊ด€์—†์ด ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•˜๋‹ค.

  • CONCAT
  • SUBSTRING
  • TRIM
  • LOWER, UPPER
  • LENGTH
  • LOCATE
  • ABS, SQRT, MOD
  • SIZE, INDEX(JPA ์šฉ๋„)

 

๊ฒฝ๋กœ ํ‘œํ˜„์‹

์ ์„ ์ฐ์–ด ๊ฐ์ฒด ๊ทธ๋ž˜ํ”„๋ฅผ ํƒ์ƒ‰ํ•˜๋Š” ๊ฒƒ

select m.username -> ์ƒํƒœ ํ•„๋“œ (๊ทธ๋ƒฅ ๊ฐ’)
	from Member m
	join m.team t -> ๋‹จ์ผ ๊ฐ’ ์—ฐ๊ด€ ํ•„๋“œ (์—”ํ‹ฐํ‹ฐ)
	join m.orders o -> ์ปฌ๋ ‰์…˜ ๊ฐ’ ์—ฐ๊ด€ ํ•„๋“œ (์—”ํ‹ฐํ‹ฐ ์ปฌ๋ ‰์…˜)
where t.name = 'ํŒ€A'

 

  • ์ƒํƒœ ํ•„๋“œ(state field): ๋‹จ์ˆœํžˆ ๊ฐ’์„ ์ €์žฅํ•˜๊ธฐ ์œ„ํ•œ ํ•„๋“œ (ex: m.username)
  • ์—ฐ๊ด€ ํ•„๋“œ(association field): ์—ฐ๊ด€๊ด€๊ณ„๋ฅผ ์œ„ํ•œ ํ•„๋“œ
    • ๋‹จ์ผ ๊ฐ’ ์—ฐ๊ด€ ํ•„๋“œ: @ManyToOne, @OneToOne, ๋Œ€์ƒ์ด ์—”ํ‹ฐํ‹ฐ(ex: m.team)
    • ์ปฌ๋ ‰์…˜ ๊ฐ’ ์—ฐ๊ด€ ํ•„๋“œ: @OneToMany, @ManyToMany, ๋Œ€์ƒ์ด ์ปฌ๋ ‰์…˜(ex: m.orders)

 

๊ฒฝ๋กœ ํ‘œํ˜„์‹ ํŠน์ง•

  • ์ƒํƒœ ํ•„๋“œ(state field): ๊ฒฝ๋กœ ํƒ์ƒ‰์˜ ๋, m.username์—์„œ ์ถ”๊ฐ€๋กœ .์„ ์ฐ์„ ์ˆ˜ ์—†์Œ
select m.username from Member m
  • ๋‹จ์ผ ๊ฐ’ ์—ฐ๊ด€ ๊ฒฝ๋กœ: ๋ฌต์‹œ์  ๋‚ด๋ถ€ ์กฐ์ธ(inner join) ๋ฐœ์ƒ, ํƒ์ƒ‰O
select o.member from Order o
  • ์ปฌ๋ ‰์…˜ ๊ฐ’ ์—ฐ๊ด€ ๊ฒฝ๋กœ: ๋ฌต์‹œ์  ๋‚ด๋ถ€ ์กฐ์ธ ๋ฐœ์ƒ, ํƒ์ƒ‰X, t.members์— ๋งŽ์€ member๋“ค์ด ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ์ถ”๊ฐ€๋กœ .์„ ์ฐ์„ ์ˆ˜ ์—†์Œ
    • FROM ์ ˆ์—์„œ ๋ช…์‹œ์  ์กฐ์ธ์„ ํ†ตํ•ด ๋ณ„์นญ์„ ์–ป์œผ๋ฉด ๋ณ„์นญ์„ ํ†ตํ•ด ํƒ์ƒ‰ ๊ฐ€๋Šฅ
select t.members from Team t

select m.username from Team t join t.members m

 

 

๋ช…์‹œ์  ์กฐ์ธ, ๋ฌต์‹œ์  ์กฐ์ธ

  • ๋ช…์‹œ์  ์กฐ์ธ: join ํ‚ค์›Œ๋“œ๋ฅผ ์ง์ ‘ ์‚ฌ์šฉ, select m from Member m join m.team t
  • ๋ฌต์‹œ์  ์กฐ์ธ: ๊ฒฝ๋กœ ํ‘œํ˜„์‹์— ์˜ํ•ด ๋ฌต์‹œ์ ์œผ๋กœ SQL ์กฐ์ธ ๋ฐœ์ƒ(๋‚ด๋ถ€ ์กฐ์ธ๋งŒ ๊ฐ€๋Šฅ), select m.team from Member m

 

์‹ค๋ฌด ์กฐ์–ธ

  • ๊ฐ€๊ธ‰์  ๋ฌต์‹œ์  ์กฐ์ธ ๋Œ€์‹ ์— ๋ช…์‹œ์  ์กฐ์ธ ์‚ฌ์šฉ
  • ์กฐ์ธ์€ SQL ํŠœ๋‹์— ์ค‘์š”ํ•œ ๋ถ€๋ถ„์ž„
  • ๋ฌต์‹œ์  ์กฐ์ธ์€ ์กฐ์ธ์ด ์ผ์–ด๋‚˜๋Š” ์ƒํ™ฉ์„ ํ•œ๋ˆˆ์— ํŒŒ์•…ํ•˜๊ธฐ ์–ด๋ ค์›€

 

ํŽ˜์น˜ ์กฐ์ธ(fetch join)

  • SQL ์กฐ์ธ ์ข…๋ฅ˜๊ฐ€ ์•„๋‹ˆ๋ผ JPQL์—์„œ ์„ฑ๋Šฅ ์ตœ์ ํ™”๋ฅผ ์œ„ํ•ด ์ œ๊ณตํ•˜๋Š” ๊ธฐ๋Šฅ
  • ์—ฐ๊ด€๋œ ์—”ํ‹ฐํ‹ฐ๋‚˜ ์ปฌ๋ ‰์…˜์„ SQL ํ•œ ๋ฒˆ์œผ๋กœ ํ•จ๊ป˜ ์กฐํšŒํ•˜๋Š” ๊ธฐ๋Šฅ
  • join fetch ๋ช…๋ น์–ด ์‚ฌ์šฉ
  • ํŽ˜์น˜ ์กฐ์ธ ::= [ LEFT [OUTER] | INNER ] JOIN FETCH ์กฐ์ธ๊ฒฝ๋กœ

 

์—”ํ‹ฐํ‹ฐ ํŽ˜์น˜ ์กฐ์ธ

ํšŒ์›์„ ์กฐํšŒํ•˜๋ฉด์„œ ์—ฐ๊ด€๋œ ํŒ€๋„ ํ•จ๊ป˜ ์กฐํšŒํ•˜๊ณ  ์‹ถ์„ ๋•Œ

select m from Member m join fetch m.team	// JPQL

SELECT M.*, T.* FROM MEMBER M				// SQL
INNER JOIN TEAM T ON M.TEAM_ID=T.ID

m๋งŒ ์กฐํšŒํ–ˆ์ง€๋งŒ, ํŒ€๋„ ๋‹ค๊ฐ™์ด ๊ฐ€์ ธ์˜จ๋‹ค.

 

Team team = new Team();
team.setName("๋–ก์žŽ๋งˆ์„๋ฐฉ๋ฒ”๋Œ€");
em.persist(team);

Team team2 = new Team();
team2.setName("์ฝฉ์žŽ๋งˆ์„๋ฐฉ๋ฒ”๋Œ€");
em.persist(team2);

Member member1 = new Member();
member1.setUsername("์งฑ๊ตฌ");
member1.addTeam(team);
em.persist(member1);

Member member2 = new Member();
member2.setUsername("๋งน๊ตฌ");
member2.addTeam(team);
em.persist(member2);

Member member3 = new Member();
member3.setUsername("์ฒ ์ˆ˜");
member3.addTeam(team2);
em.persist(member3);

em.flush();
em.clear();

List<Member> members = em.createQuery("select m from Member m join fetch m.team t", Member.class).getResultList();
for (Member member : members) {
    System.out.println("memberName = " + member.getUsername());
    System.out.println("teamName = " + member.getTeam().getName());
}

fetch join์„ ํ•˜๋ฉด ํšŒ์›์˜ ํŒ€์ด ํ”„๋ก์‹œ๊ฐ€ ์•„๋‹ˆ๋ผ ์‹ค์ œ ๊ฐ์ฒด์ด๊ธฐ ๋•Œ๋ฌธ์— ์ถ”๊ฐ€ ์ฟผ๋ฆฌ๋ฌธ์ด ๋ฐœ์ƒํ•˜์ง€ ์•Š๋Š”๋‹ค. ์ง€์—ฐ ๋กœ๋”ฉ๋ณด๋‹ค ํŽ˜์น˜ ์กฐ์ธ์ด ์šฐ์„ ์ด๋‹ค.

 

์ปฌ๋ ‰์…˜ ํŽ˜์น˜ ์กฐ์ธ

์ผ๋Œ€๋‹ค ํŽ˜์น˜ ์กฐ์ธ์ด๋ฏ€๋กœ ์ค‘๋ณต์ด ๋ฐœ์ƒํ•œ๋‹ค.

select t from Team t join fetch t.members where t.name = 'ํŒ€A'	// JPQL

SELECT T.*, M.*		// SQL
FROM TEAM T
INNER JOIN MEMBER M ON T.ID=M.TEAM_ID
WHERE T.NAME = 'ํŒ€A'

Team team1 = new Team();
team1.setName("๋–ก์žŽ๋งˆ์„๋ฐฉ๋ฒ”๋Œ€");
em.persist(team1);

Team team2 = new Team();
team2.setName("์ฝฉ์žŽ๋งˆ์„๋ฐฉ๋ฒ”๋Œ€");
em.persist(team2);

Member member1 = new Member();
member1.setUsername("์งฑ๊ตฌ");
member1.addTeam(team1);
em.persist(member1);

Member member2 = new Member();
member2.setUsername("๋งน๊ตฌ");
member2.addTeam(team1);
em.persist(member2);

Member member3 = new Member();
member3.setUsername("์ฒ ์ˆ˜");
member3.addTeam(team2);
em.persist(member3);

em.flush();
em.clear();

List<Team> teams = em.createQuery("select t from Team t join fetch t.members where t.name = '๋–ก์žŽ๋งˆ์„๋ฐฉ๋ฒ”๋Œ€'", Team.class).getResultList();
for (Team team : teams) {
    System.out.println("teamName = " + team.getName());
    for (Member member : team.getMembers()) {
        System.out.println("memberName = " + member.getUsername());
    }
}

ํ˜„ ๋ฒ„์ „์—์„œ๋Š” ์ž๋™์œผ๋กœ ์ค‘๋ณต์ด ์ƒ๋žต๋œ๋‹ค.

 

ํŽ˜์น˜ ์กฐ์ธ๊ณผ DISTINCT

JPQL์˜ DISTINCT 2๊ฐ€์ง€ ๊ธฐ๋Šฅ ์ œ๊ณต

  • SQL์— DISTINCT๋ฅผ ์ถ”๊ฐ€
  • ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ์—”ํ‹ฐํ‹ฐ ์ค‘๋ณต ์ œ๊ฑฐ

 

ํŽ˜์น˜ ์กฐ์ธ๊ณผ ์ผ๋ฐ˜ ์กฐ์ธ์˜ ์ฐจ์ด

์ผ๋ฐ˜ ์กฐ์ธ

์ผ๋ฐ˜ ์กฐ์ธ ์‹คํ–‰์‹œ JPQL์€ ๊ฒฐ๊ณผ๋ฅผ ๋ฐ˜ํ™˜ํ•  ๋•Œ ์—ฐ๊ด€๊ด€๊ณ„๋ฅผ ๊ณ ๋ คํ•˜์ง€ ์•Š๊ณ , ๋‹จ์ง€ SELECT ์ ˆ์— ์ง€์ •ํ•œ ์—”ํ‹ฐํ‹ฐ๋งŒ ๊ฐ€์ ธ์˜ฌ ๋ฟ์ด๋‹ค. ์•„๋ž˜ ๊ฒฝ์šฐ์— ํŒ€ ์—”ํ‹ฐํ‹ฐ๋งŒ ๊ฐ€์ ธ์˜ค๊ณ , ํšŒ์› ์—”ํ‹ฐํ‹ฐ๋Š” ๊ฐ€์ ธ์˜ค์ง€ ์•Š๋Š”๋‹ค.

 

ํŽ˜์น˜ ์กฐ์ธ

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

 

ํŽ˜์น˜ ์กฐ์ธ์˜ ํŠน์ง•๊ณผ ํ•œ๊ณ„

๊ฐ์ฒด ๊ทธ๋ž˜ํ”„๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ Team์—์„œ members์— ์ ‘๊ทผํ•  ๋•Œ, ๋‹ค ์กฐํšŒ๋˜๋Š” ๊ฒƒ์„ ๋ชฉ์ ์œผ๋กœ ํ•œ๋‹ค.

  • ํŽ˜์น˜ ์กฐ์ธ ๋Œ€์ƒ์—๋Š” ๋ณ„์นญ์„ ์ค„ ์ˆ˜ ์—†๋‹ค. ํ•˜์ด๋ฒ„๋„ค์ดํŠธ๋Š” ๊ฐ€๋Šฅํ•˜์ง€๋งŒ, ๊ฐ€๊ธ‰์ ์ด๋ฉด ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š” ๊ฒƒ์„ ๊ถŒ์žฅํ•œ๋‹ค.
select t from Team t join fetch t.members m

select t from Team t join fetch t.members
  • ๋‘˜ ์ด์ƒ์˜ ์ปฌ๋ ‰์…˜์€ ํŽ˜์น˜ ์กฐ์ธํ•  ์ˆ˜ ์—†๋‹ค. ์ผ๋Œ€๋‹ค+๋‹ค๋Œ€๋‹ค
  • ์ปฌ๋ ‰์…˜์„ ํŽ˜์น˜ ์กฐ์ธํ•˜๋ฉด ํŽ˜์ด์ง• API(setFirstResult, setMaxResults)๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๋‹ค.
    • ์ผ๋Œ€์ผ, ๋‹ค๋Œ€์ผ ๊ฐ™์€ ๋‹จ์ผ ๊ฐ’ ์—ฐ๊ด€ ํ•„๋“œ๋“ค์€ ํŽ˜์น˜ ์กฐ์ธํ•ด๋„ ํŽ˜์ด์ง• ๊ฐ€๋Šฅ
    • ์ปฌ๋ ‰์…˜ ํŽ˜์น˜ ์กฐ์ธ์€ ์œ„์˜ ์˜ˆ์—์„œ setMaxResults๋ฅผ 1๋กœ ์ง€์ •ํ•  ๋•Œ, ํŒ€A์˜ ํšŒ์›2๊ฐ€ ๋ฐ˜์˜์ด ์•ˆ๋˜๋Š” ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒ.
    • ํ•˜์ด๋ฒ„๋„ค์ดํŠธ๋Š” ๊ฒฝ๊ณ  ๋กœ๊ทธ๋ฅผ ๋‚จ๊ธฐ๊ณ  ๋ฉ”๋ชจ๋ฆฌ์—์„œ ํŽ˜์ด์ง•(๋งค์šฐ ์œ„ํ—˜)

 

์ปฌ๋ ‰์…˜ ํŽ˜์น˜ ์กฐ์ธ ํ•ด๊ฒฐ

์ˆœ์„œ ๋ฐ”๊พธ๊ธฐ

select t from Team t join fetch t.members

=> select m from Member m join fetch m.team t

 

BatchSize ์ง€์ •

<property name="hibernate.default_batch_fetch_size" value="100"/>
List<Team> teams = em.createQuery("select t from Team t", Team.class).setFirstResult(0).setMaxResults(2).getResultList();
System.out.println("result size = " + teams.size());
for (Team team : teams) {
    System.out.println("teamName = " + team.getName());
    for (Member member : team.getMembers()) {
        System.out.println("memberName = " + member.getUsername());
    }
}

team๋งˆ๋‹ค members๋ฅผ ๊ฐ€์ ธ์˜ค๊ธฐ ์œ„ํ•ด ์ฟผ๋ฆฌ๋ฌธ์„ ๋˜ team ์ˆ˜๋งŒํผ ๋ณด๋‚ด์•ผ ํ•œ๋‹ค. BatchSize๋ฅผ ์ง€์ •ํ•˜๋ฉด, members์™€ ๊ด€๋ จ๋œ TEAM_ID ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ๋‹ค๋ฅธ TEAM_ID๋“ค๋„ ์ง€์ •ํ•œ size๋งŒํผ ๊ฐ™์ด ํ•œ ์ฟผ๋ฆฌ๋ฌธ์— ์ง€์ •ํ•ด์„œ ๋ณด๋‚ด๊ธฐ ๋•Œ๋ฌธ์— ์ตœ์ข…์ ์œผ๋กœ ๋ณด๋‚ด๊ฒŒ ๋˜๋Š” ์ฟผ๋ฆฌ๋ฌธ์˜ ์ˆ˜๊ฐ€ ์ค„์–ด๋“ค๊ฒŒ ๋œ๋‹ค.

 

์ •๋ฆฌ

  • ํŽ˜์น˜ ์กฐ์ธ์€ ๊ฐ์ฒด ๊ทธ๋ž˜ํ”„๋ฅผ ์œ ์ง€ํ•˜๋ฉด์„œ ์ฐพ์•„๊ฐ€์•ผํ•  ๋•Œ ์‚ฌ์šฉํ•˜๋ฉด ํšจ๊ณผ์ 
  • ์—ฌ๋Ÿฌ ํ…Œ์ด๋ธ”์„ ์กฐ์ธํ•ด์„œ ์—”ํ‹ฐํ‹ฐ๊ฐ€ ๊ฐ€์ง„ ๋ชจ์–‘์ด ์•„๋‹Œ ์ „ํ˜€ ๋‹ค๋ฅธ ๊ฒฐ๊ณผ๋ฅผ ๋‚ด์•ผ ํ•˜๋ฉด, ํŽ˜์น˜ ์กฐ์ธ๋ณด๋‹ค๋Š” ์ผ๋ฐ˜ ์กฐ์ธ์„ ์‚ฌ์šฉํ•˜๊ณ  ํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ๋“ค๋งŒ ์กฐํšŒํ•ด์„œ DTO๋กœ ๋ฐ˜ํ™˜ํ•˜๋Š” ๊ฒƒ์ด ํšจ๊ณผ์ 

 

๋‹คํ˜•์„ฑ ์ฟผ๋ฆฌ

Type

์กฐํšŒ ๋Œ€์ƒ์„ ํŠน์ • ์ž์‹์œผ๋กœ ํ•œ์ •

// Item ์ค‘์— Book, Movie๋ฅผ ์กฐํšŒํ•ด๋ผ
select i from Item i where type(i) IN (Book, Movie)

 

Treat

์ž๋ฐ”์˜ ํƒ€์ž… ์บ์ŠคํŒ…๊ณผ ์œ ์‚ฌํ•˜๋ฉฐ ์ƒ์† ๊ตฌ์กฐ์—์„œ ๋ถ€๋ชจ ํƒ€์ž…์„ ํŠน์ • ์ž์‹ ํƒ€์ž…์œผ๋กœ ๋‹ค๋ฃฐ ๋•Œ ์‚ฌ์šฉํ•œ๋‹ค.

select i from Item i where treat(i as Book).author = ‘kim’

 

์—”ํ‹ฐํ‹ฐ ์ง์ ‘ ์‚ฌ์šฉ

  • JPQL์—์„œ ์—”ํ‹ฐํ‹ฐ๋ฅผ ์ง์ ‘ ์‚ฌ์šฉํ•˜๋ฉด SQL์—์„œ ํ•ด๋‹น ์—”ํ‹ฐํ‹ฐ์˜ ๊ธฐ๋ณธ ํ‚ค ๊ฐ’์„ ์‚ฌ์šฉ
select count(m) from Member m //์—”ํ‹ฐํ‹ฐ๋ฅผ ์ง์ ‘ ์‚ฌ์šฉ
==
select count(m.id) from Member m //์—”ํ‹ฐํ‹ฐ์˜ ์•„์ด๋””๋ฅผ ์‚ฌ์šฉ

 

String jpql = “select m from Member m where m = :member”;
List resultList = em.createQuery(jpql).setParameter("member", member).getResultList();
==
String jpql = “select m from Member m where m.id = :memberId”;
List resultList = em.createQuery(jpql).setParameter("memberId", memberId).getResultList();

 

Member findMember = em.createQuery("select m from Member m where m=:member", Member.class).setParameter("member", member1).getSingleResult();
System.out.println("findMember.getUsername() = " + findMember.getUsername());

 

where์— id ์กฐ๊ฑด์œผ๋กœ ์ถ”๊ฐ€๋œ ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

 

  • ์™ธ๋ž˜ํ‚ค ๊ฐ’์œผ๋กœ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ
List<Member> resultList = em.createQuery("select m from Member m where m.team = :team", Member.class).setParameter("team", team1).getResultList();
for (Member member : resultList) {
	System.out.println("memberName = " + member.getUsername());
}

List<Member> resultList = em.createQuery("select m from Member m where m.team.id = :teamId", Member.class).setParameter("teamId", team1.getId()).getResultList();
for (Member member : resultList) {
    System.out.println("memberName = " + member.getUsername());
}

m.team์ด ์™ธ๋ž˜ํ‚ค ๊ฐ’์œผ๋กœ ์‚ฌ์šฉ๋œ๋‹ค.

 

Named ์ฟผ๋ฆฌ

  • ๋ฏธ๋ฆฌ ์ •์˜ํ•ด์„œ ์ด๋ฆ„์„ ๋ถ€์—ฌํ•ด๋‘๊ณ  ์‚ฌ์šฉํ•˜๋Š” JPQL
  • ์ •์  ์ฟผ๋ฆฌ
  • ์–ด๋…ธํ…Œ์ด์…˜, XML์— ์ •์˜
  • ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋กœ๋”ฉ ์‹œ์ ์— JPQL์„ ๊ฒ€์ฆํ•˜๊ณ , SQL๋กœ ์ดˆ๊ธฐํ™”ํ•จ. ์ดํ›„์—๋Š” ์žฌ์‚ฌ์šฉ
@Entity
@NamedQuery(
        name = "Member.findByUsername",
        query = "select m from Member m where m.username = :username"	// Member๊ฐ€ ์•„๋‹ˆ๋ผ Memberr์ด๋ฉด ์˜ค๋ฅ˜๋ฅผ ๋ฐœ์ƒ์‹œํ‚ด
)
public class Member {
List<Member> resultList = em.createNamedQuery("Member.findByUsername", Member.class).setParameter("username", "์งฑ๊ตฌ").getResultList();
for (Member member : resultList) {
    System.out.println("member.getUsername() = " + member.getUsername());
}

 

@NamedQuery ๋Œ€์‹  ์Šคํ”„๋ง ๋ฐ์ดํ„ฐ JPA๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.

https://spring.io/projects/spring-data-jpa

 

Spring Data JPA

Spring Data JPA, part of the larger Spring Data family, makes it easy to easily implement JPA-based (Java Persistence API) repositories. It makes it easier to build Spring-powered applications that use data access technologies. Implementing a data access l

spring.io

 

๋ฒŒํฌ ์—ฐ์‚ฐ

  • ์ฟผ๋ฆฌ ํ•œ ๋ฒˆ์œผ๋กœ ์—ฌ๋Ÿฌ ํ…Œ์ด๋ธ” ๋กœ์šฐ ๋ณ€๊ฒฝ
  • executeUpdate()์˜ ๊ฒฐ๊ณผ๋Š” ์˜ํ–ฅ๋ฐ›์€ ์—”ํ‹ฐํ‹ฐ ์ˆ˜ ๋ฐ˜ํ™˜
  • UPDATE, DELETE์— ๋Œ€ํ•˜์—ฌ ๊ฐ€๋Šฅ
  • INSERT(insert into ... select)๋Š” ํ•˜์ด๋ฒ„๋„ค์ดํŠธ๊ฐ€ ์ง€์›ํ•ด์„œ ๊ฐ€๋Šฅํ•˜๋‹ค.
  • ๋ฒŒํฌ ์—ฐ์‚ฐ์€ ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ๋ฅผ ๋ฌด์‹œํ•˜๊ณ  ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ง์ ‘ ์ฟผ๋ฆฌ๋ฅผ ๋‚ ๋ฆฐ๋‹ค. 
    • ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์— ๊ฐ’์„ ๋„ฃ๊ธฐ ์ „์— ๋ฒŒํฌ ์—ฐ์‚ฐ์„ ๋จผ์ € ์‹คํ–‰
    • ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์— ๊ฐ’์ด ์žˆ๋‹ค๋ฉด ๋ฒŒํฌ ์—ฐ์‚ฐ ์ˆ˜ํ–‰ ํ›„ ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ๋ฅผ ์ดˆ๊ธฐํ™” ์‹œํ‚จ๋‹ค.
Team team1 = new Team();
team1.setName("๋–ก์žŽ๋งˆ์„๋ฐฉ๋ฒ”๋Œ€");
em.persist(team1);

Team team2 = new Team();
team2.setName("์ฝฉ์žŽ๋งˆ์„๋ฐฉ๋ฒ”๋Œ€");
em.persist(team2);

Member member1 = new Member();
member1.setUsername("์งฑ๊ตฌ");
member1.addTeam(team1);
em.persist(member1);

Member member2 = new Member();
member2.setUsername("๋งน๊ตฌ");
member2.addTeam(team1);
em.persist(member2);

Member member3 = new Member();
member3.setUsername("์ฒ ์ˆ˜");
member3.addTeam(team2);
em.persist(member3);

int resultCount = em.createQuery("update Member m set m.age = 20").executeUpdate();
System.out.println("resultCount = " + resultCount);

em.clear();

createQuery๋ฅผ ํ•˜๊ธฐ ์ „์— flush๊ฐ€ ํ˜ธ์ถœ๋˜์–ด Member์™€ Team ์ •๋ณด๊ฐ€ DB์— ๋ฐ˜์˜๋˜์–ด ์žˆ๋‹ค. ๋ฒŒํฌ ์—ฐ์‚ฐ์„ ์ˆ˜ํ–‰ํ•˜๋ฉด, ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ๋Š” ๋ฌด์‹œํ•˜๊ณ , ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๊ฐ’๋งŒ ๋ณ€๊ฒฝํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์—์„œ๋Š” ๋‚˜์ด๊ฐ€ 20์‚ด๋กœ ๋ฐ˜์˜๋˜์ง€ ์•Š๋Š”๋‹ค. ์ด๋•Œ em.clear()๋ฅผ ํ•ด์ฃผ๋ฉด, ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์— ์žˆ๋˜ ๊ฒƒ๋“ค์ด ๋ชจ๋‘ ์‚ฌ๋ผ์ง€๊ณ , ๋‹ค์Œ์— ํ˜ธ์ถœํ•  ๋•Œ DB์—์„œ ๋ฐ›์•„์˜ค๋ฏ€๋กœ ์—…๋ฐ์ดํŠธ๊ฐ€ ๋ฐ˜์˜๋œ ์ •๋ณด๋ฅผ ์–ป๋Š”๋‹ค.

 

๊ธฐ์–ตํ•˜๋ฉด ์ข‹์„ ๊ฒƒ

  • ์—ฐ๊ด€๊ด€๊ณ„ ์ฃผ์ธ์€ ๋‹ค ์ชฝ, ์™ธ๋ž˜ํ‚ค๋ฅผ ์ €์žฅํ•˜๋Š” ์ชฝ์˜ ํ•„๋“œ์— ์„ค์ •ํ•˜๊ณ , ์—ฐ๊ด€๊ด€๊ณ„ ๋ฉ”์†Œ๋“œ๋Š” ์˜ํ–ฅ๋ ฅ์„ ์‹œ์ž‘ํ•˜๋Š” ๋ถ€๋ถ„์— ์„ค์ •ํ•œ๋‹ค.
  • ๊ฐ’ ํƒ€์ž… ์ปฌ๋ ‰์…˜์€ ๋งค์šฐ ๊ฐ„๋‹จํ•œ ๊ฒฝ์šฐ๊ฐ€ ์•„๋‹ˆ๋ฉด ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š”๋‹ค. ๊ฐ’ ํƒ€์ž…์€ ์ •๋ง ๊ฐ’ ํƒ€์ž…์ด๋ผ ํŒ๋‹จ๋  ๋•Œ๋งŒ ์‚ฌ์šฉ
  • ์‹๋ณ„์ž๊ฐ€ ํ•„์š”ํ•˜๊ณ , ์ง€์†ํ•ด์„œ ๊ฐ’์„ ์ถ”์ , ๋ณ€๊ฒฝํ•ด์•ผ ํ•œ๋‹ค๋ฉด ๊ทธ๊ฒƒ์€ ๊ฐ’ ํƒ€์ž…์ด ์•„๋‹Œ ์—”ํ‹ฐํ‹ฐ
  • ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์— ์žˆ๋Š” ๊ฐ์ฒด์— ๋Œ€ํ•œ ์ •๋ณด๋ฅผ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ๋ณ€๊ฒฝํ•˜๋ฉด ๋”ํ‹ฐ์ฒดํ‚น์— ์˜ํ•ด ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ๋ณ€๊ฒฝ๋œ ์ •๋ณด๊ฐ€ ๋ฐ˜์˜๋˜์ง€๋งŒ, ๋ฒŒํฌ ์—ฐ์‚ฐ์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—์„œ ๋ณ€๊ฒฝํ•˜๋ฉด ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์— ์žˆ๋Š” ๊ฐ์ฒด๋Š” ์˜ํ–ฅ์„ ๋ฐ›์ง€ ์•Š๋Š”๋‹ค.