πŸ“‹ Jpa - μ–‘λ°©ν–₯ 맀핑


μ–‘λ°©ν–₯ 맀핑

  • λ°˜λŒ€λ°©ν–₯μœΌλ‘œλ„ κ·Έλž˜ν”„ μ‘°νšŒκ°€ κ°€λŠ₯해야함

μ—°κ΄€ κ΄€κ³„μ˜ 주인과

  • μ™œ mappedByλ₯Ό ν•΄μ£ΌλŠ” 건지?

  • κ°μ²΄λŠ” μ„œλ‘œλ₯Ό ν¬ν•¨ν•˜κ³  μžˆμ–΄μ•Ό 함 (2κ°œκ°€ ν•„μš”)

객체와 ν…Œμ΄λΈ”μ΄ 관계λ₯Ό λ§ΊλŠ” 차이

객체 연관관계

  • νšŒμ› -> νŒ€ μ—°κ΄€ 관계 1개 (단방ν–₯)
  • νŒ€ -> νšŒμ› 연관관계 1개 (단방ν–₯)

ν…Œμ΄λΈ” 연관관계

  • νšŒμ› <-> νŒ€μ˜ 연관관계 1개 (μ–‘λ°©ν–₯)

이 차이λ₯Ό μ–΄λ–»κ²Œ 극볡할거냐??

객체의 μ–‘λ°©ν–₯ 관계

κ°μ²΄λŠ” 사싀 μ–‘λ°©ν–₯ 관계가 μ•„λ‹ˆλΌ μ„œλ‘œ λ‹€λ₯Έ 단방ν–₯ 관계 2κ°œμ΄λ‹€.
객체λ₯Ό μ–‘λ°©ν–₯으둜 μ°Έμ‘°ν•˜λ €λ©΄ 단방ν–₯ 연관관계λ₯Ό 2개 λ§Œλ“€μ–΄μ•Ό 함

ν…Œμ΄λΈ”μ˜ μ–‘λ°©ν–₯ 관계

ν…Œμ΄λΈ”μ€ μ™Έλž˜ν‚€ ν•˜λ‚˜λ‘œ λ‘ν…Œμ΄λΈ”μ˜ μ—°κ΄€ 관계λ₯Ό 관리

λ‘˜ 쀑 ν•˜λ‚˜λ‘œ μ™Έλž˜ν‚€λ₯Ό 관리해야 ν•œλ‹€

  • κ°μ²΄λŠ” 두 κ³³μ—μ„œ 연관관계에 μžˆλŠ” 객체λ₯Ό 닀루고 있음
  • 그러면 뭘 λ―Ώμ–΄μ•Ό ν•˜λ‚˜?
  • λ‘κ°œλŠ” μ–΄μ°¨ν”Ό λ‹€λ₯΄λ‹€. λ‘˜ 쀑 ν•˜λ‚˜λ§Œ 주인으둜 λ§Œλ“€μž

μ—°κ΄€ κ΄€κ³„μ˜ 주인

  • 두곳에 μžˆλŠ” 객체쀑 ν•˜λ‚˜λ§Œ 주인으둜 두고 κ±”λ§Œ 영ν–₯을 λ°›μŒ
μ–‘λ°©ν–₯ 맀핑 κ·œμΉ™
  • 객체의 두 관계 쀑 ν•˜λ‚˜λ₯Ό μ—°κ΄€κ΄€κ³„μ˜ 주인으둜 지정
  • μ—°κ΄€κ΄€κ³„μ˜ 주인만이 μ™Έλž˜ν‚€λ₯Ό 관리(등둝, μˆ˜μ •)
  • 주인이 μ•„λ‹Œ μͺ½μ€ 읽기만 κ°€λŠ₯
  • 주인은 mappedBy 속성 μ‚¬μš© x
  • 주인이 μ•„λ‹ˆλ©΄ mappedBy μ†μ„±μœΌλ‘œ 주인 지정

