이전 포스팅의 단방향 연관관계에서 더 발전시켜서 양방향으로 만들어보겠습니다.
모든 코드는 Github에 올라와 있습니다.
양방향 연관관계
두 엔티티가 서로를 참조하는 관계
회원과 팀 예시
- 회원과 팀이 있다.
- 회원은 하나의 팀에 소속된다.
- 하나의 팀은 여러 회원을 가지고 있다.
Member와 Team을 양방향 연관관계로 만들어도 DB입장에서는 Table 변화가 없습니다.
DB에서는 Foreign Key를 가지고 Join 해서 데이터를 가지고 오기 때문에 A, B 테이블 상관없이 한쪽에만 Foreing Key가 있으면 됩니다.
Member
Member 엔티티는 단방향 연관관계 와 차이가 없습니다.
...
@Entity
public class Member {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "MEMBER_ID")
private Long id;
@Column(name = "USERNAME")
private String username;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "TEAM_ID")
private Team team;
...
}
Team
양방향 연관관계를 만들기 위해서 Team
에서 member
를 List
형태로 가지고 있습니다.
...
@Entity
public class Team {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "TEAM_ID")
private Long id;
private String name;
@OneToMany(mappedBy = "team")
private List<Member> members = new ArrayList<>();
...
}
mappedBy
: 양방향 연관관계를 설정할 때, 두 엔티티 간의 관계에서 주인(owner)과 종속(inverse) 관계를 정의
- 주인(Owner): 외래 키(FK)를 관리하는 쪽
@ManyToOne
또는@JoinColumn
을 사용한다.(Member
엔티티에 있는Team team
) - 종속(Inverse):
mappedBy
속성을 통해 주인을 가리킵니다. 주로@OneToMany
에서 사용한다.(Team
엔티티에 있는List<Member> members
)
JpaMain
위 코드를 보면 em.find
로 member
를 찾고 findMember
에서 team
으로 갔다가 team
에서 member
를 조회한 것을 보실 수 있습니다.
단방향 연관관계 에서는 Team
에서 Member
를 조회할 수 없었지만 Team
과 Member
를 양방향으로 매핑을 하면 Team
에서 Member
를 조회할 수 있습니다.
양방향 연관관계 데이터 관리는 누가 하나?
결론부터 말하자면, 양방향 연관관계에서 데이터의 수정, 삭제 등 관리는 연관관계 "주인"이 한다.
그럼 1:다 관계에서 누가 주인을 해야 하는가? 그건 바로 '다' 쪽에서 주인을 하고 데이터를 관리해야 한다.
'다' 쪽에서 주인을 하고 데이터를 관리하는 이유는 여러 가지가 있겠지만
- 데이터베이스 설계 원칙
- 성능 및 데이터 무결성
- JPA 구현체의 매핑 전략
등이 있습니다.
데이터베이스 설계 원칙
정규화 원칙에서는 외래키는 종속 관계를 가지는 테이블(Member)에 위치하는 것이 바람직합니다. 관계에서 M쪽(자식 테이블)이 1쪽(부모 테이블)을 참조하게 됩니다.
성능 및 데이터 무결성
성능 효율성: M쪽에 외래 키가 위치한 경우, 데이터 조회와 조인 연산이 효율적입니다.
- 예) 특정
Team
에 속한 모든Member
를 조회할 때Member
테이블의team_id
컬럼에 인덱스를 설정하여 빠르게 검색할 수 있습니다.
JPA 구현체의 매핑 전략
JPA에서는 @ManyToOne
관계가 기본적으로 외래 키를 관리하는 주체가 됩니다.(위 예제에서는 Member Entity의 Team team
)
정리
양방향 연관관계에는 주인과 종속이 있고 주인(Member 엔티티의 Team team
)은 데이터를 관리한다.
종속 관계(Team 엔티티의 List<Member> members
)에는 mappedBy로 주인을 가리키고 조회의 역할을 한다.
주인은 항상 1:다 관계에서 다(Member)가 된다.