내가 깃허브에 올린 데이터베이스 스키마를 이용해서 설명하겠다.
우선 매핑은 서로 연관되는 항목의 수에 따라 일대일 매핑, 일대다 매핑, 다대일 매핑, 다대다 매핑으로 나눌 수 있으며,
방향성에 따라 단방향매핑과 양방향매핑으로 나눌 수 있다.
쉽게 이해할 수 있게 하나씩 설명을 하겠다.
1. 일대다 매핑
스키마에서 Member와 Order은 일대다 매핑인 것을 볼 수 있다.(한 사람이 여러개의 주문을 할 수 있기 때문에)
2. 일대일 매핑
Order와 Delivery는 일대일 매핑이다.(주문 한개당 한 개의 배송을 하기 때문)
3. 다대일 매핑
Order와 Member의 관계(여러 주문을 한 사람이 할 수 있다)
4. 다대다 매핑
Order와 Item의 관계(여러개의 주문에 다양한 물건들을 구매할 수 있다)
*다대다 매핑은 권장하지 않으므로 중간 엔티티를 만들어서 일대다, 다대일 관계로 사용했다.
이렇게 말만 들으면 쉬운 것 같지만 실제 프로젝트를 할 때 가장 고민되는 부분이 데이터베이스 정규화 과정이다.
단방향 매핑과 양방향 매핑이란?
데이터베이스 테이블은 외래 키 하나로 양쪽 테이블 조인이 가능합니다.
즉 굳이 단방향이나 양방향으로 나눌 필요가 없다는 것입니다.
그럼에도 불구하고 나누는 이유는 객체는 참조용 필드가 있는 객체만 다른 객체를 참조하는 것이 가능하기 때문입니다.
두 객체 모두가 참조용 필드를 가지고 참조를 하면 양방향 필드, 한쪽만 참조용 필드를 가지고 있으면 단방향 필드입니다.
코드와 함께 설명하겠습니다.
양방향 매핑
1. ManyToOne, OneToMany
1
2
3
4
|
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "member_id")
private Member member;
|
cs |
Order 클래스에서 Member 객체를 ManyToOne(다대일 매핑)으로 관리한다.
이때 JoinColumn 어노테이션에 집중해야 한다. JoinColumn 어노테이션을 가지고 있는 클래스를 보통 주인이라고 하며,
상대 테이블을 Join해주는 역할을 한다. Order클래스는 연관관계의 주인이므로 외래키(대상 테이블의 PK)를 가진다.
1
2
3
4
5
6
|
@Id
@GeneratedValue
@Column(name = "member_id")
private Long id;
@OneToMany(mappedBy = "member") // order 테이블에 있는 Member 필드에 의해 나는 mapping 된거야
private List<Order> orders = new ArrayList<>();
|
cs |
mappedBy는 연관관계의 주인이 아닌 클래스에서 사용하는데 위 코드는 Member클래스이다.
Member클래스와 Order클래스의 연관관계에서는 Order 클래스가 주인이므로 Member 클래스에는 mappedBy가 있어야 한다. mappedBy(member)를 해서 Order 클래스에 있는 Member 필드에 의해 매핑된 것을 알 수가 있다.
양방향 매핑에서는 연관관계의 주인 쪽에 JoinColumn이 있고 주인이 아닌 쪽에 mappedBy가 있다.(일대다 매핑이면 보통 "다"쪽이 주인이다)
2. OneToOne
1
2
3
|
@OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL) // FetchType 알아보기
@JoinColumn(name = "delivery_id")
private Delivery delivery;
|
cs |
OneToOne은 1대1관계이다. Order과 Delivery는 앞서 말했듯이 일대일 관계이다.
일대다 다대일 관계에서는 "다"쪽이 연관관계의 주인인데 일대일 관계는 설계자가 설계하기 나름이다.
나는 Order과 Delivery에서 Order를 연관관계의 주인으로 설정했다.
3번째 줄의 JoinColumn(name = "delivery_id")를 통해 Order에서 외래키(Delivery의 PK인 delivery_id)를 가질 수 있게 해 준다.
1
2
3
4
5
6
7
|
@Id
@GeneratedValue
@Column(name = "delivery_id")
private Long id;
@JsonIgnore
@OneToOne(mappedBy = "delivery",fetch = FetchType.LAZY)
private Order order;
|
cs |
Delivery는 연관관계의 주인이 아님으로 mappedBy를 사용해서 Order클래스에 있는 delivery필드에 의해 매핑된 것을 말해준다.
단방향 매핑
단방향 매핑은 JoinColumn으로 연관관계의 주인만 참조용 필드를 가지고 있는 경우입니다.
ex) 연관관계의 주인만 JoinColumn을 하고 대상 테이블은 mappedBy를 하지 않는다.
@ManyToMany
실무에서 절대 사용하면 안 되는 것이다.
만약 사용하게 된다면 중간 엔티티를 만들어서 일대다, 다대일 관계로 사용해야 한다.
1
2
3
4
5
|
@ManyToMany
@JoinTable(name = "category_item",
joinColumns = @JoinColumn(name = "category_id"),
inverseJoinColumns = @JoinColumn(name = "item_id"))
private List<Item> items = new ArrayList<>();
|
cs |
@JoinTable을 통해 두 테이블을 연결하는 별도의 테이블을 생성한다.
그리고 JoinColums, inverseJoinColumns를 통해 일다, 다대일 관계로 관리합니다.
joinColumns : 현재 Entity를 참조하는 FK
inverseColumns : 반대방향 Entity를 참조하는 FK
ToOne에 사용되는 fetchType은 다음 강의에서 설명하겠다 :)