그런데 λˆ„κ΅¬λ₯Ό 주인으둜 ν•΄μ•Όν•˜λ‚˜?

  • μ™Έλž˜ν‚€κ°€ μžˆλŠ” 곳을 주인으둜 정해라
  • μ—¬κΈ°μ„œλŠ” Member.team이 μ—°κ΄€κ΄€κ³„μ˜ 주인

μ°Έκ³  - 섀계
  • 일단 단방ν–₯으둜 λ‹€ 섀계λ₯Ό ν•˜κ³  μ–‘λ°©ν–₯이 ν•„μš”ν•  λ•Œ μ–‘λ°©ν–₯을 μΆ”κ°€ν•˜μž
  • μ–‘λ°©ν–₯은 쑰회λ₯Ό νŽΈν•˜κ²Œ ν•˜κΈ° μœ„ν•΄ μ‚¬μš©ν•˜λŠ” λŠλ‚Œ
  • 인지 λΆ€μ‘°ν™”κ°€ 생기지 μ•ŠμŒ

μ–‘λ°©ν–₯ λ§€ν•‘μ‹œ κ°€μž₯ 많이 ν•˜λŠ” μ‹€μˆ˜

  • μ—°κ΄€κ΄€κ³„μ˜ 주인에 값을 μž…λ ₯ν•˜μ§€ μ•ŠμŒ

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

Member member = new Member();
member.setName("amazzi");
member.setTeam(team);
em.persist(member);
  • 주인에 값을 μž…λ ₯ν•˜κΈ°

μ–‘λ°©ν–₯ λ§€ν•‘μ‹œ μ—°κ΄€κ΄€κ³„μ˜ 주인에 값을 μž…λ ₯해야함

  • μˆœμˆ˜ν•œ 객체 관계λ₯Ό κ³ λ €ν•˜λ©΄ 항상 μ–‘μͺ½ λ‹€ 값을 μž…λ ₯ν•΄μ•Ό 함

  • μ‹€μ œ μ½”λ“œμ—λŠ” μ–‘μͺ½ λ‹€ ν•  것을 ꢌμž₯

μ–‘λ°©ν–₯ λ§€ν•‘μ˜ μž₯점

  • 단방ν–₯ λ§€ν•‘λ§ŒμœΌλ‘œλ„ 이미 연관관계 맀핑은 μ™„λ£Œ
  • μ–‘λ°©ν–₯ 맀핑은 λ°˜λŒ€ λ°©ν–₯으둜 쑰회(객체 κ·Έλž˜ν”„ 탐색) κΈ°λŠ₯이 μΆ”κ°€λœ 것 뿐
  • JPQLμ—μ„œ μ—­λ°©ν–₯으둜 탐색할 일이 많음
  • 단방ν–₯ 맀핑을 μž˜ν•˜κ³  μ–‘λ°©ν–₯은 ν•„μš”ν•  λ•Œ 좔가해도 됨(ν…Œμ΄λΈ”μ— 영ν–₯을 주지 μ•ŠμŒ)

μ—°κ΄€ 관계 맀핑 μ–΄λ…Έν…Œμ΄μ…˜

  • λ‹€λŒ€μΌ @ManyToOne
  • μΌλŒ€λ‹€ @OneToMany
  • μΌλŒ€μΌ @OneToOne
  • λ‹€λŒ€λ‹€ @ManyToMany
  • @JoinColum, @JoinTable

상속 관계 맀핑 μ–΄λ…Έν…Œμ΄μ…˜

  • @Ingeritance
  • @DiscriminatorColumn
  • @DiscriminatorValue
  • @MappedSuperclass : 맀핑 μ†μ„±λ§Œ 상속

볡합킀 μ–΄λ…Έν…Œμ΄μ…˜

  • @IdClass
  • @EmbeddeId
  • @Embeddable
  • @MapsId

JPA 내뢀ꡬ쑰

