양방향 매핑
양방향 매핑이란 아래의 사진과 같이 서로 다른 두객체가 서로를 참조하고 있는 형태이다.
객체 연관관계는 다음과 같다.
회원 ➡️ 팀 (team)
팀 ➡️ 회원 (members)
여기서 Team과 Member는 일대다 관계를 맺고 있어 List 컬렉션을 사용해준다.
위의 객체 관계를 테이블로 표현하면 아래와 같다.
데이터베이스에선 PK와 FK를 통해 일대다 관계 설정과 양방향 조회를 할 수 있다.
양방향 연관관계 매핑
이제 코드를 통해 실제로 JPA로 어떻게 양방향 관계를 맺는지 알아보자.
Team.class
@Entity
public class Team {
@Id @GeneratedValue
@Clumn(name = "TEAM_ID")
private Long id;
private String name;
@OneToMany(mappedBy = "team")
private List<Member> members = new ArrayList<>();
// Getter
// Setter
}
- @OneToMany : 하나의 Team은 여러개의 Member를 가질 수 있다.
- List<Member> members : 일대다 관계인 여러개의 Member를 가지기 위해 Collection 사용. NPE를 막기 위해 new로 초기화까지 해주는 것이 관례이다.
- mappedBy : 양방향 매핑이 되는 반대편 사이드에 걸려있는 필드명을 써주면 된다. 상세한 내용은 아래 기술할 예정이다.
Member.class
@Entity
public class Member {
@Id
@Column(name = "MEMBER_ID")
private String id;
private String username;
@ManyToOne
@JoinColumn(name="TEAM_ID")
private Team team;
// Getter
// Setter
}
- @ManyToOne : 여러개의 Member는 하나의 Team을 가질 수 있다.
- Team team : 다대일 관계인 하나의 Team을 가지기 위해 단일 객체로 선언한다.
- @JoinColumn : 양방향 매핑이 되는 반대편 사이드의 Id값의 컬럼명을 써주면된다.
정리하면, Team과 Member는 일대다 관계이다.
Team에서는 @OneToMany를 사용한 List<Member>를 통해 여러개의 Member를 매핑하였다.
반대로 Member는 @ManyToOne을 사용한 Team을 통해 한개의 Team에 매핑하였다.
양방향 연관관계 조회
try{
Team team = new Team();
team.setName("TeamA");
em.persist(team);
Member member = new Member();
member.setUsername("member1");
member.setTeam(team);
em.persist(member);
em.flush();
em.clear();
// Member에서 Team으로 Team에서 Member로
Member findMember = em.find(Member.class, member);
List<Member> members = findMember.getTeam().getMembers();
members.forEach(System.out::println);
// Team에서 Member로
Team findTeam = em.find(Team.class, team.getId());
findTeam.getMembers().forEach(System.out::println);
...
}
위의 코드를 보면 양방향 연관관계를 통해 Team에서 Member를 조회할 수 있고 그 Member에서 다시 Team을 조회할 수도 있는 구조로 되어 있어 객체를 계속 왔다갔다할 수 있다.
실제로 양방향으로 설계할 경우 고려해야할 상황이 많아, 현업에선 보통 단방향위주로 하며 이것을 권장한다고 한다.
연관관계의 주인과 mappedBy
위의 연관관계 매핑에서 넘어간 mappedBy를 왜 사용하는가에 대해 자세히 알아보자.
객체는 사실 양방향 관계가 아니라, 서로 다른 단방향 관계 2개이다.
따라서 객체를 양방향으로 참조하려면 아래의 코드와 같이 단방향 연관관계를 2개 만들어야한다.
class A {
B b;
}
class B {
A a;
}
이와 다르게 DB의 테이블에서는 하나의 외래 키로 양방향 연관관계를 가질 수 있다.
SELECT *
FROM MEMBER M
JOIN TEAM T ON M.TEAM_ID = T.TEAM_ID;
SELECT *
FROM TEAM T
JOIN MEMBER M ON T.TEAM_ID = M.TEAM_ID;
위의 코드를 보면 MEMBER.TEAM_ID 외래 키 하나로 양방향 연관관계를 맺었다. (Join)
만약, 객체에서 Fk값을 바꿀 때 양방향매핑이라면 Team, Member 양쪽에서 다 바꿔도 바껴야 되는게 맞다.
이러한 상황때문에 양방향 매핑에는 규칙이 있다.
양방향 매핑 규칙
- 객체의 두 관계 중 하나를 연관관계의 주인으로 지정해야 한다.
- 연관관계의 주인만이 외래 키를 관리(등록, 수정) 한다.
- 주인이 아닌 쪽은 조회만 가능하다.
- 주인은 mappedBy 속성을 사용 안한다.
- 주인이 아니면 mappedBy 속성으로 주인을 지정한다.
연관관계의 주인
그렇다면 연관관계의 주인은 뭘까?
연관관계의 주인을 정한다는 것은 외래키의 관리자를 정하는 것이다.
따라서 연관관계의 주인은 외래키가 있는 곳을 주인으로 지정해야한다.
왜일까?
Team과 Member의 관계에서 만약 Team이 연관관계의 주인이 된다면, Team객체에서 Member객체를 생성하고 수정하고 삭제하게 된다. 이는 물리적으로 전혀 다른 테이블의 Fk를 관리하는 것이기에 올바르지 않은 경우라 할 수 있다.
따라서 DB상 Fk를 가지고 있는 테이블의 객체가 연관관계의 주인이 되어야 한다.
본 내용은 아래의 강의를 참고했습니다.
'Framework > 🍃Spring' 카테고리의 다른 글
JPA - 연관관계의 매핑 [N:1다대일 | 1:N 일대다] (0) | 2022.12.20 |
---|---|
JPA - 양방향 연관관계와 연관관계 주인 - 2 (0) | 2022.12.15 |
JPA - 기본키 매핑 (0) | 2022.12.13 |
JPA - 플러시(Flush)란? (2) | 2022.12.01 |
JPA - 영속성 컨텍스트란? (0) | 2022.11.25 |
댓글