양방향 매핑
이전에 Member는 Team을 통해 team의 정보를 얻을 수 있었다.
그렇다면 Team에서는 Member로 갈 방법이 없을까?
findTeam.getMember() 를 사용할 수 없다.
그러므로 Team에다가 List<Member> members를 만들어줘야한다.
Team 클래스
@Entity
@Getter
@Setter
public class Team {
@Id @GeneratedValue
@Column(name = "TEAM_ID")
private Long id;
private String name;
@OneToMany(mappedBy = "team")
private List<Member> memberList = new ArrayList<>();
}
Team Entity에다가 List members를 추가해줬다.
mappedBy
여기서 주의해줘야할 것이 있는데, mappedBy는 반대편 사이드, Member에서 Team이 정의된 변수 명을 따라가야한다!!
이전 글에서 Member는
@Entity
@Setter
@Getter
public class Member {
@Id @GeneratedValue
@Column(name = "MEMBER_ID")
private Long id;
@Column(name = "USERNAME")
private String username;
@ManyToOne
@JoinColumn(name = "TEAM_ID")
private Team team;
}
이렇게 객체 지향스럽게 바꿨는데, 변수명이 team이므로 Team 엔티티에서 mappedBy를 team으로 한 것이다.
이제 JpaMain에서 이를 사용해보자.
Member findMember = em.find(Member.class, member.getId());
List<Member> members = findMember.getTeam().getMembers();
for (Member m : members) {
System.out.println("m.getUsername() = " + m.getUsername());
}
이렇게 사용할 수 있다.
연관관계의 주인과 mappedBy
객체와 테이블 간에 연관관계를 맺는 차이를 이해해야한다.
객체의 연관관계
객체 연관관계는 2개이다.
Team -> Member 연관관계 1개 (단방향)
Member -> Team 연관관계 1개 (단방향)
객체의 양방향 관계는 사실 양방향 관계가 아니라 서로 다른 단방향 관계 2개이다.
객체를 양방향으로 참조하려면 단방향 연관관계를 2개 만들어야한다.
테이블의 연관관계
하지만 테이블에서는 연관관계는 1개이다.
테이블은 외래 키 하나로 두 테이블의 연관관계를 관리한다.
TEAM_ID FK를 통해서 양쪽에서 조회가 다 가능하다.
MEMBER.TEAM_ID 외래 키 하나로 양방향 연관관계를 가진다. (양쪽으로 조인할 수 있다)
테이블에 있어 수정이 필요하면 TEAM을 수정하면 됐다.
하지만 객체에 있어서는 Meber의 Team가 업데이트 됐을 때 table의 TEAM_ID를 수정해야할지, Team의 List members가 업데이트 됐을 때 table의 TEAM_ID를 수정해야할지 혼란스럽다.
둘 중 하나로 외래 키를 관리해야한다.
연관관계의 주인
객체의 두 관계중 하나를 연관관계의 주인으로 지정해야한다.
연관관계의 주인만이 외래키를 관리한다. (등록, 수정)
주인이 아닌쪽은 읽기만 가능해야한다!!!
주인은 mappedBy 속성을 사용하지 않는다.
주인이 아니면 mappedBy 속성으로 주인을 지정한다.
누구를 주인으로 해야할까?
🔥 mappedBy로는 조회만 가능하며 수정이 불가능하다. 🔥
mappedBy = "team" 이라고 했으므로 team에 의해 관리가 되는 것이기에,
Member.team 이 연관관계의 주인이 되는 것이다.
주인의 반대편인 Team.members는 가짜매핑이다.
외래키가 있는 곳을 주인으로 정해야한다. !!
"다, n" 쪽이 연관관계의 주인이 되어야한다.
양방향 매핑시 가장 많이 하는 실수
team.getMembers().add(member) 이런식으로 가짜주인에게 추가를 하면 DB는 이를 무시해버린다.
우린 아까 Member의 Team이 연관관계의 주인으로 설정했으므로, Member.team 자체를 추가해줘야한다.
member.setTeam(team)처럼 말이다.
하지만 객체지향 관점에서 볼 때 둘 다 추가를 해줘야한다.
member.setTeam(team)
team.getMembers().add(member)
이렇게 둘다 해주는게 맞다.
연관관계 편의 메서드
public void setTeam(Team team) {
this.team = team;
team.getMembers().add(this);
}
순수 객체 상태를 고려해서 항상 양쪽에 값을 설정해야한다.
하지만 이를 사람이기에 까먹을 수 있기 때문에 연관관계 편의 메서드를 만들으면 이러한 과정을 직접할 필요가 없다.
또한, 양방향 매핑시에 무한루프를 조심해야한다 .
toString(), lombok, JSON 생성 라이브러리, Entity를 절대 Controller에서 반환하지 말것!
양방향 매핑 정리
- 단방향 매핑만으로도 이미 연관관계 매핑은 완료된 것이나 마찬가지다.
- 양방향 매핑은 반대 방향으로 조회(객체 그래프 탐색) 기능이 추가된 것 뿐이다.
- JPQL에서 역방향으로 탐색할 일이 많다.
- 단방향 매핑을 잘 하고 양방향은 필요할 때 추가하면 된다.
'JPA > ORM 표준 JPA' 카테고리의 다른 글
[ORM 표준 JPA] 9.고급매핑 - 상속관계 매핑, @MappedSuperclass (0) | 2023.09.30 |
---|---|
[ORM 표준 JPA] 8.다양한 연관관계 매핑 (0) | 2023.09.30 |
[ORM 표준 JPA] 6. 연관관계 매핑 1 (0) | 2023.09.23 |
[ORM 표준 JPA] 5. 엔티티 매핑2 - 기본키 전략 (0) | 2023.09.20 |
[ORM 표준 JPA] 4. 엔티티 매핑1 - 객체와 테이블 매핑 (0) | 2023.09.18 |