JPA를 공부하며 기록이 필요하다고 생각되는 내용들을 작성하고 있습니다.
혹시라도 틀린 부분이 있다면 알려주시면 감사드리겠습니다 😁
다대일 관계에서 단방향이 아닌 양방향을 만드는 경우가 있습니다.
저는 개발을 할때 되도록이면 단방향으로 만들려고 하지만, 양방향이 필요한 경우도 분명 있습니다.
예를 들어 하나의 게시글을 조회할 때, 해당하는 답글들을 모두 가져오기 위해서는 양방향으로 설정을 해두면 훨씬 편리하게 코드를 작성할 수 있습니다.
하지만, N:1 양방향 연관관계를 만들 경우 연관관계의 주인을 설정하는 일은 가끔 헷갈릴 때가 있었습니다.
이 부분에 대해 정리를 해두려 합니다 😃
가장 먼저 양방향 연관관계가 어떤 것일까요? 간단한 단방향 관계를 놔두고 양방향 관계를 설정하려 하는 것일까요?
그 이유 중 하나는 객체 지향 언어와 관계형 데이터베이스간의 차이에 있습니다.
Member
와 Team
이 있다고 생각해보겠습니다.
하나의 팀에는 여러명의 회원이 가입할 수 있습니다.
즉, Member
와 Team
은 N:1 관계가 됩니다.
간단하게 그린 테이블 관계입니다
이 테이블 관계를 단방향 객체 연관관계로 표현해보겠습니다.
두 그림의 차이가 느껴지시나요?
단방향 연관관계는 member.getTeam()
을 통해 team에 접근할 수 있지만, 반대로 team.getMember()
는 할 수 없습니다.
하지만, 테이블은 외래키(Foriegn key) 조인을 통해 member
테이블에서도 team
테이블을 조인할 수 있고, team
에서도 member
를 조인할 수 있습니다.
이런 객체지향언어와 관계형 데이터베이스간의 차이때문에, 양방향 연관관계, 즉 team
에서도 member
를 참조할 수 있는 관계가 나오게 된 것입니다.
이 그림을 보니 약간 이상한 점이 하나 느껴지지 않나요?
Member 엔티티의 team 값을 바꿨을 때, Member 테이블의 team_id가 바뀌어야 하는건지, 아니면, Team 엔티티의 members를 수정했을 때, Member 테이블의 team_id가 바뀌어야 하는건지..
기존 단방향 연관관계 엔티티를 먼저 보겠습니다.
@ManyToOne
을 Member
엔티티에 추가함으로 써, 다대일 관계를 만들었습니다.
이를 통해 우리는 member.getTeam()
을 호출함으로써, 해당 멤버가 속한 팀을 조회할 수 있습니다.
하지만, 반대로 team
에서는 member
로 갈 수 있는 방법이 없습니다.
이번에는 양방향 연관관계로 설정해보겠습니다.
추가할 내용은 딱 하나밖에 없습니다. Team
엔티티에 members 리스트 변수를 추가하고, 1:N 관계인 @OneToMany(mappedBy = "team")
을 추가해주면 됩니다.
mappedBy
에 적을 매핑 정보는 Member
에서 이 필드와 매핑되는 필드 이름인 team
을 주면 됩니다.
이렇게 작성하고나면, 해당 팀에 가입되어있는 멤버들을 이 members
필드로 조회할 수 있게 됩니다.
양방향 연관관계를 만들고나니 궁금한 점이 하나 생깁니다.
@OneToMany는 Team 입장에서 Member와 1:N 관계라 작성한 것인데 mappedBy는 무엇일까?
양방향 연관관계에서는 연관 관계 주인이 필요합니다.
연관 관계의 주인만 데이터베이스 연관관계와 매핑되고, 외래키를 등록, 수정, 삭제 시킬 수 있는 것입니다. 반면에 연관 관계의 주인이 아닌 반대쪽은 읽기만 가능하게 됩니다.
이런 이유로 @OneToMany
에 mappedBy
를 붙혀 연관 관계의 주인이 아니라는 표시를 해두는 것입니다.
즉, 아무리 team 엔티티에서 members를 가져와서 변경시키더라도, Member 테이블은 변하지 않습니다.
이번 포스팅에서 하고 싶었던 이야기가 드디어 나오게 됬네요.
그러면 도대체 연관 관계의 주인은 누구로 설정을 해야되는 걸까요??
연관 관계의 주인을 정하는 것은 결국 외래 키 관리자를 선택하는 일입니다.
즉 외래키가 있는 곳을 연관 관계의 주인으로 선정하면 됩니다.
두가지 상황에 대해 생각해 보겠습니다.
첫째, Member.team
을 주인으로 선택하는 경우, 외래키가 있는 본인과 매핑되어있는 테이블 Member
를 관리하면 됩니다.
둘째, Team.members
를 주인으로 선택하는 경우 외래키는 전혀 다른 Member
테이블에 있어서 관리가 어려워 지게 됩니다.
만약, 연관 관계 주인을 두번째 경우처럼 설정하는 경우 Team.members
를 수정하면, 업데이트 쿼리는 Update Member ~~
가 되게 됩니다. 즉 전혀 다른 테이블에 쿼리가 나가게 되는 것입니다.
테이블이 복잡해지고 많아지면, 이런 경우는 분명 큰 어려움이 생길 것입니다.
사실 이런 이유로 JPA의 @ManyToOne
에는 mappedBy 속성이 없습니다. 관계형 데이터베이스에서 N:1은 무조건 N쪽이 외래키를 가지게 되어 연관 관계의 주인이 됩니다. 하지만 위와 같은 고민을 해보고 JPA를 사용할 경우 훨씬 더 확실히 이해를 할 수 있고, 설계도 원활히 할 수 있을 것이라고 생각합니다!
다음 포스팅에서는 양방향 연관관계에서 데이터를 추가하거나 업데이트 하는 것에 대해 다뤄보도록 하겠습니다 😃