- 친구목록 가져오기에서 하나의 유저와 연관된 다른 유저들의 정보를 참조하는 방법으로
@ManyToMany
를 생각했다.
JPA 의 @ManyToMany
- 실무에선 사용하지 않은 것을 추천한다고 한다.
- RDB에서는 정규화된 테이블 2개로 다대다 관계를 표현할 수 없다.
- 그대신, 연결 테이블(조인 테이블)을 중간에 추가하여 일대다, 다대일 관계를 이용해야한다.
객체에서의 다대다
- ORM은 테이블에서 불가능한 부분을 객체에서 지원해줘야 한다.
객체의 다대다 관계
(위의 그림)와 테이블에서의 일대다, 다대일 관계
(아래 그림)의 차이를 ORM이 연결해준다.
- JPA에서
@ManyToMany
어노테이션을 사용하여 이를 연결하고, @JoinTable
로 연결 테이블을 지정할 수 있다.
다대다 단방향
@Entity
public class Member {
...
@ManyToMany
@JoinTable(name = "member_product")
private List<Product> products = new ArrayList<>();
...
}
다대다 양방향
@Entity
public class Product {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@ManyToMany(mappedBy = "products")
private List<Member> members = new ArrayList<>();
}
다대다 매핑의 한계
- 편리해 보이지만 실무에서 사용하면 안된다.
- 이유
- 매핑 정보만 넣는 것이 가능하고, 추가 정보를 넣는 것 자체가 불가능하다
- 중간 테이블(Member_Product table)이 숨겨져 있으므로 예상하지 못한 쿼리가 나간다.
다대다 매핑 한계의 극복
- 연결 테이블용 엔티티를 추가한다 ->
연결 테이블을 엔티티로 승격
시킨다.
@ManyToMany
를 -> 각각 일대다, 다대일 관계로 맺어준다.
- 다시 말하면, JPA가
@ManyToMany
어노테이션으로 만들어주는 숨겨진 매핑테이블의 존재를 바깥으로 꺼낸다.
코드
Member @OneToMany
- Member 엔티티에서
@OneToMany
관계로 변경한다.
@Entity
public class Member {
...
@OneToMany(mappedBy = "member")
private List<MemberProduct> memberProducts = new ArrayList<>();
...
}
Product @OneToMany
- Product 엔티티에서
@OneToMany
관계로 변경한다.
@Entity
public class Product {
...
@OneToMany(mappedBy = "product")
private List<MemberProduct> members = new ArrayList<>();
...
}
MemberProduct @ManyToOne
2번
- 연결 테이블을
MemberProduct
엔티티로 승격시킨다.
- 이 곳에서
@ManyToOne
매핑을 두개 한다. (연관 관계의 주인)
@Entity
@Getter
@Setter
public class MemberProduct {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToOne
@JoinColumn(name = "member_id")
private Member member;
@ManyToOne
@JoinColumn(name = "product_id")
private Product product;
}
결론
- User 객체는 또다른 User 객체를 참조해야한다.
@ManyToMany
가 아닌 @OneToMany
가 맞는 설계방향이다.
- 셀프 참조