Java/JPA

JPA - 연관관계

ta_chan 2023. 10. 1. 18:14

 연관관계 매핑이란, JPA에서 객체 간의 관계를 데이터베이스 테이블 간의 관계와 매핑하는 것을 말한다.

 

연관관계 매핑에 대해서는 고려해야할점이 크게 3가지가 존재한다.

1. 단방향, 양방향

테이블

  • 외래키 하나로 조인 가능
  • 방향이라는 개념이 없음

객체

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

2. 연관관계 주인

  • 외래키를 관리하는 참조를 의미
  • 주인의 반대편은 단순 조회만 가능
  • 테이블은 외래키 하나로 두 테이블이 연관관계를 맺음
  • 객체의 양방향 관계는 A->B, B->A처럼 참조가 2군데

3. 다중성

엔터티 간의 관계의 다중성을 나타낸다 . A 엔터티에서 B 엔터티로의 참조가 여러 개가 가능한지 , 혹은 하나만 참조가 가능한지, 아니면 서로가 서로 여러개를 참조 할 수 있는지 등을 설정한다.

N:1

단방향

N 쪽인 엔터티가 외래키를 가지며, 이를 연관관계의 주인으로 설정한다.

@JoinColumn 어노테이션을 사용하여 연관관계의 외래키 컬럼명을 지정할 수 있다.

양방향

양방향인 경우에는 참조된 엔티티 변수에 @ManyToOne(mappedBy = "엔티티변수명") 을 추가한다. 이 때, mappedBy 속성에는 연관관계의 주인이 아닌 반대편 엔터티의 필드명을 지정한다.

1:N

1 쪽인 엔티티가 연관관계의 주인이 된다.

DB의 1:N 관계에는 언제나 N쪽에 외래키가 존재하지만, 엔티티에서는 1쪽에 외래키가 존재할 수 있기 때문에 가능한 방법이다.

하지만 이런 DB와 객체의 관계차이 때문에 설계, 운영에 어려움이 많아, 일반적으로 권장되지 않는 방법이다.

또한, 연관관계 관리를 위해 추가적으로 UPDATE SQL이 실행된다.

단방향

N:1과는 반대로 @OneToMany가 붙은 변수에 @JoinColumn을 추가하고, 외래키 컬럼명을 지정한다. 또한 @JoinColumn을 추가하지 않으면 조인 테이블 방식(중간 테이블 생성)을 사용한다.

양방향

1:N 양방향은 JPA 스펙상 존재하지는 않지만 읽기전용 필드를 사용해서 양방향처럼 구현이 가능하다.

N쪽에도 @JoinColumn을 추가한다. 추가로 N쪽에는 연관관계 주인이 되는것을 막기 위해 속성에 insertable, updatable을 모두 false로 설정하여 읽기전용으로 만든다.

1:1

주 테이블과 대상 테이블 어느쪽에도 외래키를 선택할 수 있다.

외래키에 유니크 제약조건을 걸어야 1:1이 된다.

주 테이블에 외래키 단방향

@OneToOne, @JoinColumn을 이용하여 연결한다.

주 테이블에 외래키 양방향

1:1 관계에서 양방향 관계는 참조된 엔티티 변수쪽에 마찬가지로 @OneToOne을 추가하고, mappedBy설정을 해주면 된다.

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

지원하지 않는다.

 

 

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

연관관계의 주인을 대상 테이블쪽 엔티티로 잡아서 구현한다.

개발자 입장에서는 주 테이블에 외래키가 있는것이 성능적으로 좋다. 주 테이블의 값을 조회할 때 대상테이블을 확인하는데 있어서 주 테이블만 확인하면 되기 때문이다.

반대로 대상 테이블에 외래키가 존재한다면 주테이블 엔티티만으로는 확인 할 수 없으므로 무조건적으로 대상테이블을 조회해야 한다. 그러므로 지연로딩으로 설정해도 항상 즉시로딩된다.

 

N:N

실무에서는 권장하지 않는다

 

데이터베이스에서의 N:N

관계형 데이터베이스는 정규화된 테이블 2개로 다대다 관계를 표현할 수 없다.

연결 테이블을 추가해서 1:N, N:1 관계로 풀어내야 한다.

객체에서의 N:N

객체에서는 컬렉션을 사용하여 N:N 관계를 표현 할 수 있다.

단방향

@ManyToMany 어노테이션과, 중간 테이블 생성을 위한 @JoinTable을 추가한다.

양방향

@ManyToMany.(mappedBy = "xxx")를 추가한다.

 

@ManyToMany의 한계

쿼리도 복잡하고 중간 테이블에 무엇인가 추가 정보를 넣기가 불가능하기 때문에 실무에서는 사용하기 어렵다.

 

@ManyToMany의 한계 극복

@ManyToMant, @JoinTable로 중간테이블을 생성하는 대신,

중간 테이블을 엔티티로 승격시키고, @ManyToMany를 @ManyToOne, @OneToMany로 변경한다.

중간 테이블쪽을 @ManyToOne으로 잡고 연결이 필요한 테이블들을 @OneToMany로 잡는다.