μ˜μ†μ„± μ»¨ν…μŠ€νŠΈ

  • μ—”ν‹°ν‹°λ₯Ό 영ꡬ히 μ €μž₯ν•˜λŠ” ν™˜κ²½
  • μ˜μ†μ„± = 영ꡬ히 μ €μž₯ν•˜λŠ” 속성
  • EntityManager.persist(entity)
  • μ˜μ†μ„± μ»¨ν…μŠ€νŠΈλŠ” 논리적인 κ°œλ… (λˆˆμ— 보이지 μ•ŠμŒ)
  • μ—”ν‹°ν‹° λ§€λ‹ˆμ €λ₯Ό 톡해 μ˜μ†μ„± μ»¨ν…μŠ€νŠΈμ— μ ‘κ·Ό

μŠ€ν”„λ§ ν”„λ ˆμž„ μ›Œν¬μ—μ„œμ˜ μ˜μ†μ„± μ»¨ν…μŠ€νŠΈ

  • μ—”ν‹°ν‹° λ§€λ‹ˆμ €μ™€ μ˜μ†μ„± μ»¨ν…μŠ€νŠΈκ°€ N:1
  • 같은 νŠΈλžœμž­μ…˜μ΄λ©΄ 같은 μ˜μ†μ„± μ»¨ν…μŠ€νŠΈμ— μ ‘κ·Ό

μ—”ν‹°ν‹° λ§€λ‹ˆμ € νŒ©ν† λ¦¬μ™€ μ—”ν‹°ν‹° λ§€λ‹ˆμ €

  • μš”μ²­μ΄ 듀어와 이걸 μ²˜λ¦¬ν•˜λŠ” μŠ€λ ˆλ“œκ°€ ν•˜λ‚˜ 생성될 λ•Œλ§ˆλ‹€ μƒˆλ‘œμš΄ μ—”ν‹°ν‹° λ§€λ‹ˆμ €λ₯Ό λ§Œλ“ λ‹€.

  • μ—”ν‹°ν‹° λ§€λ‹ˆμ €μ—μ„œ λ‚΄λΆ€μ μœΌλ‘œ λ°μ΄ν„°λ² μ΄μŠ€ 컀λ„₯μ…˜ ν’€μ—μ„œ JPAλ₯Ό μ‚¬μš©

μ—”ν‹°ν‹°μ˜ 생λͺ… μ£ΌκΈ°

λΉ„μ˜μ† (New)

  • 객체λ₯Ό μƒμ„±ν•œ μƒνƒœ
Team team = new Team();
team.setName("teamA");

μ˜μ†

  • λΉ„μ˜μ† μƒνƒœμΈ 객체λ₯Ό μ˜μ†μ„± μ»¨ν…μŠ€νŠΈμ— μ €μž₯ν•œ μƒνƒœ
  • μ˜μ†μ„± μ»¨ν…μŠ€νŠΈ μ•ˆμ—μ„œ κ΄€λ¦¬λ˜κΈ° μ‹œμž‘
em.persist(team);

μ€€μ˜μ† (Detached)

  • μ—”ν‹°ν‹°λ₯Ό μ˜μ†μ„± μ»¨ν…μŠ€νŠΈμ—μ„œ 뢄리
em.detach(team);
  • μ˜μ† -> μ€€μ˜μ†
  • μ˜μ† μƒνƒœμ˜ μ—”ν‹°ν‹°κ°€ μ˜μ†μ„± μ»¨ν…μŠ€νŠΈμ—μ„œ 뢄리
  • μ˜μ†μ„± μ»¨ν…μŠ€νŠΈκ°€ μ œκ³΅ν•˜λŠ” κΈ°λŠ₯을 μ‚¬μš© λͺ»ν•¨
μ€€μ˜μ† μƒνƒœλ‘œ λ§Œλ“œλŠ” 방법
  • em.detched(entity) : νŠΉμ • μ—”ν‹°ν‹°λ§Œ μ€€μ˜μ† μƒνƒœλ‘œ μ „ν™˜
  • em.clear() : μ˜μ†μ„± μ»¨ν…μŠ€νŠΈλ₯Ό μ™„μ „νžˆ μ΄ˆκΈ°ν™”
  • em.close() : μ˜μ†μ„± μ»¨ν…μŠ€νŠΈλ₯Ό μ’…λ£Œ

