연관관계 매핑 기초
단방향 연관관계
목표
- 객체와 테이블 연관관계의 차이 이해
- 객체의 참조와 테이블의 외래 키 매핑
- 방향(Direction) : 단방향, 양방향
- 다중성(Multiplicity) : 다대일(N:1), 일대다(1:N), 일대일(1:1), 다대다(N:M)
- 연관관계의 주인(Owner) : 객체 양방향 연관관게는 관리주인이 필요
연관관계가 필요한 이유
객체지향 설계의 목표는 자율적인 객체들의 협력 공동체를 만드는 것이다.
객체를 테이블에 맞추어 모델링
@Column(name = "TEAM_ID")
private Long teamId;
-
식별자로 다시 조회하기 때문에 객체지향적인 방법은 아니다.
-
멤버 ID를 통해 멤버 조회 -> 해당 멤버의 팀 ID 조회 -> 조회한 팀 ID를 통해 팀 조회
-
테이블은 외래 키로 조인을 사용해서 연관 테이블을 찾는다.
-
객체는 참조를 사용해서 연관된 객체를 찾는다.
객체 지향 모델링 (ORM)
@ManyToOne
@JoinColumn(name = "TEAM_ID")
private Team team;
DB에서의 TEAM_ID(FK)와 Team 객체를 위와 같이 연결해주면 끝!
양방향 연관관계와 연관관계의 주인
테이블과 객체의 차이
- 테이블의 연관관계는 외래 키 하나로 양방향이 모두 존재한다.
- 객체는 서로의 클래스를 포함시켜야 양방향이 가능하다.
양방향 매핑
public class Team {
@OneToMany(mappedBy = "team")
private List<Member> members = new ArrayList<>();
}
연관관계의 주인과 mappedBy
객체와 테이블이 관계를 맺는 차이
- 객체 연관관계 : 2개
- 회원 -> 팀 1개 (단방향)
- 테이블 연관관계 : 1개
- 회원 <-> 팀 1개 (양방향)
객체의 양방향 관계
테이블의 양방향 관계
- 테이블은 외래 키 하나로 양방향 연관관계를 가진다.
둘 중 하나로 외래 키를 관리해야 한다.
- 정보가 변경될 때, Member의 Team을 변경할지 Team의 Member를 변경할지..
연관관계의 주인(Owner)
- 객체의 두 관계중 하나를 연관관계의 주인으로 지정
- 연관관계의 주인만이 외래 키를 관리(등록, 수정)
- 주인이 아닌쪽은 읽기만 가능
- 주인은 mappedBy 속성 사용X, 주인이 아닌 경우는 mappedBy 속성 사용
누구를 주인으로?
- 외래 키가 있는 곳을 주인으로 정한다. (MEMBER 테이블에 TEAM_ID 라는 외래 키가 존재함)
- 진짜 매핑 - 연관관계의 주인 (Member.team)
- 가짜 매핑 - 주인의 반대편 (Team.members)
주의점
순수 객체 상태를 고려해서 항상 양쪽에 값을 설정하자
- 연관관계의 주인이 아닌 가짜매핑에 접근하여 값을 넣을 경우 주인의 값에는 null이 들어간다.
- 주인과 주인이 아닌 곳 모두 값을 넣어줘야 한다.
- 영속성 컨텍스트를 초기화 했을 경우, 1차 캐시가 아닌 DB에서 조회하기 때문에 주인에만 값이 있어도 알아서 가져온다.
- 영속성 컨테스트를 초기화하지 않을 경우, findTeam은 1차 캐시에 저장된 순수한 team 객체이다.
- member.setTeam(team)을 하고 team.add(member)를 하지 않는 경우, member에는 team이 저장되지만 team 객체를 가져온 findTeam에는 member가 없기 때문에 조회해도 값이 출력되지 않는다.
연관관계 편의 메서드 작성
- 항상 양쪽에 값을 넣어야 하니까 이를 편리하게 하기 위해 메서드 작성
- Member 클래스의 setter에 team.add(member) 추가
- setTeam이 아닌 changeTeam으로 이름을 바꾸어 기능이 추가되었다는 것을 명시
- Member, Team 클래스 둘 중 하나에 작성
양방향 매핑시 무한루프
- Member 클래스 toString()의 team.toString() -> team.toString()의 Members.t oString() -> Member 클래스 -> ...
- controller에서 entity를 직접 반환할 경우 JSON으로 바꾸는 과정
- Member클래스를 JSON으로 바꿀 때 안에 있는 Team 객체도 JSON -> Team 객체 안의 Members에 있는 모든 Member 클래스 JSON -> Member 클래스 안의 Team 객체 JSON -> ...
정리
- 단방향 매핑만으로도 이미 연관관계 매핑이 완료된 것이다.
- 객체, 테이블 설계시 외래 키를 보고 단방향 매핑부터 먼저 한다.
- 양방향 매핑은 반대 방향으로 조회(객체 그래프 탐색) 기능이 추가된 것 뿐이다.
- 단방향 매핑을 모두 잘 끝내고 양방향은 필요할 때 추가해도 된다. (member가 team을 굳이 알 필요는 없다.)
- OneToMany 할 경우 new로 생성자를 초기화해주는 이유는 NPE 방지