본문 바로가기

Spring/JPA

일대일 연관관계 매핑

엔티티의 연관관계는 다대일, 일대다, 일대일, 그리고 다대다 관계로 나뉩니다. 다대일은 연관관계의 주인을 다쪽에 두었다는 의미이고 반대인 일대다는 일쪽에 연관관계의 주인을 두었음을 뜻합니다. 아래 글에서 다대일 연관관계에 대해 자세히 설명했으니 참고하면 좋을 같습니다.

 

다대일 연관관계

https://preyhong.tistory.com/63

 

연관관계 매핑 기초

preyhong.tistory.com

 

일대다 연관관계

다대일의 반대인 일대다 관계는 권장하지 않는 연관관계입니다. 일대다 관계에서 일쪽이 연관관계의 주인이 경우 엔티티가 관리하는 외래 키가 다른 테이블에 있는 문제가 생깁니다. 또한 연관관계 관리를 위해 추가로 UPDATE 쿼리를 날려야 합니다. 아래와 같은 그림이 일대다 단방향인데 구현이 가능하긴 하지만 지양합시다.

 

일대다 단방향 매핑보다는 다대일 양방향 매핑을 사용합시다.

일대다 양방향 매핑은 JPA에서 공식적으로 제공하지 않고 있습니다. 물론 @JoinColumn(insertable = false, updatable = false) 사용하여 insert update 불가능하도록 만들어 읽기 전용 필드를 사용해 양방향처럼 사용할 있지만 일대다 양방향 대신에 다대일 양방향을 사용합시다.

 

일대일 연관관계

일대일 관계에서는 주 테이블이나 대상 테이블 둘 중 한 테이블에 외래 키를 둘 수 있습니다. 여기서 주 테이블은 주로 접근하는 테이블을 뜻합니다. 일대일 관계가 성립하기 위해서는 외래 키에 데이터베이스 유니크(UNI) 제약조건이 추가되어야 합니다. DB 입장에서도 일대일 관계에서는 외래 키를 주 테이블 또는 대상 테이블에 둘 수 있습니다.

 

하지만 일대다 관계에서는 연관관계 주인 객체에서 반대의 테이블로 UPDATE 쿼리를 날려 업데이트할 있었지만, 일대일 관계의 경우 테이블을 연관관계의 주인으로 잡고 대상 테이블에 외래 키를 두면 업데이트할 없습니다.

 

연관관계의 주인 엔티티와 외래 키가 존재하는 테이블이 같아야 합니다. 따라서 주 테이블에 외래 키를 둘 경우 연관관계의 주인은 주 테이블의 엔티티가 되고, 대상 테이블에 외래 키를 둘 경우 연관관계의 주인이 대상 테이블의 엔티티가 됩니다.

 

그렇다면 Member Locker 엔티티를 일대일 단방향 관계로 구현하고 테이블(Member) 외래 키를 넣어보겠습니다.

일대일: 주 테이블에 외래키, 단방향

 

일대일 단방향 관계를 매핑할 때는 다대일 단방향 매핑과 유사합니다. Member 엔티티의 locker 필드에 @JoinColumn 애노테이션을 사용해 MEMBER 테이블의 LOCKER_ID와 매핑하면 됩니다. 

 

아래 그림과 같이 일대일 양방향 관계도 다대일 양방향 매핑처럼 외래 키가 있는 곳을 연관관계의 주인으로 지정하고 반대편을 mappedBy 속성으로 연관관계 거울로 지정하면 됩니다

주 테이블에 외래키, 양방향

 

다대일 관계에서는 항상 외래 키가 다 쪽에 있던 것과 달리 일대일 관계에서는 주 테이블 또는 대상 테이블에 외래 키를 둘 수 있습니다. 하지만 위에서 이야기했듯이 MEMBER 테이블에 외래 키를 넣었다면 Member 엔티티를 연관관계 주인으로 설정해야 하고 LOCKER 테이블에 외래 키를 넣었다면 Locker 엔티티가 연관관계의 주인이 되어야 합니다. 또한 JPA에서는 일대일 관계에서 대상 테이블에 외래 키가 있을 경우 단방향을 지원하지 않고 양방향만 지원합니다. 따라서 일대일 관계에서 대상 테이블에 외래 키를 넣으면 양방향 연관관계를 사용해야 합니다.

 

지금까지는 테이블인 MEMBER 테이블에 외래 키를 두고 Member 엔티티가 연관관계의 주인인 설계를 살펴봤다면, 이번에는 대상 테이블에 외래 키를 양방향 연관관계를 살펴봅시다.

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

 

대상 테이블에 외래 키를 두고 양방향 관계를 설정하면 Locker의 member 필드가 연관관계 주인이 되고, Member의 locker 필드가 연관관계 거울이 됩니다. 대상 테이블에 외래 키를 두고 양방향으로 설정하여도 매핑 방법 자체는 주 테이블에 외래 키를 둔 양방향 관계와 동일합니다.

 

결론적으로 일대일 관계에서는 본인 테이블의 외래 키는 본인이 직접 관리해야 합니다.

 

일대일 관계: 주 테이블에 외래 키 vs 대상 테이블에 외래 키

그렇다면 일대일 관계에서 외래 키를 주 테이블(MEMBER)에 두는 게 좋을까요? 아니면 대상 테이블(LOCKER)에 두는 게 좋을까요?

 

DB 입장에서 두 방법 모두 일대일 관계가 유효하게 성립합니다. 일대일 관계에서 외래 키를 어디에 넣을지는 정답은 없습니다. 하지만 비즈니스가 확장되는 상황은 충분히 고려해야 합니다. 만약 회원 한 명이 여러 라카를 가질 수 있게 된다면 LOCKER 테이블에 외래 키를 두는 게 좋을 것입니다. LOCKER 테이블에 외래 키를 두면 회원 한 명이 여러 라카를 가질 경우 MEMBER_ID에 UNI만 빼면 일대일 관계에서 일대다 관계로 쉽게 바꿀 수 있습니다. MEMBER에 외래 키가 있을 경우 LOCKER 테이블에 컬럼 추가 등 여러 변경 포인트가 생깁니다. 

 

하지만 하나의 라카가 여러 회원을 가질 수 있게 된다면 이야기가 달라집니다. 이때는 MEMBER에 외래 키가 있어야 다대일 매핑으로 변경이 쉽습니다.

 

DB 입장에서가 아닌 ORM에 매핑하여 실제로 개발해야 하는 개발자 입장에서는 MEMBER 테이블에 LOCKER_ID 외래 키가 있는 것이 성능 등 여러 측면에서 유리합니다. 보통은 Member 객체를 Locker 객체보다 많이 접근하기 때문입니다.

 

정리해 보면 주 테이블에 외래 키를 둘 경우 주 테이블만 조회해도 대상 테이블에 데이터가 있는지 확인할 수 있지만, 대상 테이블에 값이 없다면 외래 키에 null을 허용해야 하는 문제가 있습니다. 반대로 대상 테이블에 외래 키를 둘 경우 주 테이블과 대상 테이블을 일대일에서 일대다 관계로 전환할 때 테이블 구조를 유지할 수 있고 not null 조건도 추가할 수 있다는 장점이 있지만, 프록시 기능의 한계로 지연 로딩을 설정해도 항상 즉시 로딩되는 문제가 있습니다. 예를 들어 Member에서 Locker를 조회하면 MEMBER 테이블과 함께 LOCKER 테이블도 필수로 조회해야 합니다.

 

보통 객체지향 진영에서는 테이블에 외래 키를 두는 방식을 선호하고 전통적인 데이터베이스 진영에서는 대상 테이블에 외래 키를 두는 방식을 선호합니다.

'Spring > JPA' 카테고리의 다른 글

@MappedSuperclass  (0) 2024.10.17
상속관계 매핑  (2) 2024.10.17
연관관계 매핑 기초  (1) 2024.10.17
기본키 매핑  (0) 2024.10.17
영속성 컨텍스트 플러시  (1) 2024.10.16