μ‚­μ œ

  • 객체λ₯Ό μ‚­μ œν•œ μƒνƒœ
em.remove(team);

μ˜μ†μ„± μ»¨ν…μŠ€νŠΈμ˜ 이점

1μ°¨ μΊμ‹œ

  • μ—”ν‹°ν‹° λ§€λ‹ˆμ € μ•ˆμ— ν‚€ λ°Έλ₯˜λ‘œ μΊμ‹œκ°€ 됨
1μ°¨ μΊμ‹œμ—μ„œ 쑰회
Member member = new Member();
member.setName("amazzi");
// 1μ°¨ μΊμ‹œμ— μ €μž₯됨
em.persist(member);
// 1μ°¨ μΊμ‹œμ—μ„œ 쑰회
Memeber findMember = em.find(Member.class, "member1");
  • DBλ₯Ό 가지 μ•ŠμŒ
  • 1μ°¨ μΊμ‹œλŠ” Global μΊμ‹œκ°€ μ•„λ‹˜. νŠΈλžœμž­μ…˜ μ•ˆμ—μ„œλ§Œ λ™μž‘ν•˜λŠ” μΊμ‹œ

데이터 λ² μ΄μŠ€μ—μ„œ 쑰회
Member findMember2 = em.find(Member.classm "member2");

동일성 보μž₯

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

a == b // 동일성 비ꡐ true
  • 1μ°¨ μΊμ‹œλ‘œ 반볡 κ°€λŠ₯ν•œ 읽기 λ“±κΈ‰(REPEATABLE READ) λ“±κΈ‰μ˜ νŠΈλžœμž­μ…˜ 격리 μˆ˜μ€€μ„ DB κ°€ μ•„λ‹Œ μ• ν”Œλ¦¬μΌ€μ΄μ…˜ μ°¨μ›μ—μ„œ 제곡

νŠΈλžœμž­μ…˜μ„ μ§€μ›ν•˜λŠ” μ“°κΈ° 지연

em = entityManagerFactory.createEntityManager();
EntityTransaction transaction = em.getTransaction();
// μ—”ν‹°ν‹° λ§€λ‹ˆμ €λŠ” 데이터 λ³€κ²½μ‹œ νŠΈλžœμž­μ…˜μ„ μ‹œμž‘ν•΄μ•Ό 함
transaction.begin();
try {
    em.persist(member1);
  	em.persist(member2);
    // μ—¬κΈ°κΉŒμ§€ INSERT 쿼리λ₯Ό DB에 날리지 μ•ŠμŒ

    transaction.commit(); // νŠΈλžœμž­μ…˜ 컀밋
    em.flush();
    em.clear();
} catch (Exception e) {
    transaction.rollback();
}

  • 논리적인 κ°œλ…. 이 λ•Œ Insert 쿼리λ₯Ό μŒ“μ•„ λ†“λŠ”λ‹€.
transcation.commit();

λ³€κ²½ 감지(Dirty Checking)

