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

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

by devraphy 2022. 4. 4.

0. 개요

- 이번 포스팅에서는 다양한 연관 관계의 종류에 대해서 알아보도록 하자.

 

a) 연관 관계의 종류

- 연관 관계에는 다음과 같은 종류가 있다. 

   → 다대일(N:1, @ManyToOne)

   → 일대다(1:N, @OneToMany)

   → 일대일(1:1, @OneToOne)

   → 다대다(N:N, @ManyToMany)

 

- 위의 4가지 형태의 연관 관계에 대하여 알아보도록 하자.

 

1. 연관 관계 매핑 시 고려사항

a)  다중성

- 위에서 4가지 종류의 연관 관계가 있다는 것을 알았다.

- 이 연관 관계를 매핑할 때에는 다중성을 고려해야 하는데, 다중성이란 다음과 같다.

  ex) 하나의 주문이 여러 개의 아이템을 가지는 관계 

  ex) 하나의 팀이 여러 명의 선수를 가지는 관계 

 

- 이처럼 연관 관계의 대상이 다수의 데이터를 보유할 수 있는 관계인지를 파악해야 올바른 관계를 형성할 수 있다.

- 즉, DB 관점에서의 다중성을 고려해야 한다. 

 

- 만약 다중성이 헷갈리는 경우, 연관 관계의 대상의 입장에서 생각해보면 된다.

- 모든 관계는 대칭을 이루기 때문이다. 

  ex) 팀 → 선수는 OneToMany의 관계다. 선수 → 팀은 ManyToOne의 관계다.

 

* 알아두자!

  - 사실 다대다(N:N) 관계는 사용하면 안 되는 연관 관계다.

  - 왜 사용하면 안 되는지, 어떻게 대체할 수 있는지 추후에 언급하도록 하겠다.

 

b) 단방향, 양방향

- 연관 관계의 방향성을 고려해야 올바른 관계를 형성할 수 있다.

- 이전 포스팅에서도 강조한 내용이지만 다시 한번 복습해보자.

 

- 테이블은 FK를 생성하여 양방향 연관 관계를 형성한다. 그러므로 테이블은 관계의 방향이 애초에 없다.

 

- 객체는 참조 필드가 존재하는 Entity만 참조가 가능하다.

- 연관 관계에서 참조 필드가 한쪽에만 존재하면 단방향, 양쪽에 모두 참조 필드가 존재하면 양방향 매핑이 된다.

- 즉, 서로를 향하는 단방향 매핑이 2개 있는 것일 뿐 양방향 매핑이 따로 존재하는 것은 아니다.

 

- 다만, 단방향 매핑으로 모든 관계를 표현할 수 있기에 최초에는 단방향 매핑을 기준으로 설계한다.

- 그러므로 추가적인 조회 기능이 필요한 경우에만 양방향 관계를 형성하는 것을 권장한다.

 

c) 연관 관계의 주인

- 양방향 매핑을 형성할 때에는 반드시 연관 관계의 주인을 결정해야 한다.

- 그 이유는 다음과 같다.

 

- 테이블은 FK 하나로 양방향 관계를 형성한다.

- 그러나 객체는 단방향 매핑 2개를 이용하여 양방향 관계를 형성한다.

- 그러므로 객체에서 양방향 매핑을 이용하여 데이터를 삽입할 때, 그 기준을 정해줘야 한다.

- 즉, 객체를 이용하여 데이터를 삽입할 때 어떤 필드가 FK의 역할을 하는지를 JPA에게 알려줘야 하는 것이다.

 

- 결론적으로 연관 관계 주인을 결정하는 것은 FK를 지정하는 것과 동일하다.

- 연관 관계의 주인을 FK로 FK의 값을 업데이트할 수 있으며, 주인의 반대에 있는 Entity는 값의 조회만 가능하게 된다.

 

2. 다대일(N:1, ManyToOne) 관계

a) 다대일 연관 관계란

- DB의 관점에서 보면 다대일 연관 관계는 다음과 같은 테이블 구조를 이룬다.

 

- 다수의 Player는 하나의 Team에 속해있다. (Many To One)

 

b) ManyToOne의 연관 관계 주인

- 다대일(N:1) 관계는 다수(Many)의 입장이 연관 관계의 주인이 된다. 

- 위의 예시에서는 다수(Many)의 입장은 Player가 된다.

- 그러므로 Player가 연관 관계의 주인이 된다. 

 

- 가장 많이 사용하는 연관 관계이다.

 

