JPA의 핵심은 "객체 지향 프로그래밍과 관계형 데이터베이스 간의 패러다임 불일치를 해결하는 것" 이다.
이것과 가장 직접적으로 관련되어있는 연관관계 매핑에 대해 알아보자.
기존의 테이블간의 관계와 객체간의 관계의 차이를 알아보자.
시나리오
- 회원과 팀이 있다.
- 회원은 하나의 팀에만 소속될 수 있다.
- 회원과 팀은 다대일 관계다.
위와 같은 시나리오가 있다고 가정하고 객체의 관계와 테이블 관계를 알아보자.
테이블의 연관관계에 맞게 객체를 설계하고 아래의 코드로 작성했다.(참조 대신 외래 키를 그대로 사용)
@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);
이 상황에서 회원의 팀을 조회 하려면 어떻게 해야할까?
방법은 회원 객체의 teamId로 team을 찾아야 한다.
//조회
Member findMember = em.find(Member.class, member.getId());
//연관관계가 없음
Team findTeam = em.find(Team.class, findMember.getTeamId());
위 처럼 두 객체간에 연관관계가 없다면 member의 team 객체를 꺼내오기 위해서는 계속해서 member를 조회하고, team을 조회해야 한다.
결국 많은 비용이 들게 되고, 객체 지향스럽지 않은 코드가 된다.
객체를 테이블에 맞추어서 데이터 중심으로 모델링하면 협력 관계를 만들 수 없다!
위 방식의 문제점을 나타내보자.
1. 테이블은 외래 키로 조인을 사용해서 연관된 테이블을 찾는다.
2. 하지만 객체는 참조를 사용해서 연관된 객체를 찾는다.
3. 이처럼 테이블과 객체 사이에는 큰 간격이 있다.
이 세 가지 말을 요약한 것이 객체지향과 관계형 DB 사이의 패러다임이 다르다는 말이다
회원과 팀의 관계를 통해 다대일 단방향 관계를 살펴보자.
SELECT *
FROM MEMBER M
JOIN TEAM T ON M.TEAM_ID = T.TEAM_ID
SELECT *
FROM TEAM T
JOIN MEMBER M ON T.TEAM_ID = M.TEAM_ID
기존의 Member 객체 코드를 아래 처럼 수정했다.
JoinColumn 어노테이션은 생략 가능하다.
@JoinColumn을 생략하면 외래 키를 찾을 때 기본 전략을 사용한다.
기본 전략: 필드명 + _ + 참조하는 테이블의 컬럼명
여기서 @ManyToOne 인 이유는 Member와 Team이 다대일(N:1) 관계 이기 때문이다.
//팀 저장
Team team = new Team();
team.setName("TeamA");
em.persist(team);
//회원 저장
Member member = new Member();
member.setName("member1");
member.setTeam(team); //단방향 연관관계 설정, 참조 저장
em.persist(member)
이제 setTeam을 통해 Member에 Team을 연관시켜줄 수 있다.
/조회
Member findMember = em.find(Member.class, member.getId());
//참조를 사용해서 연관관계 조회
Team findTeam = findMember.getTeam();
Member를 한번 조회를 통해 Member를 가져오고, getTeam 메소드로 Member가 속한 Team도 조회가 가능하다.
// 새로운 팀B
Team teamB = new Team();
teamB.setName("TeamB");
em.persist(teamB);
// 회원1에 새로운 팀B 설정
member.setTeam(teamB);
위와 같이 Team 수정도 가능하다!