@ManyToOne
)@OneToMay
)@OneToOne
)@ManyToMany
)다대다 관계는 실무에서 거의 사용하지 않는데, 그 이유는?
@ManyToMany
를 사용하면 연결 테이블을 자동으로 처리해주므로 도메인 모델이 단순해지고, 여러가지고 편리하다. 하지만 이 매핑을 실무에서 사용하기에는 한계가 있다.
회원과 상품 사이에 다대다 관계를 맺으면, 회원아이디와 상품 아이디만 담기게 된다. 하지만 보통은 이 외에도 주문한 날짜, 수량과 같은 컬럼이 더 필요하다. 이런 컬럼을 추가하면@ManyToMany
를 사용할 수 없다.
결국, 중간 테이블을 만들고, 거기에 칼럼을 추가해서, 일대다 / 다대일 관계로 풀어야 한다.
데이터베이스 테이블은 외래 키 하나로 양 쪽 테이블 조인이 가능하기때문에, 데이터베이스는 단방향이니 양방향이니 나눌 필요가 없다.
그러나 객체는 참조용 필드가 있는 객체만 다른 객체를 참조하는 것이 가능하다.
그렇기 때문에 두 객체 사이에 하나의 객체만 참조용 필드를 갖고 참조하면 단방향 관계,
두 객체 모두가 각각 참조용 필드를 갖고 참조하면 양방향 관계라고 한다.
객체 입장에서 양방향 매핑을 했을 때, 오히려 복잡해질 수 있다.
제일 좋은 방법은 기본적으로 단방향 매핑으로 하고, 나중에 역방향으로 객체 탐색이 꼭 필요하다고 느낄 때 추가하는 것이다.
두 객체(A, B)가 양방향, 단방향 관계(A -> B, B -> A)를 맺을 때, 연관관계의 주인을 정해야 한다.
🙋♀️ 제어의 권한(외래 키를 비록한 테이블 레코드를 저장, 수정, 삭제 처리를 갖는) 실질적인 관계가 어떤 것인지 JPA에게 알려줘야한다. 연관 관계의 주인은 연관 관계를 갖는 두 객체 사이에서 조회, 저장, 수정, 삭제를 할 수 있지만, 연관 관계의 주인이 아니면 조회만 가능하다.
mappedBy
속성을 사용하고, 연관 관계 주인 필드 이름을 값으로 입력해야 한다.Team team1 = new Team("Team1", "민정팀");
em.persist(team1);
Member member1 = new Member("Member1","민정");
member1.setTeam(team1); // 연관관계 설정 member1 -> team1
team1.getMembers().add(member1) // 연관관계 설정 tema1 -> member1
이렇듯 양방향 연관관계는 결국 양쪽 모두를 신경써야한다. 만약, setTeam과 getMembers().add를 각각 호출하면 실수가 발생할 수 있다. 따라서 양쪽 모두의 관계를 맺어주는 것을 하나의 코드처럼 사용하는 것이 안전하다.
public void setTeam(Team team) {
this.team = team;
team.getMembers().add(this);
}
하지만 위와 같이 setTeam 메서드를 작성하는 경우 버그가 발생할 수 있다. 예를 들어서
member1.setTeam(team1);
member1.setTeam(team2);
위와 같이 연속적으로 setTeam을 호출한 이후 team1에서 멤버를 조회하면 member1가 여전히 조회된다. team2로 변경할 때 team1과의 관계를 제거하지 않았기 때문이다.
public void setTeam(Team team) {
if (this.team != null) { // 기존에 이미 팀이 존재한다면
this.team.getMembers().remove(this); // 관계를 끊는다.
}
this.team = team;
team.getMembers().add(this);
}
따라서 위와 같이 기존 팀과의 관계를 제거하는 코드를 추가해야 정상적으로 동작한다.