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

11. 연관 관계의 종류(일대일, 다대다)

by devraphy 2022. 4. 5.

0. 개요

- 이전 포스팅에 이어서 연관 관계의 종류에 대해서 알아보자.

 

1. 일대일(1:1, OneToOne) 관계

a) 일대일 연관 관계란

- 일대일 관계는 그 반대도 일대일 관계다.

- 일대일 관계의 경우, 주 테이블 또는 대상 테이블 중 한쪽에서 FK를 관리한다.

- 즉, 연관 관계의 주인을 선택하는 과정이 자유롭다. 

- 다만, DB에서 FK에 유니크(UNIQ) 제약조건이 추가되어야 한다. 

 

* 알아두자!

- UNIQUE 제약 조건이란, 해당 칼럼에 들어가는 값(value)이 고유 값이어야 한다는 제약이다.

- 즉, 중복 값이 존재하지 않는다. 

 

b) 일대일 관계의 예시

- 예를 들어, 선수 1명당 1개의 라커를 할당받는다는 규칙을 테이블로 구현하면 다음과 같다.

 

- 이를 객체로 구현하면 다음과 같다.

 

- 앞서 설명했듯이, 일대일 관계는 연관 관계의 주인을 자유롭게 선택할 수 있다.

- 그러므로 다대일 관계와는 다르게, 다음과 같이 반대의 관계도 형성이 가능하다.

 

 c) 일대일 관계 예시 코드 - 단방향

- 위의 관계를 코드로 구현하면 다음과 같다.

- 아래의 예시 코드는 Player 객체를 연관 관계의 주인으로 했을 때이다. 

@Entity
public class Player {

    @Id @GeneratedValue
    @Column(name = "PLAYER_ID")
    private Long id;

    @Column(name = "PLAYER_NAME")
    private String name;

    @ManyToOne
    @JoinColumn(name = "TEAM_ID")
    private Team team;

    @OneToOne
    @JoinColumn(name = "LOCKER_ID")
    private Locker locker;
    
    // Getter & Setter 생략
}

 

@Entity
public class Locker {

    @Id @GeneratedValue
    @Column(name = "LOCKER_ID")
    private Long id;

    private String name;
    
    // Getter & Setter 생략
}

 

- 위의 코드를 실행했을 때, 콘솔에서 다음과 같은 결과를 확인할 수 있다.

 

 d) 일대일 관계 예시 코드 - 양방향

- 일대일 관계를 양방향 연관 관계를 형성하면 다음과 같은 구조를 가진다.

 

- 이를 코드로 구현하면 Locker 객체를 다음과 같이 수정하면 된다.

@Entity
public class Locker {

    @Id @GeneratedValue
    @Column(name = "LOCKER_ID")
    private Long id;

    private String name;

    @OneToOne(mappedBy = "locker")
    private Player player;
    
    // Getter & Setter 생략
}

 

- 이처럼 일대일 연관 관계는 연관 관계의 주인을 선택함에 있어서 자유롭다.

- 다만, FK가 존재하는 테이블이 연관 관계의 주인이 된다는 점은 변함없다.

 

e) 일대일 관계 예시 코드 - 단방향, 대상 테이블이 FK 관리

- 일대일 관계에서 단방향 매핑 시, 대상 테이블이 FK를 관리하는 경우다.

- 즉, 관계의 주인은 Player 객체고 관계의 대상이 Locker 객체인 경우다.

 

- 일대일 단방향 매핑에서 관계의 주인이 아닌, 대상 테이블이 FK를 관리하는 관계는 허락되지 않는다.

- JPA에서 단방향 매핑 시 대상 테이블이 FK를 관리하는 관계를 지원하지 않기 때문이다.

- 다만, 양방향 매핑 시에는 가능하다.

 

f) 일대일 관계 예시 코드 - 양방향, 대상 테이블이 FK 관리

- 일대일 관계에서 양방향 매핑 시, 대상 테이블이 FK를 관리하는 경우다.

- 즉, 관계의 주인은 Player 객체고 관계의 대상이 Locker 객체인 경우다.

 

- 위의 경우는 대상 테이블이 FK를 관리하는 것이 아니다.

