0. 개요
- 이전 포스팅에서 DB의 Table을 하나의 Entity(= 객체)로 표현하는 방법에 대해서 배웠다.
- 다양한 어노테이션과 옵션을 사용하여 테이블의 Column과 Entity의 필드를 매핑하였고,
primary key를 설정하는 방법까지 알아보았다.
- 이처럼 Table을 Entity로 표현하는 방법에 대해 알아보았지만, 아직 완전하다고 할 수 없다.
- 왜냐면 객체 지향과 RDB의 근본적인 차이를 해결하지 못했기 때문이다.
1. 근본적인 문제
- 객체 지향과 RDB의 근본적인 문제는 관계를 표현하는 방식에 있다.
- 객체 지향은 참조 또는 상속을 통해 관계를 형성한다.
- RDB에서는 외래 키(= Foreign key)를 통해 관계를 형성한다.
- 이처럼 관계를 표현하는 방식이 다르므로, Java와 SQL 간의 소통이 이뤄지지 않는다.
a) 예시 코드
- 리그의 팀과 선수를 관리하기 위한 시스템을 만들려고 한다.
- RDB 테이블의 구성은 다음과 같다.
- 위의 테이블 구조를 구현하면 다음과 같다.
@Entity
public class Player {
@Id @GeneratedValue
@Column(name = "PLAYER_ID")
private Long id;
@Column(name = "PLAYER_NAME")
private String name;
@Column(name = "TEAM_ID")
private Long teamId;
// Getter & Setter 생략
}
@Entity
@Table(name = "TEAMS")
public class TEAM {
@Id @GeneratedValue
@Column(name = "TEAM_ID")
private Long id;
@Column(name = "TEAM_NAME")
private String name;
// Getter & Setter 생략
}
- 각 Entity(= DB 테이블)의 역할은 다음과 같다.
→ Player는 리그의 모든 선수 명단과 각 선수의 소속 팀에 대한 정보를 가진다.
→ Team은 리그의 모든 팀과 팀 이름에 대한 정보를 가진다.
- Player와 Team 엔티티는 연관관계를 형성한다.
- 리그에서 경기하는 모든 선수는 소속 팀이 있으므로, 선수는 소속 팀 정보(teamId)를 가진다.
b) 정확히 뭐가 문제일까?
- 여기서 주목해야 할 점은 Player 엔티티의 teamId 필드의 자료형이다.
- Player와 Team 엔티티의 코드를 보면 내부에 참조 또는 상속이 존재하지 않는다.
- 객체지향적으로 본다면 teamId 필드는 Long이 아니라 Team 클래스를 자료형으로 사용해야 한다.
- 그러나 teamId 필드를 Team 타입으로 할당하면 DB는 Team 타입을 자료형으로 이해하지 못한다.
- 결론적으로 위의 예시 코드는 연관관계가 아예 형성되지 않은 구조다.
c) 단방향 연관 관계란?
- 위에서 언급한 문제점에서 단방향 연관 관계의 개념이 등장한다.
- RDB는 FK를 이용하여 두 테이블의 Join이 가능하므로 관계의 방향을 구분하지 않는다.
- 그러나 객체(Entity)는 참조용 필드를 통해 다른 객체를 참조할 수 있다.
- 즉, 관계를 이루는 두 객체 간의 참조 필드의 유무에 따라 관계의 방향이 구분된다.
- 그렇다면 단방향 연관 관계란 무엇일까?
- 단방향은 관계를 이루는 두 Entity 중 하나의 Entity만 다른 Entity를 참조하는 필드를 가진 관계다.
- 위의 예제에서 Player는 Team을 참조하는 team_id라는 필드를 가진다.
- 이처럼 한쪽의 Entity만 다른 Entity를 참조하는 필드를 가진 구조를 단방향 연관 관계라고 한다.
- 만약 여기서 Team 객체가 player_id라는 Player 객체의 참조 필드를 가진다면, 이는 양방향 연관 관계가 된다.
2. 단방향 연관관계 명시
- 위의 코드를 테이블 중심 모델링이 아닌 객체지향 모델링으로 만들어보자.
- Player 테이블의 teamId 필드의 코드를 개선시키면 다음과 같다.
@Entity
@Table(name = "Player")
public class Player {
@Column(name = "TEAM_ID")
private Team teamId;
}
- 위의 코드를 그대로 사용하면 오류가 발생한다.
- JPA가 위의 코드를 SQL로 변환할 때, Team 클래스를 SQL 자료형으로 변환할 수 없기 때문이다.
- JPA는 이 문제를 해결하기 위해서 다음 2가지를 명시한다.
→ 연관관계의 형태
→ 관계의 대상
a) @ManyToOne
- 우선 연관관계의 형태를 JPA에게 알려줘야한다.
→ 한명의 Player는 하나의 Team을 가진다.
→ 하나의 Team은 다수의 Player를 가진다.
- 그러므로 Player와 Team의 관계는 N:1 관계다.(= 하나의 team이 다수의 player를 보유한다.)
- @ManyToOne는 JPA에서는 N:1 관계를 명시하는 방법이다.
@Entity
@Table(name = "Player")
public class Player {
@ManyToOne
private Team teamId;
}
b) @JoinColumn
- 연관관계의 형태를 명시했다면, 이제 연관관계의 대상을 명시해야 한다.
- 다음과 같이 Foreign Key(= 외래 키)에 해당하는 필드에 @JoinColumn를 명시한다.
@Entity
@Table(name = "Player")
public class Player {
@ManyToOne
@JoinColumn(name = "TEAM_ID")
private Team teamId;
}
* JoinColumn의 의미는...
- RDB 테이블의 구조로 봤을 때, Player의 teamId 필드는 Foreign Key다.
- RDB에서 Foreign Key를 이용한 검색은 Join을 사용한다.
- 이때 Join의 대상은 Foreign Key를 Primary Key로 사용하는 테이블이다.
3. 결과
- 그렇다면 이제 실제로 잘 동작하는지 확인해보자.
a) 저장
- 다음과 같이 코드를 작성하였다.
- 객체지향적인 코드가 작성되었고, 잘 작동한다면 문제없이 DB에 해당 데이터가 저장되어야 한다.
- 위의 사진과 같이 아주 잘 저장되는 것을 확인할 수 있다.
b) 조회
- 이제 조회를 시도해보자. 다음과 같이 코드를 작성하였다.
- 객체지향적인 코드가 작성되었고, 잘 작동한다면 문제없이 DB에서 조회되어야 한다.
- 위의 사진과 같이 콘솔에 잘 출력되는 것을 확인할 수 있다.
'Back-end > JPA 개념' 카테고리의 다른 글
9. 양방향 연관 관계 사용 시 주의사항 (0) | 2022.04.01 |
---|---|
8. 양방향 연관 관계의 기본 개념 (0) | 2022.03.31 |
6. Primary key(기본 키) Mapping (0) | 2022.03.29 |
5. Field(칼럼) Mapping (0) | 2022.03.28 |
4. Entity Mapping (0) | 2022.03.25 |
댓글