티스토리 뷰
다대다[N:M]
실무에선 사용하지 않는 것을 추천한다. 사용하면 안되는 이유를 학습하자.
관계형 데이터베이스는 정규화된 테이블 2개로 다대다 관계를 표현할 수 없다.
연결 테이블(조인 테이블)을 추가해서 일대다, 다대일 관계로 풀어내야한다.
객체는 컬렉션을 사용해서 객체 2개로 다대다 관계가 가능하다.
ORM 입장에서는 테이블은 안되고, 객체는 안되는 것을 지원해줘야 한다.
따라서, 아래의 그림에서와 같이 객체의 다대다 관계(멤버와 프로덕트가 서로 리스트를 가짐)와
테이블에서 다대다 관계를 일대다 다대일 관계로 풀어낸 것 두개의 차이를 연결해준다.
JPA @ManyToMany 어노테이션을 사용하고
@JoinTable로 연결 테이블을 지정해줄 수 있다.
다대다 단방향
public class Member {
...
name = "member_product") (
private List<Product> products = new ArrayList<>();
...
}
실행된 SQL
조인 테이블이 하나 생성되고, 외래 키 제약조건도 두가지가 설정 된다.
...
Hibernate:
create table member_product (
Member_id bigint not null,
products_id bigint not null
)
...
Hibernate:
alter table member_product
add constraint FK17rh8i9jrsy7yqy2j6e9yijuw
foreign key (products_id)
references Product
Hibernate:
alter table member_product
add constraint FK3cjijenmv5sgu1w04p4ofy6ik
foreign key (Member_id)
references Member
다대다 양방향
똑같이 @ManyToMany 설정을 해주고, mappedBy 설정을 해줘야 한다.
public class Product {
strategy = GenerationType.IDENTITY) (
private Long id;
private String name;
mappedBy = "products") (
private List<Member> members = new ArrayList<>();
}
다대다 매핑의 한계
편리해 보이지만 실무에서 사용하면 안된다.
개발하다 보면, 연결 테이블이 단순히 연결만 하고 끝나지 않는다. 조인 테이블 자체에 주문시간, 수량 같은 추가 데이터가 많이 들어갈 수 있다.
하지만, 매핑 정보만 넣는 것이 가능하고, 추가 정보를 넣는 것 자체가 불가능하다.
그리고 중간 테이블이 숨겨져 있기 때문에 예상하지 못하는 쿼리들이 나간다.
이런 문제점들 때문에 실무에서는 안쓰는게 맞다고 본다.
다대다 한계 극복
어떻게 한계를 극복할까.
연결 테이블용 엔티티를 추가한다. 사실상 연결 테이블을 엔티티로 승격시킨다.
그리고 @ManyToMany를 각각 일대다, 다대일로 관계를 맺어준다.
사실 개인적으로 이부분에 대해서는 @ManyToOne, 다대일 관계 두개로 풀어낸다는 표현이 맞는 것 같다.
앞서 일대다 관계를 학습할 때, 결론적으로 다대일 양방향 매핑을 사용하자는 결론을 내기도 했고,
실제로 아래의 코드상으로 봐도 FK 2개를 중간테이블에서 관리하고, @ManyToOne 양방향 매핑 2개로 이어져있다.
테이블의 배치 순서상(Member -> MemberProduct(Order) -> Product) 표현을 @OneToMany, @ManyToOne으로 표현했던 것이라면 이해가 간다.
JPA가 만들어주는 숨겨진 매핑테이블의 존재를 바깥으로 꺼내는 것이다.
위에 다대다 매핑의 한계 첨부 그림에서는 MemberProduct의 MEMBER_ID, PRODUCT_ID를 묶어서 PK로 썻지만, 실제로는 아래 처럼 독립적으로 generated되는 id를 사용하는 것을 권장한다. ID가 두개의 테이블에 종속되지 않고 더 유연하게 개발 할 수 있다. 시스템을 운영하면서 점점 커지는데 만약 비즈니스적인 제약 조건이 커지면 PK를 운영중에 업데이트 하는 상황이 발생할 수도 있다.
코드로 이해하기
Member
멤버 엔티티에서 @OneToMany 관계로 변경한다.
public class Member {
...
mappedBy = "member") (
private List<MemberProduct> memberProducts = new ArrayList<>();
...
}
Product
마찬가지로 @OneToMany 관계로 변경한다.
public class Product {
...
mappedBy = "product") (
private List<MemberProduct> members = new ArrayList<>();
...
}
MemberProduct
연결 테이블을 엔티티로 승격시킨다. 그리고 @ManyToOne 매핑을 두개 한다.(연관관계의 주인)
여기서 추가 데이터가 들어간다면 아예 의미있는 엔티티 이름(Order)으로 변경 될 것이다.
public class MemberProduct {
strategy = GenerationType.IDENTITY) (
private Long id;
name = "member_id") (
private Member member;
name = "product_id") (
private Product product;
}
Reference
'ICT Eng > JPA' 카테고리의 다른 글
[JPA] @MappedSuperclass (2) | 2019.08.26 |
---|---|
[JPA] 상속관계 매핑 전략(@Inheritance, @DiscriminatorColumn) (0) | 2019.08.26 |
[JPA] @OneToOne, 일대일[1:1] 관계 (0) | 2019.08.23 |
[JPA] @OneToMany, 일대다[1:N] 관계 (1) | 2019.08.23 |
[JPA] @ManyToOne, 다대일[N:1] 관계 (1) | 2019.08.23 |
- Total
- Today
- Yesterday
- AWS
- springboot
- vuex
- 젠킨스
- 레드블랙트리
- IT융합인력양성사업단
- 한밭대학교
- JPA
- 알고리즘
- vuejs
- Spring Boot
- Algorithm
- Spring
- Recursion
- Java
- Wisoft
- Vue.js
- Raspberry Pi
- 스프링부트
- 인프런
- 라즈베리파이
- 한밭이글스
- 순환
- RBT
- 자바
- 정렬
- ORM
- 무선통신소프트웨어연구실
- github
- 시간복잡도
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |