본문 바로가기
Back-End/JPA

[JPA] 밑바닥부터 알아보는 연관관계의 주인

by blackjack_96 2024. 4. 13.

연관관계의 주인이라는 것은 어느 블로그나 자료를 봐도 이해하기가 쉽지 않았습니다.

연관관계의 주인이라는 주제를 며칠동안 파 보고서 나온 결론은 다음과 같습니다.

 

"연관관계의 주인이라는 것은,

양방향 연관관계 (Bi-Directional Relation) 에 있는 두 엔티티를 데이터베이스 테이블로 매핑할 때에,

어느 쪽 테이블에 다른 테이블을 참조할 외래키를 만들 것인가를 설정하기 위함이다"

 

단방향 연관관계

먼저 단방향 연관관계를 살펴보겠습니다.

 

예시를 들기 위해서,

Member와 Locker라는 엔티티가 존재하며,

이들은 1 : 1 관계라고 가정해 보겠습니다.

 

즉, 하나의 회원은 단 하나의 라커를 소유할 수가 있으며,

하나의 라커는 단 한명의 회원에게만 속할 수 있는 현실을 모델링 한 것입니다.

 

그리고 Member 엔티티는,

자신에게 할당된 락커의 정보를 접근하기 위한 Member.Locker라는 참조를 가지고 있다고 해 보겠습니다.

그러면 이들의 엔티티 관계를 다음과 같이 나타낼 수가 있습니다.

단방향 연관관계 엔티티 구조

 

 Member라는 엔티티의 Locker라는 필드로,

 해당 회원에게 할당된 Locker 엔티티에 접근할 수가 있습니다.

 그리고 이를 데이터베이스 테이블로 매핑을 할 경우, 다음과 같이 나타내어집니다.

단방향 연관관계 테이블

 

 

여기서 주의해서 보셔야 할 부분은,

 Member 테이블 쪽에 Locker_id라는 외래 키(FK)가 설정되었다는 것입니다.

 

단방향 연관관계일 때는 위와 같이, 

Member 엔티티에서 Locker 엔티티에 접근하도록 구현이 된 경우,

즉, Member 엔티티의 locker라는 필드로 Locker엔티티에 접근이 가능한 경우에

외래 키(FK)는 Member테이블에 할당이 됩니다.

 

 

"A와 B엔티티가 서로 단방향 연관관계(A → B)에 있을 때에,

A테이블에 외래키(FK)가 할당되어 B테이블을 참조하는 형태로 매핑된다."

 

 

 

이제는 엔티티가 서로간의 참조를 할 수 있는 양방향 연관관계일 때에

데이터베이스 테이블로 어떻게 매핑이 되는 지를 살펴보겠습니다.

양방향 연관관계에서는, 어느 쪽에 FK를 관리하게 할 것인가를 정해야한다

 

이전에는 Member에서 Locker로만 접근할 수 있는 단 방향 연관관계 였다면,

이제는 다음과 같이 Member에서 Locker,

Locker에서 Member 서로의 엔티티에 접근할 수 있도록

양방향 연관관계로 설정을 해보겠습니다.

 

 

그러면 이러한 연관관계를 반영한다면,

 데이터베이스 테이블은 이렇게 매핑이 됩니다.

 

 

하지만, 데이터베이스에서는 이와 같이 매핑이 되어서는 안 됩니다.

(중요한 내용입니다)

 

객체와 달리 데이터베이스의 테이블 끼리는, 

왜냐하면 위와 같이 Member와 Locker라는 테이블 각각이

서로에 대한 FK를 들고 있는 구조가 아니어도,

즉, 한 쪽 테이블이 다른 쪽 테이블의 PK를 참조하는 FK를 가지고 있는 형태가 되어도

JOIN 문을 통해 서로간의 양방향 참조가 가능하기 때문입니다.

 

즉, 데이터베이스의 테이블 끼리는,

다음 둘 중 한 케이스처럼 되기만 하면

Member테이블과 Locker테이블은 서로간의 정보에 접근을 할 수가 있습니다.

 

 

1. Member에서 Locker를 참조하는 FK를 관리하는 경우

 

2. Locker에서 Member를 참조하는 FK를 관리하는 경우

 

즉, 하나의 구조로만 해도 된다는 것입니다.

1. {Member 에서만 Locker 테이블에 FK를 가지는 경우}  → 첫 번째 그림에 해당

2. {Locker 에서만 Member 테이블에 FK를 가지는 경우}  → 두 번째 그림에 해당

 

 

그러면, 다음과 같이 되었던 엔티티가

다음의 그림이 아니라,

 

 

다음 두 그림처럼 매핑되게 하려면 어떻게 해야 할까요?

 

 

Member와 Locker라는 두 엔티티에

연관관계의 주인이라는 것을 설정하면 됩니다.

 

다음과 같이 Member.locker라는 필드를 연관관계의 주인으로 설정하면,

 

다음과 같은 데이터베이스 테이블로 매핑이 되고,

다음과 같이 Locker.member라는 필드를 연관관계의 주인으로 설정하면,

 

다음과 같은 데이터베이스 테이블로 매핑이 됩니다.

 

즉, 결론은 다음과 같습니다.

 

"엔티티(Entity) 끼리의 관계가 양방향 연관관계(Bi-Directional Relation)일 때에는

반드시 연관관계의 주인(Owner)이라는 것을 설정해서

어떤 테이블에서 외래 키를(FK)관리할 것인지 설정해야 합니다.

그래야 엔티티가 올바른 데이터베이스 테이블로 매핑이 됩니다."

 

 

연관관계의 주인을 설정하는 방법

 

다음과 두 경우를 구현해보겠습니다.

1.  연관관계의 주인을 Member.locker 필드로 설정한다.

  {Member 테이블에서 FK를 관리하게 된다} 

 

[Member 엔티티 구현 코드]

@Entity

...

public class Member {

    @Id
    @GeneratedValue
    @Column(name = "member_id")
    private Long id;
    private String username;
    private int age;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "team_id")
    private Team team;

    @OneToOne(mappedBy = "member")
    @JoinColumn(name = "locker_id")
    private Locker locker;

 

위에서 locker라는 필드를 주의해서 보시면 됩니다.

 

@Entity
...
public class Locker {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @OneToOne(mappedBy = "locker")
    private Member member;

    private String lockerName;
}

 

Locker엔티티의 member 필드에 @OneToOne(mappedBy = "locker")라는 어노테이션이 붙어있습니다.

이것은 다음을 선언하는 것입니다.

 

"locker라는 필드명으로 본 엔티티(Locker)를 조회하는 쪽이 연관관계의 주인이다"

 

 연관관계의 주인은 이렇게,

 참조당하는 엔티티 쪽에서 mappedBy옵션을 사용해서 설정정할 수가 있습니다.

 

그러면 이 경우는 다음과 같이 데이터베이스 테이블 매핑이 이루어집니다.

 

 

 

2.  연관관계의 주인을 Locker.member 필드로 설정한다.

  {Locker 테이블에서 FK를 관리하게 된다} 

 

위와 같은 방법으로(mappedBy 옵션) 연관관계의 주인을 설정하면 됩니다.

연관관계의 주인을 Locker.member필드로 두면

다음과 같은 데이터베이스 테이블로 매핑이 될 것입니다.

 

이상으로 연관관계의 주인 개념에 대하여 알아보았습니다.