728x90
5. 양방향 매핑
연관관계 주인과 mappedBy
- mappedBy == JPA의 멘붕 클래스
- mappedBy는 처음에는 이해하기 어렵다.
- 객체와 테이블간에 연관관계를 맺는 차이를 이해해야 한다.
객체와 테이블이 관계를 맺는 차이
객체 연관관계
- 회원 → 팀 연관관계 1개(단방향)
- 팀 → 회원 연관관계 1개(단방향)
- 즉 양방향은 단방향 2개로 이루어져있다.
- 객체의 양방향 관계는 사실 양방향 관계가 아니라 서로 다른 단방향 관계 2개다.
- 객체를 양방향으로 참조하려면 단방향 연관관계를 2개 만들어야 한다.
테이블 연관관계
- 회원 ↔팀의 연관관계 1개(양방향)
- 테이블은 외래 키 하나로 두 테이블의 연관관계를 관리
- 외래 키 하나로 양방향 연관관계 가짐(양쪽으로 조인할 수 있다.)
- 둘 중 하나로 외래 키를 관리해야 한다.
연관관계의 주인(Owner)
- 양방향 매핑 규칙
- 객체의 두 관계중 하나를 연관관계의 주인으로 지정
- 연관관계의 주인만이 외래 키를 관리(등록, 수정)
- 주인이 아닌 쪽은 읽기만 가능
- 주인은 mappedBy 속성 사용 X
- 주인이 아니면 mappedBy 속성으로 주인 지정
누구를 주인으로?
- 외래 키가 있는 곳을 주인으로 정해라
- 여기서 Member가 연관관계의 주인
- 그러나 순한참조로 인해 양방향보다 단방향이 훨씬 좋다.
- 처음 설계에서는 단방향으로 진행하고 나중에 양방향으로 진행하는 것이 좋다. 처음에는 단방향으로 하는 것이 좋다.
양방향 매핑시 가장 많이 하는 실수
- 연관관계의 주인에 값을 입력하지 않음
import static org.assertj.core.api.Assertions.*;
import static org.junit.jupiter.api.Assertions.*;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
class MemberTest {
@DisplayName("양방향 실수 테스트")
@Test
void bidirectionalNotActionTest() {
final EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
final EntityManager em = emf.createEntityManager();
final EntityTransaction transaction = em.getTransaction();
transaction.begin();
try {
final Team team = new Team(1L, "Team A");
em.persist(team);
final Member memberA = new Member(1L, null, "Member A");
em.persist(memberA);
team.getMembers().add(memberA);
em.flush();
em.clear();
final Member findMember = em.find(Member.class, memberA.getId());
assertThat(findMember.getTeam()).isNull();
transaction.commit();
} catch (final Exception e) {
transaction.rollback();
} finally {
em.close();
}
emf.close();
}
}
null이 나오는걸 확인할 수 있다.
그렇다면 실제 현업에서는 어떻게 해야 하는가?
두 군데 모두 넣어주면 된다.
양쪽에서 서로 처리해버리면 순한참조 문제가 발생하기 때문에 방어 코드를 꼭 넣어줘야 하는데 방어 코드를 작성하는 것이 쉽지 않기 때문에 양방향은 피하는 것이 좋다.
import java.util.ArrayList; import java.util.List; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.OneToMany; @Entity public class Team { @Id private Long id; private String name; @OneToMany(mappedBy = "team") private final List<Member> members = new ArrayList<>(); public Team() { } public Team(final Long id, final String name) { this.id = id; this.name = name; } public List<Member> getMembers() { return members; } public Long getId() { return id; } public String getName() { return name; } public void updateMember(Member member) { member.setTeam(this); this.members.add(member); } }
import static org.assertj.core.api.Assertions.*; import static org.junit.jupiter.api.Assertions.*; import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; import javax.persistence.EntityTransaction; import javax.persistence.Persistence; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; class MemberTest { @DisplayName("양방향 실수 해결 테스트") @Test void bidirectionalNotActionSolutionTest() { final EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello"); final EntityManager em = emf.createEntityManager(); final EntityTransaction transaction = em.getTransaction(); transaction.begin(); try { final Team team = new Team(1L, "Team A"); em.persist(team); final Member memberA = new Member(1L, null, "Member A"); em.persist(memberA); team.updateMember(memberA); em.flush(); em.clear(); final Member findMember = em.find(Member.class, memberA.getId()); assertThat(findMember.getTeam()).isNotNull(); transaction.commit(); } catch (final Exception e) { transaction.rollback(); } finally { em.close(); } emf.close(); } }
장점
- 단방향 매핑만으로도 이미 연관관계 매핑은 완료되었지만 반대 방향으로 조회 기능이 추가된 것 뿐
- JPQL에서 역방향으로 탐색할 일이 많음
- 단방향으로 먼저 설계하고 양방향은 필요할 때 추가해도 됨.(테이블에 영향을 주지 않음)
연관관계 매핑 어노테이션
- 다대일 (@ManyToOne)
- 일대다 (@OneToMany)
- 일대일 (@OneToOne)
- 다대다 (@ManyToMany)
- @JoinColumn, @JoinTable
상속 관계 매핑 어노테이션
- @Inheritance
- @DiscriminatorColumn
- @DiscriminatorValue
- @MappedSuperclass(매핑 속성만 상속)
복합키 어노테이션
- @IdClass
- @EmbeddedId
- @Embeddable
- @MapsId
출처
728x90
728x90
'Lecture > [Tacademy] JPA 프로그래밍 기본기 다지기' 카테고리의 다른 글
7. JPA 객체지향 쿼리 (0) | 2020.12.17 |
---|---|
6. JPA 내부구조 (0) | 2020.12.16 |
4. 연관관계 매핑 (0) | 2020.12.15 |
3. 필드와 컬럼 매핑 (0) | 2020.12.13 |
2. JPA 기초와 매핑 (0) | 2020.12.13 |