단방향 연관관계
연관관계 중에선 다대일(N:1) 단방향 관계를 가장 먼저 이해해야 한다.
회원과 팀의 관계를 통해 다대일 단방향 관계를 알아보자.
![](https://blog.kakaocdn.net/dn/dkDBNV/btscRqzkvPf/SWNGKAEQp60blKMh8x5KoK/img.png)
· 회원과 팀이 있다.
· 회원은 하나의 팀에만 소속될 수 있다.
· 회원과 팀은 다대일 관계다.
▼ 객체 연관관계
- 회원 객체는 Member.team 필드(멤버변수)로 팀 객체와 연관관계를 맺는다.
- 회원 객체와 팀 객체는 단방향 관계다. 회원은 Member.team 필드를 통해서 팀을 알 수 있지만 반대로 팀은 회원을 알 수 없다.
▼ 테이블 연관관계
- 회원 테이블은 TEAM_ID 외래 키로 팀 테이블과 연관관계를 맺는다.
- 회원 테이블과 팀 테이블은 양방향 관계다. 회원 테이블의 TEAM_ID 외래 키를 통해서 회원과 팀을 조인할 수 있고 반대로 팀과 회원도 조인할 수 있다.
객체 관계 매핑
JPA를 사용해서 매핑해본다.
![](https://blog.kakaocdn.net/dn/bmZXvc/btscQQEZfyW/K5nrG4g7a86THcEn3r4zkK/img.png)
<매핑한 회원 엔티티>
@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;
}
//Getter, Setter...
}
<매핑할 팀 엔티티>
@Entity
public class Team {
@Id
@Column(name = "TEAM_ID")
private String id;
private String name;
//Getter, Setter...
}
양방향 연관관계
지금까지 회원에서 팀으로만 접근하는 다대일 단방향 매핑을 알아보았다. 이번에는 반대 방향인 팀에서 회원으로 접근하는 관계를 추가하자.
![](https://blog.kakaocdn.net/dn/S1rt5/btscQQLKMKl/NmU9QTcwismmL4q3nU9ktK/img.png)
그림과 같이 회원과 팀은 다대일 관계이고, 반대로 팀에서 회원은 일대다 관계이다. 일대다 관계는 여러 건과 연관관계를 맺을 수 있으므로 컬렉션을 사용해야 한다. Team.members를 List컬렉션으로 추가했다.
테이블의 관계는 어떻게 될까?
데이터베이스 테이블은 외래 키 하나로 양방향으로 조회할 수 있다.
![](https://blog.kakaocdn.net/dn/bjtC0F/btscS4WCK2j/i4CM6KmjybtlDikKk0TfuK/img.png)
두 테이블의 연관관계는 외래 키 하나만으로 양방향 조회가 가능하므로 처음부터 양방향 관계다.
MEMBER JOIN TEAM도 가능하고, 반대인 TEAM JOIN 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;
}
//Getter, Setter...
}
<매핑한 팀 엔티티>
@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>();
//Getter, Setter ..
}
팀과 회원은 일대다 관계다. 따라서 팀 엔티티에 컬렉션인 List<Member> members 를 추가했다. 그리고 일대다 관계를 매핑하기 위해 @OneToMany 매핑정보를 사용했다. mappedBy 속성은 양방향 매핑일 때 사용하는데 반대쪽 매핑의 필드 이름을 값으로 주면 된다.
연관관계의 주인
단순히 @OneToMany 만 있으면 되지 mappedBy는 왜 필요할까?
엄밀히 이야기하면 객체에는 양방향 연관관계라는 것이 없다. 서로 다른 단방향 연관관계 2개를 애플리케이션 로직으로 잘 묶어서 양방향인 것처럼 보이게 할뿐이다. 반면에 데이터베이스 테이블은 앞서 설명했듯이 외래 키 하나로 양쪽이 서로 조인할 수 있다. 따라서 테이블은 외래 키 하나만으로 양방향 연관관계를 맺는다.
객체 연관관계는 다음과 같다.
- 회원 → 팀 연관관계 1개 (단방향)
- 팀 → 회원 연관관계 1개 (단방향)
테이블 연관관계는 다음과 같다.
- 회원 ↔ 팀의 연관관계 1개 (양방향)
엔티티를 단방향으로 매핑하면 참조를 하나만 사용하므로 이 참조로 외래키를 관리하면 된다. 그런데 엔티티를 양방향으로 매핑하면 서로를 참조하므로 객체의 연관관계를 관리하는 포인트는 2곳으로 늘어난다.
엔티티를 양방향 연관관계로 설정하면 객체의 참조는 둘인데 외래키는 하나다. 따라서 둘 사이에 차이가 발생한다.
이런 차이로 인해 JPA에서는 두 객체 연관관계 중 하나를 정해서 테이블의 외래키를 관리해야 하는데 이것을 연관관계의 주인이라 한다.
양방향 연관관계 매핑 시 지켜야 할 규칙이 있는데 두 연관관계 중 하나를 연관관계의 주인으로 정해야 한다. 연관관계의 주인만이 데이터베이스 연관관계와 매핑되고 외래키를 관리(등록, 수정, 삭제)할 수 있다. 반면에 주인이 아닌 쪽은 읽기만 할 수 있다.
어떤 연관관계를 주인으로 정할지는 mappedBy 속성을 사용하면 된다.
- 주인은 mappedBy 속성을 사용하지 않는다.
- 주인이 아니면 mappedBy 속성을 사용해서 속성의 값으로 연관관계의 주인을 지정해야 한다.
![](https://blog.kakaocdn.net/dn/wJZdQ/btscQcayqz6/UHH44ESYbzLkLx0j8EpWTK/img.png)
그렇다면 Member.team, Team.members 둘 중 어떤 것을 연관관계의 주인으로 정해야 할까?
연관관계의 주인을 정한다는 것은 사실 외래키 관리자를 선택하는 것이다. 여기서는 회원 테이블에 있는 TEAM_ID 외래키를 관리할 관리자를 선택해야 한다.
만약 회원 엔티티에 있는 Member.team을 주인으로 선택하면 자기 테이블에 있는 외래키를 관리하면 된다. 하지만 팀 엔티티에 있는 Team.members를 주인으로 선택하면 물리적으로 전혀 다른 테이블의 외래키를 관리해야 한다. 왜냐하면 이 경우 Team.members 가 있는 Team 엔티티는 TEAM 테이블에 매핑되어 있는데 외래키는 MEMBER 테이블에 있기 때문이다.
연관관계의 주인은 외래키가 있는 곳
연관관계의 주인은 테이블에 외래 키가 있는 곳으로 정해야 한다. 여기서는 회원 테이블이 외래키를 가지고 있으므로 Member.team이 주인이 된다. 주인이 아닌 Team.members 에는 mappedBy="team" 속성을 사용해서 주인이 아님을 설정한다.
class Team {
@OneToMany(mappedBy = "team")
private List<Member> members = new ArrayList<Member>();
...
}
'JPA' 카테고리의 다른 글
다양한 연관관계 매핑 (2) (0) | 2023.04.27 |
---|---|
다양한 연관관계 매핑 (1) (0) | 2023.04.27 |
엔티티 매핑 (0) | 2023.04.27 |
영속성 관리 (2) (0) | 2023.04.27 |
영속성 관리 (1) (0) | 2023.04.27 |