본문 바로가기
Back-end/JPA 개념

7. 단방향 연관 관계

by devraphy 2022. 3. 30.

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

댓글