BackEnd/JPA

[JPA] 양방향 연관관계 & 연관관계의 주인 (1)

JJangGu 2022. 5. 9. 02:46

이번에는 양방향 연관관계와 연관관계의 주인에 대해서 작성해보겠습니다. 기본(3) 으로 넣을까하다가 그렇기에는 중요한 개념인 것 같아서 따로 뺐습니다. 앞에 작성했던 글에서는 모두 단방향 연관관계만 다루었습니다. 양방향 연관관계부터 시작해보겠습니다.

 

양방향 연관관계

DB의 테이블에서는 단방향이라는게 없었습니다. 외래 키를 통해서 항상 양쪽 모두에게서 접근이 가능했죠. 하지만 객체간의 관계에서는 어떻게 해야할까요? 위의 예시에서 먼저 생각할 점은 다대일 관계라는 것 입니다. 

 

다시 코드를 통해서 보겠습니다. 먼저 Member 의 경우에는 이전의 예시와 다를게 없어보입니다. 

 

@Entity
public class Member {

    @Id
    @Column(name="MEMBER_ID")
    private String id;
    
    private String username;
    
    @ManyToOne
    @JoinColumn(name="TEAM_ID")
    private Team team;
    
    public void setTeam(Team team) {
    	this.team = team;
    }
    
    ...
}

 

이제 Team 엔티티의 코드를 보겠습니다. 

 

@Entity
public class Team {

    @Id
    @Column(name="TEAM_ID")
    private String id;
    
    private String name;
    
    @OneToMany(mappedBy="team")
    private List<Member> members = new ArrayList<Member>();
    
    ...
}

 

코드에 List<Member> members = new ArrayList<Member>(); 가 추가되었습니다. Member 엔티티에서 Team 을 참조했듯이 Team에서도 Member를 참조하기 위함입니다. 거기에 다대일 조건이 성립되어야 하므로 List 를 사용하였습니다. 또한 @OneToMany 를 작성하였습니다.

 

여기서 하나 헷갈리는건 @OneToMany 를 통해 일대다 관계를 매핑한 것은 알겠는데, mappedBy 가 헷갈립니다. (저는 그랬습니다;;) mappedBy 속성에 필드의 이름이 들어가서 헷갈렸던 것 같습니다. 왜냐면 그전에 @JoinColumn 에는 DB의 컬럼이 들어갔으니까요. 이 문제에 대해서 의문을 가지는게 자연스러운거라고 생각합니다. 여기서부터 연관관계의 주인이라는 개념이 출발한다고 생각합니다.

 

연관관계의 주인

먼저 mappedBy 가 정확히 이해가 가지 않는 이유는 객체에는 엄밀히 말하면 객체에는 양방향 연관관계가 없습니다. 그래서 서로 다른 단방향 연관관계 2개를 마치 양방향인 것처럼 보이게 합니다. 그렇다면 한쪽에 어떤 객체랑 매핑이 되어있는지를 정의해줘야 합니다. 

 

그것을 mappedBy를 써서 객체의 필드에 연결을 시켜준겁니다. 그렇다면 왜 객체를 양방향으로 관계를 맺을때 이런현상이 생기는지 잠깐 살펴보면, DB의 경우는 외래키 하나를 통해서 두개의 테이블을 연결합니다. 하지만 같은 경우에 객체는 참조가 두개가 생기는 차이가 발생합니다. 그러면서 객체의 연관관계를 관리하는 포인트도 2곳으로 늘어나죠.

 

그렇다면 이 두 객체에서 외래 키는 어느쪽에서 관리가 되는걸까요? 🤔

 

외래 키를 관리하는 쪽을 바로 연관관계의 주인이라고 부릅니다. 

 

연관관계의 주인만이 데이터베이스 연관관계와 매핑되고, 외래 키를 관리할 수 있습니다. 주인이 아닌쪽은 읽기만 할 수 있죠. 그렇다는 것은 연관관계의 주인은 외래 키 관리자라는 것을 뜻합니다. 외래 키를 관리한다는 것은 테이블에 외래 키를 가지고 있어야합니다. 물리적으로 키가 있어야 되는 쪽을 주인으로 선택하면 된다는 것 입니다. 

 

create table TEAM (
    id bigint generated by default as identity,
    name varchar(255)
    primary key (id)
);

create table MEMBER (
    id bigint generated by default as identity,
    username varchar(255),
    team_id bigint,
    primary key (id)
);
alter table MEMBER add constraint member_team_fkey foreign key (id) references team;

 

이렇게 테이블이 생성되었다고 하면 외래 키가 있는 쪽은 MEMBER 테이블입니다. 즉, 연관관계의 주인이라는 뜻이죠. 

 

만약 이 개념이 바로 생각이 안난다면 항상 많은 쪽이 외래 키를 가지는 연관관계의 주인이라고 생각하면 됩니다. @ManyToOne 은 항상 연관관계의 주인이 되므로 mappedBy를 설정할 수 없습니다. 저는 "쪽수 못당한다..많은 쪽이 주인이다.." 이렇게 쉽게 외우기는 했습니다..ㅎㅎ

 

쓰다보니 내용이 길어지네요. 양방향 연관관계에서의 저장과 편의메소드까지는 다음에 다시 다루겠습니다.