728x90
4. 연관관계 매핑
객체지향 설계의 목표는 자율적인 객체들의 협력 공동체를 만드는 것이다. - 조영호(객체지향의 사실과 오해)
객체를 테이블에 맞추어 모델링(연관관계가 없는 객체)
참조 대신에 외래 키를 그대로 사용한다면?
import javax.persistence.Entity;
import javax.persistence.Id;
@Entity
public class Member {
@Id
private Long id;
private Long teamId;
private String name;
public Long getId() {
return id;
}
public void setId(final Long id) {
this.id = id;
}
public Long getTeamId() {
return teamId;
}
public void setTeamId(final Long teamId) {
this.teamId = teamId;
}
public String getName() {
return name;
}
public void setName(final String name) {
this.name = name;
}
}
import javax.persistence.Entity;
import javax.persistence.Id;
@Entity
public class Team {
@Id
private Long id;
private String name;
public Long getId() {
return id;
}
public void setId(final Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(final String name) {
this.name = name;
}
}
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 SaveAndFindForTable() {
final EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
final EntityManager em = emf.createEntityManager();
final EntityTransaction transaction = em.getTransaction();
transaction.begin();
try {
final Team team = new Team();
team.setId(1L);
team.setName("Team A");
em.persist(team);
final Member member = new Member();
member.setId(1L);
member.setTeamId(1L);
member.setName("Member A");
em.persist(member);
em.flush();
em.clear();
final Member findMember = em.find(Member.class, member.getId());
assertAll(
() -> assertThat(findMember.getId()).isEqualTo(member.getId()),
() -> assertThat(findMember.getTeamId()).isEqualTo(member.getTeamId()),
() -> assertThat(findMember.getName()).isEqualTo(member.getName())
);
transaction.commit();
} catch (final Exception e) {
transaction.rollback();
} finally {
em.close();
}
emf.close();
}
}
식별자로 다시 조회, 객체 지향적인 방법이 아니다.
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 findTeam() {
final EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
final EntityManager em = emf.createEntityManager();
final EntityTransaction transaction = em.getTransaction();
transaction.begin();
try {
final Team team = new Team();
team.setId(1L);
team.setName("Team A");
em.persist(team);
final Member member = new Member();
member.setId(1L);
member.setTeamId(1L);
member.setName("Member A");
em.persist(member);
em.flush();
em.clear();
final Member findMember = em.find(Member.class, member.getId());
final Team findTeam = em.find(Team.class, findMember.getTeamId());
assertAll(
() -> assertThat(findTeam.getName()).isEqualTo(team.getName())
);
transaction.commit();
} catch (final Exception e) {
transaction.rollback();
} finally {
em.close();
}
emf.close();
}
}
객체를 테이블에 맞추어 데이터 중심으로 모델링하면, 협력 관계를 만들 수 없다.
- 테이블은 외래 키로 조인을 사용해서 연관된 테이블을 찾는다.
- 객체는 참조를 사용해서 연관된 객체를 찾는다.
- 테이블과 객체 사이에는 이런 큰 간격이 있다.
단방향 매핑
객체 지향 모델링 (객체 연관관계 사용)
- 여기서 객체에서는 Member가 Team을 알고 있지만 Team이 Member를 알고 있지 않다.
- 그러나 DB는 Join을 통해 둘다 가능하다.
ORM 매핑
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
@Entity
public class Member {
@Id
private Long id;
@ManyToOne
@JoinColumn(name = "team_id")
private Team team;
private String name;
public Member() {
}
public Member(final Long id, final Team team, final String name) {
this.id = id;
this.team = team;
this.name = name;
}
public Long getId() {
return id;
}
public Team getTeam() {
return team;
}
public String getName() {
return name;
}
}
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 SaveAndFindForObj() {
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 member = new Member(1L, team, "Member A");
em.persist(member);
em.flush();
em.clear();
final Member findMember = em.find(Member.class, member.getId());
assertAll(
() -> assertThat(findMember.getId()).isEqualTo(member.getId()),
() -> assertThat(findMember.getTeam().getId()).isEqualTo(member.getTeam().getId()),
() -> assertThat(findMember.getName()).isEqualTo(member.getName())
);
transaction.commit();
} catch (final Exception e) {
transaction.rollback();
} finally {
em.close();
}
emf.close();
}
}
FetchType
JPA 페치(Fetch) 전략 - 즉시 로딩(EAGER)과 지연 로딩(LAZY)
연관관계 수정
- setter로 손쉽게 변경이 가능하다. (따로 update 쿼리를 위해 무언가를 할 필요가 없다.)
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 updateTest() {
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, team, "Member A");
em.persist(memberA);
em.flush();
em.clear();
final Member findMember = em.find(Member.class, memberA.getId());
findMember.setName("Member B");
em.flush();
em.clear();
final Member findMember2 = em.find(Member.class, memberA.getId());
assertThat(findMember2.getName()).isEqualTo("Member B");
transaction.commit();
} catch (final Exception e) {
transaction.rollback();
} finally {
em.close();
}
emf.close();
}
}
- 아래와 같이 update 쿼리가 날라가는걸 확인할 수 있습니다.
update
Member
set name=?,
team_id=?
where id = ?
양방향 매핑
@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;
}
}
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 bidirectionalTest() {
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, team, "Member A");
em.persist(memberA);
final Member memberB = new Member(1L, team, "Member B");
em.persist(memberB);
em.flush();
em.clear();
final Team findTeam = em.find(Team.class, team.getId());
assertThat(findTeam.getMembers()).hasSize(2);
transaction.commit();
} catch (final Exception e) {
transaction.rollback();
} finally {
em.close();
}
emf.close();
}
}
출처
728x90
728x90
'Lecture > [Tacademy] JPA 프로그래밍 기본기 다지기' 카테고리의 다른 글
6. JPA 내부구조 (0) | 2020.12.16 |
---|---|
5. 양방향 매핑 (0) | 2020.12.15 |
3. 필드와 컬럼 매핑 (0) | 2020.12.13 |
2. JPA 기초와 매핑 (0) | 2020.12.13 |
1. JPA 소개 (0) | 2020.12.05 |