3. 일대다(1:N, OneToMany) 관계

a) 일대다 연관 관계란

- DB의 관점에서 보면 일대다 연관 관계는 다음과 같은 테이블 구조를 이룬다.

 

- 하나의 Team은 다수의 Player를 보유하고 있다. (One To Many)

 

b) OneToMany의 연관 관계 주인

- 일대다 관계는 일(One)의 입장이 연관 관계의 주인이 된다.

- 그러나 위의 테이블 구조를 살펴보면, FK는 다수(Many)인 Player 테이블에 존재한다.

 

- 이것이 어떻게 가능할까? 

- 검색의 주체가 Team이 되는 것이다. 즉, Team 객체를 이용하여 Player 정보를 검색하는 상황인 것이다.

 

c) OneToMany 구현의 문제점

- 우선 OneToMany 관계는 실무에서 사용하지 않는 것을 권장하는 구조다.

- RDB 입장에서 고려해보면 일대다의 관계는 올바르지 않은 구조를 갖기 때문이다.

- 그 이유를 차근차근 알아보자. 

 

- 위의 테이블 구조를 객체로 바꿔보면 다음과 같이 형성된다.

 

- 객체 관점에서 보면, Team이 Player 정보를 요구하는 관계다.

- 다시 말해서 Team 정보를 이용하여 Player 정보를 조회하는 상황이다. 

 

- RDB 관점에서는 다수(Many)의 입장이 FK를 가져야 올바른 설계다.

- 그러므로 FK를 가지는 Entity가 연관 관계의 주인이 되어야 한다. 

 

- 그러나 객체의 연관 관계는 이를 반대로 설정하고 있기 때문에, 처리과정이 복잡해진다.

- 연관 관계의 주인이 Team Entity가 되기 때문에 Team 객체에서 Player의 team_id 값을 변경하는 경우

   Player 객체의 team_id를 update 해줘야 한다.

 

- 조금 이질감이 있지만, 위의 연관 관계를 코드로 작성하면 다음과 같다.

@Entity
public class Player {

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

    @Column(name = "PLAYER_NAME")
    private String name;
    
    // Getter & Setter 생략
}
@Entity
public class Team {

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

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

    @OneToMany
    @JoinColumn(name = "TEAM_ID")
    private List<Player> players = new ArrayList<>();
    
    // Getter & Setter 생략
}

 

* 알아두자!

- OneToMany 관계를 구현할 때에는 반드시 @JoinColum을 사용해야한다.

- @JoinColumn을 사용하지 않으면 default 설정으로 Join Table 방식을 사용하기 때문이다. 

- JoinTable 방식을 사용하면 두 테이블을 조인한 중간 테이블이 DB에 추가적으로 생성된다. 

 

- 위의 코드를 바탕으로 데이터를 저장해 보면, 다음과 같은 코드를 작성하게 된다.

 

- 조금 이상한 부분이 빨간 줄을 친 코드다.

- team 객체를 이용하여 player 정보를 삽입하는 것처럼 보이지만, 

- 결국 team 객체를 통해 FK를 가지고 있는 Player 테이블의 team_id 값에 접근하게 된다.

 

- 위의 코드를 실행시켜보면 다음과 같은 SQL이 콘솔에 출력된다.

 

- 콘솔에 출력된 내용을 읽어보면 one-to-many 관계에 의해 player 테이블에 update가 실행된 것을 볼 수 있다. 

- 더불어, 아래에는 UPDATE 쿼리문이 DB로 전달됨을 확인할 수 있다. 

 

 

- DB에는 위와 같이 데이터가 잘 저장된 것을 확인할 수 있다. 

 

d) 일대다 관계는 권장하지 않는다.

- 이와 같은 이유로 OneToMany 관계는 구현하는 것을 권장하지 않는다.

- 위의 예제에서는 2개의 테이블만 이용하여 연관 관계를 구축했지만, 실무에서는 수십 또는 수백 개의 테이블이 돌아간다.

- 이와 같은 상황에서 OneToMany 관계를 일일이 신경 쓸 수 없기 때문에 실수가 발생하기 마련이다.

 

e) 일대다 관계 구현의 해결책

- OneToMany 관계를 ManyToOne 관계로 구현하여 양방향 매핑을 사용하는 것을 권장한다.

- OneToMany 관계라고 할지라도, 객체 구조상 양방향 매핑을 통해 다수(Many)를 담당하는 Entity가

  주인이 되도록 설정한다.

 

 

 

댓글