다양한 연관관계 매핑

김민우·2022년 9월 3일
0

JPA

목록 보기
5/10

연관관계 매핑시 고려해야할 사항은 크게 3가지이다.

  1. 다중성
  2. 단방향, 양방향
  3. 연관관계의 주인

하나씩 알아보자.


다중성

다중성은 4가지가 있다.

  • 다대일: @ManyToOne
  • 일대다: @OneToMany
  • 일대일: @OneToOne
  • 다대다: @ManyToMany

DB의 다중성을 기준으로 어노테이션을 고르면 된다. 애매하면 반대쪽을 생각하자!
(일대일의 반대는 일대일 다대일의 반대는 일대다, 이들은 대칭성이 존재한다.)

사실, 다대다는 실무에서 절대 사용을 하지 않는다.
가장 많이사용하는 것은 다대일, 일대다 이다. 일대일도 가끔은 나온다.

하나씩 자세히 알아보자.

다대일 [N:1]

다대일 단방향

  • 가장 많이 사용되는 연관관계이며 다대일의 반대는 일대다 이다.
  • FK가 있는 곳에 참조를 걸고 연관관계를 매핑하면 된다.

다대일 양방향

  • FK가 있는 쪽이 연관관계의 주인이다.
  • 양쪽을 서로 참조하도록 개발한다.

다가 연관관계의 주인이다. 따라서, 다에 FK가 가야한다. RDB의 관점에서 보면 이렇게 해야 설계에 유리하다.

단방향 양뱡향은 테이블 설계에 영향을 주지 않는다.

양방향으로 하기 위해선 객체의 설계를 다르게 하면 된다. 단순히 코드만 추가하면 된다는 뜻이다.

일대다 [1:N]

일대다 단방향

  • 일대다 단방향은 일대다(1:N)에서 일(1)이 연관관계의 주인이다.
  • 테이블 일대다 관계는 항상 다(N)쪽에 외래 키가 있다.
  • 객체와 테이블의 차이 때문에 반대편 테이블의 외래 키를 관리하는 특이한 구조이다.
  • @JoinColumn 을 꼭 사용해야 한다. 그렇지 않으면 조인 테이블 방식을 사용한다.(중간에 테이블을 하나 추가함)

일대다 양방향

  • 이런 매핑은 공식적으로 존재하지 않는다.
  • 주인이 아닌 엔티티에 @JoinColumn(insertable=false, updatable=false)을 사용하면 된다.
  • 읽기 전용 필드를 사용해서 양방향 처럼 사용하는 방법이다.
  • 말할 것도 없다. 그냥 다대일 양방향을 사용하자

그러나, 일대다는 엔티티가 관리하는 외래 키가 다른 테이블에 있고 연관관계 관리를 위해 추가로 UPDATE SQL 실행하므로 성능상 손해가 존재한다.

하지만, 안쓰는 가장 큰 이유는 작성한 코드와 쿼리의 차이로 인해 혼란을 야기한다는 점이다.
(난 분명 Team 객체 관련 코드만 작성했는데 Member 쿼리가 나가면 당황스러울것이다...)

따라서, 실무에서 이러한 모델은 거의 사용하지 않는다.

일대일 [1:1]

일대일 관계는 그 반대도 일대일이므로 주 테이블이나 대상 테이블 둘 중 아무대나 FK를 넣어도 된다.

이 둘을 기준으로 나눠서 알아보자.

주 테이블에 외래 키 단방향

  • 다대일( @ManyToOne ) 단방향 매핑과 유사하다.

주 테이블에 외래 키 양방향

  • 다대일 양방향 매핑처럼 FK가 있는 곳이 연관관계의 주인이다.
  • 반대편은 mappedBy를 적용한다.

대상 테이블에 외래 키 단방향

  • 단방향 관계는 JPA 지원X
  • 양방향 관계는 지원한다.

대상 테이블에 외래 키 양방향

  • 일대일 주 테이블에 외래 키 양방향과 매핑 방법은 같다.

참고

@JoinColumn(name = "")

name 값은 디폴트값이 매우 복잡하므로 직접 입력하자.

일대일 정리

  1. 주테이블에 FK
  • 주 객체가 대상 객체의 참조를 가지는 것 처럼 주 테이블에 외래 키를 두고 대상 테이블을 찾는다.
  • 객체지향 개발자 선호
  • JPA 매핑 편리
  • 장점: 주 테이블만 조회해도 대상 테이블에 데이터가 있는지 확인 가능
  • 단점: 값이 없으면 외래 키에 null 허용
  1. 대상테이블에외래키
  • 대상 테이블에 외래 키가 존재
  • 전통적인 데이터베이스 개발자 선호
  • 장점: 주 테이블과 대상 테이블을 일대일에서 일대다 관계로 변경할 때 테이블 구조 유지
  • 단점: 프록시 기능의 한계로 지연 로딩으로 설정해도 항상 즉시 로딩됨(프록시는 뒤에서 설명)

일대일관계는 내 Entity에 있는 FK는 직접관리해야 한다.

일대일 관계에서 FK를 어떤 테이블에 적용해도 상관이 없지만 미래를 고려해보자.
비지니스 룰이 바뀌어서 일대일에서 일대다로 바뀌면 주 테이블에 FK가 있는게 대응하기가 쉽다.
반대로 다대일로 바뀌면 대상 테이블에 FK가 있는게 대응하기 쉽다.

실무에선 주 테이블에 FK를 두는게 더 낫다.

프록시 객체를 두면 JPA는 필드값이 있는지를 반드시 알아야 한다.
대상 테이블에 FK를 두면 어차피 조회할 때 값을 알고 있으므로 지연로딩이 의미가 없어진다.

다대다 [N:M]

RDB는 정규화된 테이블 2개로 다대다 관계를 표현할 수 없다. 연결 테이블을 추가해서 일대다, 다대일 관계로 풀어내야한다.

물론, 객체는 컬렉션을 사용해서 다대다 관계를 구현할 수 있다.

@ManyToMany 을 사용하면 되고 @JoinTable로 연결 테이블을 지정한다. 또한, 단방향, 양방향 모두 가능하다.

편리해 보이지만 실무에서 절대 사용하지 않는다. 연결 테이블이 단순히 연결만 하고 끝나지 않는다.

다음과 같이 주문시간, 수량 같은 데이터가 들어올 수 있다.

참고

@JoinColumn(...)

FK 제약조건을 매핑하는 것이 아니라 FK를 매핑하는 것이다.

연결 테이블용 엔티티 추가(연결 테이블을 엔티티로 승격)함으로써 다대다 한계를 극복할 수 있다.

  • @ManyToMany -> @OneToMany, @ManyToOne

단방향, 양방향

테이블

  • 외래 키 하나로 양쪽 조인이 가능하다.
  • 사실 방향이라는 개념이 없다.

객체

  • 참조용 필드가 있는 쪽으로만 참조 가능하다.
  • 한쪽만 참조하면 단방향, 양쪽이 서로 참조하면 양방향이다.

계속 언급하지만 객체의 양방향은 단방향이 2개가 있는 것이다.
이렇게 이해해야 연관관계의 주인을 확실히 이해할 수 있다.

0개의 댓글