연관관계 매핑 기초
객체의 참조와 테이블의 외래 키를 매핑할 수 있다.
방향
단방향, 양방향
다중성
다대일(N:1), 일대다(1:N), 일대일(1:1), 다대다(N:N)
연관관계의 주인
객체 양방향 연관관계는 주인을 정해야한다.
예제 시나리오
- 회원과 팀이 있다.
- 회원은 하나의 팀에만 속한다.
- 회원과 팀은 대대일 관계이다.
@Entity
public class Member {
@Id @GeneratedValue
private Long id;
@Column(name = "USERNAME")
private String name;
@Column(name = "TEAM_ID")
private Long teamId;
…
}
@Entity
public class Team {
@Id @GeneratedValue
private Long id;
private String name;
…
}
객체를 테이블에 맞추어 모델링 => 참조 대신 외래 키를 그대로 사용
///팀 저장
Team team = new Team();
team.setName("TeamA");
em.persist(team);
//회원 저장
Member member = new Member();
member.setName("member1");
member.setTeamId(team.getId());
em.persist(member);
외래 키 식별자를 직접 다룸
//조회
Member findMember = em.find(Member.class, member.getId());
//연관관계가 없음
Team findTeam = em.find(Team.class, findMember.getTeamId());
식별자로 다시 조회, 객체 지향적인 방법은 아니다..
문제점
객체를 테이블에 맞추어 데이터 중심으로 모델링하면, 협력 관계를 만들 수 없다.
테이블은 외래 키로 조인을 사용해서 연관된 테이블을 찾는다.
객체는 참조를 사용해서 연관된 객체를 찾는다.
테이블과 객체 사이에는 이런 큰 간격이 있다..
@Entity
public class Member {
@Id @GeneratedValue
private Long id;
@Column(name = "USERNAME")
private String name;
private int age;
// @Column(name = "TEAM_ID")
// private Long teamId;
@ManyToOne
@JoinColumn(name = "TEAM_ID")
private Team team;
…
}
//팀 저장
Team team = new Team();
team.setName("TeamA");]
em.persist(team);
//회원 저장
Member member = new Member();
member.setName("member1");
member.setTeam(team); //단방향 연관관계 설정, 참조 저장
em.persist(member);
연관 관계 저장
//조회
Member findMember = em.find(Member.class, member.getId());
//참조를 사용해서 연관관계 조회
Team findTeam = findMember.getTeam()
참조로 연관 관계 조회 - 객체 그래프 탐색
// 새로운 팀B
Team teamB = new Team();
teamB.setName("TeamB");
em.persist(teamB);
// 회원1에 새로운 팀B 설정
member.setTeam(teamB)
연관관계 수정
양방향 연관관계와 연관관계의 주인
@Entity
public class Member {
@Id @GeneratedValue
private Long id;
@Column(name = "USERNAME")
private String name;
private int age;
@ManyToOne
@JoinColumn(name = "TEAM_ID")
private Team team;
…
}
Member 엔티티는 단방향과 동일
@Entity
public class Team {
@Id @GeneratedValue
private Long id;
private String name;
@OneToMany(mappedBy = "team")
List<Member> members = new ArrayList<Member>();
…
}
Team 엔티티는 컬렌션 추가
// 조회
Team findTeam = em.find(Team.class, team.getId());
// 역방향 조회
int memberSize = findTeam.getMembers().size();
객체와 테이블이 관계를 맺는 차이
- 객체 연관관계 = 2
- 회원 -> 팀 연관관계 1개 (단방향)
- 팀 -> 회원 연관관계 1개 (단방향)
- 테이블 연관관계 = 1
- 회원 <-> 팀의 연관관계 1개(양방향)
테이블의 양방향 연관관계
- 테이블은 외래 키 하나로 두 테이블의 연관관계를 관리
- MEMBER.TEAM_ID 외래 키 하나로 양방향 연관관계 가짐 (양쪽으로 조인할 수 있다.)
객체의 양방향 연관관계 (연관관계의 주인)
- 객체의 두 관계중 하나를 연관관계의 주인으로 지정
- 연관관계의 주인만이 외래 키를 관리(등록, 수정)
- 주인이 아닌쪽은 읽기만 가능
- 주인은 mappedBy 속성 사용 x
- 주인이 아니면 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);
=> Member 테이블에 team_id 값이 저장되지 않는 오류 발생.
양방향 매핑시 연관관계의 주인에 값을 입력해야 한다.
Team team = new Team();
team.setName("TeamA");
em.persist(team);
Member member = new Member();
member.setName("member1");
team.getMembers().add(member);
//연관관계의 주인에 값 설정
member.setTeam(team);
em.persist(member);
=> 순수한 객체 관계를 고려하면 항상 양쪽 다 값을 입력해야 한다.
양방향 매핑 정리
- 단방향 매핑만으로도 이미 연관관계 매핑은 완료
- 양방향 매핑은 반대 방향으로 조회(객체 그래프 조회) 기능이 추가된 것 뿐
- JPQL에서 역방향으로 탐색할 일이 많음
- 단방향 매핑을 잘 하고 양방향은 필요할 때 추가해도 됨 (테이블이 영향을 주지 않음)
연관관계의 주인을 정하는 기준
- 비즈니스 로직을 기준으로 연관관계의 주인을 선택하면 안됨
- 연관관계의 주인은 외래 키의 위치를 기준으로 정해야함
'Spring Data' 카테고리의 다른 글
고급매핑 #7 (0) | 2021.07.23 |
---|---|
다양한 연관관계 매핑 #6 (0) | 2021.07.19 |
엔티티 매핑 #4 (0) | 2021.07.18 |
영속성 관리 - 내부 동작 방식 #3 (0) | 2021.07.17 |
JPA 시작하기 #2 (0) | 2021.07.17 |