em = entityManagerFactory.createEntityManager();
EntityTransaction transaction = em.getTransaction();
// μ—”ν‹°ν‹° λ§€λ‹ˆμ €λŠ” 데이터 λ³€κ²½μ‹œ νŠΈλžœμž­μ…˜μ„ μ‹œμž‘ν•΄μ•Ό 함
transaction.begin();
try {
  	// μ˜μ† μ—”ν‹°ν‹° 쑰회
    Member memberA = em.find(Member.class, "memberA");
  
    // μ˜μ† μ—”ν‹°ν‹° 데이터 μˆ˜μ •
  	memberA.setName("hi");
    
    transaction.commit(); // νŠΈλžœμž­μ…˜ 컀밋
    em.flush(); // 쿼리λ₯Ό λ‹€ λ‚ λ¦Ό
    em.clear(); // μ˜μ†μ„± μ»¨ν…μŠ€νŠΈ μ•ˆμ— μžˆλŠ” μΊμ‹œλ₯Ό λ‹€ λ‚ λ¦Ό
} catch (Exception e) {
    transaction.rollback();
}
  • em.update(member) 이런 μ½”λ“œκ°€ 없어도 됨

  • 1μ°¨ μΊμ‹œκ°€ μƒμ„±λ˜λŠ” μˆœκ°„ μŠ€λƒ…μƒ·μ΄ 생긴닀.
  • JPAλŠ” νŠΈλžœμž­μ…˜μ΄ μ»€λ°‹λ˜λŠ” μˆœκ°„ μŠ€λƒ…μƒ·κ³Ό 1μ°¨ μΊμ‹œμ˜ μ—”ν‹°ν‹°λ₯Ό λΉ„κ΅ν•΄μ„œ 바뀐 λ†ˆμ„ κ°μ§€ν•œλ‹€.
  • κ·Έλž˜μ„œ update 쿼리λ₯Ό λ‚ λ¦Ό
μ™œ μ΄λ ‡κ²Œ ν–ˆμ„κΉŒ?
  • μžλ°” μ»¬λ ‰μ…˜μ—μ„œ 데이터λ₯Ό κ°€μ Έμ˜€κ³  이λ₯Ό λ³€κ²½ν•˜λ©΄ λ¦¬μŠ€νŠΈμ— μžˆλŠ” 값이 λ°”λ€Œμ£ ?
  • μ΄κ±°λž‘ 같은 λ§₯락
  • κ·Έλž˜μ„œ λ°”κΎΈκ³  μ»€λ°‹λ§Œ 치면 λœλ‹€.
μ—”ν‹°ν‹° μ‚­μ œ
// μ‚­μ œ λŒ€μ‚° μ—”ν‹°ν‹° 쑰회
Member memberA = em.find(Member.class, "memberA");
em.remove(memberA); // μ—”ν‹°ν‹° μ‚­μ œ
  • νŠΈλžœμž­μ…˜ 컀밋 μ‹œμ μ— 쿼리가 날라감
  • 버퍼λ₯Ό μ΅œλŒ€ν•œ λŠ¦μΆ˜λ‹€. (νŠΈλžœμž­μ…˜ 컀밋할 λ•ŒκΉŒμ§€)

ν”ŒλŸ¬μ‹œ

  • μ˜μ†μ„± μ»¨ν…μŠ€νŠΈμ˜ λ³€κ²½ λ‚΄μš©μ„ λ°μ΄ν„°λ² μ΄μŠ€μ— 반영
ν”ŒλŸ¬μ‹œκ°€ λ°œμƒλ  λ•Œ
  • λ³€κ²½ 감지
  • μˆ˜μ •λœ μ—”ν‹°ν‹° μ“°κΈ° 지연 SQL μ €μž₯μ†Œμ— 등둝
  • μ“°κΈ° 지연 SQL μ €μž₯μ†Œμ˜ 쿼리λ₯Ό λ°μ΄ν„°λ² μ΄μŠ€μ— 전솑 (등둝, μˆ˜μ •, μ‚­μ œ 쿼리)
ν”ŒλŸ¬μ‹œν•˜λŠ” 방법
  • em.flush() : 직접 호좜
  • νŠΈλžœμž­μ…˜ 컀밋 : ν”ŒλŸ¬μ‹œ μžλ™ 호좜
  • JPQL 쿼리 μ‹€ν–‰ : ν”ŒλŸ¬μ‹œ μžλ™ 호좜
JPQL 쿼리 μ‹€ν–‰μ‹œ ν”ŒλŸ¬μ‹œκ°€ μžλ™μœΌλ‘œ ν˜ΈμΆœλ˜λŠ” 이유
em.persist(memberA);
em.persist(memberB);
em.persist(memberC);