- 사실상 Locker 객체가 연관 관계의 주인이 되는 것이다. 

- 그러므로 위의 관계를 구현할 때에는 Locker 객체의 playerId를 관계의 주인(FK)으로 설정한다.

- 즉, 일대일 관계의 양방향 매핑과 동일한 방법으로 관계의 주인이 Locker 객체일 뿐이다.

 

2. 다대다(N:M, ManyToMany) 관계

- 다대다 연관 관계는 실무에서 사용하지 않는 연관 관계다.

- 그러나 JPA에서 지원하는 연관 관계의 형태이므로 무엇인지 알아보자. 

 

a) 다대다 연관 관계란

- 다대다 연관 관계는 정규화된 RDB에서 표현할 수 없는 형태의 관계다.

 

- 이를 구현하기 위해서는 연결 테이블이라는 테이블과 테이블 사이의 중간 테이블을 이용하여 구현할 수 있다.

 

- 그러나 객체의 관점에서는 다대다 연관 관계를 구현할 수 있다.

- Java collections를 이용하여 관계를 맺는 양쪽 객체의 참조 리스트를 가질 수 있기 때문이다.

 

- 위의 설명과 같이, 객체에서는 가능하지만 RDB에서는 불가능한 관계가 다대다 관계이다.

- 이와 같은 이유로 JPA는 다대다 관계를 연결 테이블을 이용한 매핑 관계로 해석하여 DB에게 전달한다.

 

b) 다대다 연관 관계 구현

- 다대다 연관 관계를 구현할 때에는 @ManyToMany 어노테이션을 사용한다.

- 이때 @JoinTable을 사용하여 연결할 테이블을 설정한다.

- 다대다 연관 관계는 단방향, 양방향 모두 형성이 가능하다.

 

- 이를 단방향 매핑으로 구현하면 다음과 같다.

@Entity
public class Player {

    @Id @GeneratedValue
    @Column(name = "PLAYER_ID")
    private Long id;

    @Column(name = "PLAYER_NAME")
    private String name;

    @ManyToOne
    @JoinColumn(name = "TEAM_ID")
    private Team team;

    @ManyToMany
    @JoinTable(name = "PLAYER_LOCKER")
    private List<Locker> lockerList = new ArrayList<>();
    
    // Getter & Setter 생략
}

 

@Entity
public class Locker {

    @Id @GeneratedValue
    @Column(name = "LOCKER_ID")
    private Long id;

    private String name;
    
    // Getter & Setter 생략
}

 

- 위의 코드를 실행하면 다음과 같은 결과를 콘솔에서 확인할 수 있다.

연결 테이블 생성

 

FK 제약조건 생성

 

c) 다대다 연관 관계의 한계 

- 다대다 연관 관계는 현업에서, 실무에서 사용하지 않는 연관 관계다.

 

- 사용하지 않는 이유는 다음과 같다.

  → 연결 테이블에는 매핑된 칼럼 외 추가적인 칼럼이 삽입될 수 없다.

  → 그러므로 조회할 수 있는 데이터의 범위가 협소하다. 

  → 반드시 연결 테이블을 거쳐서 데이터를 조회해야 하므로 복잡한 쿼리가 생성 및 실행된다.

 

- 이 외에도 다양한 한계와 문제점이 존재한다. 

- 현재 시점에서는 다대다 연관 관계는 현업에서 사용하지 않는다는 정도로 이해하자. 

 

d) 다대다 연관 관계의 해결책

- 다대다 연관 관계의 문제점을 극복하려면 어떻게 해야 할까?

 

- 이에 대한 해결책은 다음과 같다. 

  → 다대다 연관 관계를 OneToMany 또는 ManyToOne 관계로 구조를 변경하고

  → 연결 테이블을 하나의 정식적인 테이블로 구현하여 Entity로써 역할하도록 만든다. 

 

- 이 방법은 다대다 관계를 형성하는 양쪽의 테이블을 모두 참조하는 새로운 테이블을 만드는 방법이다.

- 즉, 다대다 관계를 더 작은 단위의 관계로 쪼개어 직접적인 관계가 아닌 간접적인 관계로 풀어내는 방법이다.

 

 

 

댓글