// 쀑간에 JPQL μ‹€ν–‰ // ν”ŒλŸ¬μ‹œκ°€ λ¨Όμ € μˆ˜ν–‰λœλ‹€.
query - em.createQuery("select m from Member m", Member.class);
List<Member> members = query.getResultList();
  • 쿼리 μ‹€ν–‰ λ•Œ ν”ŒλŸ¬μ‹œκ°€ μ•ˆλ˜λ©΄ DBμ—μ„œ μ‘°νšŒν•΄μ˜¬ 수 μ—†κΈ° λ•Œλ¬Έ
  • λ§Œμ•½ λ§ˆμ΄λ°”ν‹°μŠ€λ‚˜ JDBCλ₯Ό μ‚¬μš©ν•˜λ©΄ ν”ŒλŸ¬μ‹œκ°€ μ•ˆλ¨
ν”ŒλŸ¬μ‹œλŠ”!
  • μ˜μ†μ„± μ»¨ν…μŠ€νŠΈλ₯Ό λΉ„μš°μ§€ μ•ŠμŒ
  • μ˜μ†μ„± μ»¨ν…μŠ€νŠΈμ˜ λ³€κ²½ λ‚΄μš©μ„ λ°μ΄ν„°λ² μ΄μŠ€μ— 동기화
  • νŠΈλžœμž­μ…˜μ΄λΌλŠ” μž‘μ—… λ‹¨μœ„κ°€ μ€‘μš” -> 컀밋 μ§μ „μ—λ§Œ 동기화 ν•˜λ©΄ 됨

지연 λ‘œλ”©(Lazy Loading)

μ§€μ—°λ‘œλ”©κ³Ό μ˜μ†μ„± μ»¨ν…μŠ€νŠΈ
  • Memberλ₯Ό μ‘°νšŒν•  λ•Œ Team도 ν•¨κ»˜ μ‘°νšŒν•΄μ•Ό ν• κΉŒ?
  • λ‹¨μˆœνžˆ member μ •λ³΄λ§Œ μ‚¬μš©ν•˜λŠ” λΉ„μ¦ˆλ‹ˆμŠ€ λ‘œμ§μ΄λΌλ©΄β€¦ 미리 μ‘°νšŒν•  ν•„μš”κ°€ μ—†κ² μ£ 
    • member.getName()
    • 이러면 FetchType.Lazy둜 μ§€μ—°λ‘œλ”©μ„ 걸자

  • Memberλ₯Ό μ‘°νšŒν•  λ•Œ Team은 κ°€μ§œ 객체(ν”„λ‘μ‹œ 객체)κ°€ 듀어감
μ¦‰μ‹œλ‘œλ”©
  • FetchType.EAGER
  • Memberλ₯Ό μ‘°νšŒν•  λ•Œ Team도 ν•¨κ»˜ 쑰회

ν”„λ‘μ‹œμ™€ μ¦‰μ‹œλ‘œλ”© 주의
  • 가급적 지연 λ‘œλ”©μ„ μ‚¬μš©
  • μ¦‰μ‹œ λ‘œλ”©μ„ μ μš©ν•˜λ©΄ μ˜ˆμƒν•˜μ§€ λͺ»ν•œ SQL이 λ°œμƒ
  • μ¦‰μ‹œ λ‘œλ”©μ€ JPQLμ—μ„œ N+1 문제λ₯Ό μΌμœΌν‚΄
  • @ManyToOne, @OneToOne 은 기본이 μ¦‰μ‹œ λ‘œλ”©
    • LAZY둜 μ„€μ •ν•˜κΈ°
  • @OneToMany, @ManyToMany λŠ” 기본이 지연 λ‘œλ”©
  • μŠ€ν”„λ§μ—μ„œλŠ” νŠΈλžœμž­μ…˜μ΄ λλ‚˜κ³  μ»¨νŠΈλ‘€λŸ¬μ—μ„œ LAZY λ‘œλ”© ν•˜λ €κ³  ν•  λ•Œ 문제 생김

참고 자료