📌 다대다 [N:M]
✔ 목차
- 연관관계 매핑시 고려사항 3가지
- 다대일 [N:1]
- 일대다 [1:N]
- 일대일 [1:1]
- 다대다 [N:M]
- 실전 예제 - 3. 다양한 연관관계 매핑 🚀
📌 배송, 카테고리 추가 - 엔티티
들어가기에 앞서 이번 장에서는 개념적인 부분보다는 간단한 실습 위주로
진행을 할 예정이니 내용이 미약해도 양해 부탁드리겠습니다 🤣
- 주문과 배송은 1:1 (@OneToOne)
- 상품과 카테고리는 N:M (@ManyToMany)
📌 배송, 카테고리 추가 - ERD
- 1:1 관계에서 Orders에 DELIVERY_ID (FK)가 존재한다.
- N:M 관계(Category : CATEGORY_ITEM : Item)의 중간 테이블인 CATEGORY_ITEM에는 CATEGORY_ID (FK)와 ITEM_ID (FK)가 존재한다.
📌 배송, 카테고리 추가 - 엔티티 상세
✅ Order와 Delivery 1:1 관계 설정
@Entity
public class Delivery {
@Id
@GeneratedValue
private Long id;
@OneToOne(mappedBy = "delivery") // 주테이블(Order)을 연관관계의 주인으로 결정
private Order order;
//..중략
}
- Order 테이블을 주 테이블로 바라보고 연관관계의 주인으로 설정.
- Delivery에서는 @OneToOne(mappedBy = “delivery”)를 선언해야 한다.
- 일대일(1:1) 관계에서 외래키를 관리하는 쪽이 어딘지 지정을 해야 하기에.
@Entity
@Table(name = "ORDERS")
public class Orders {
@Id
@GeneratedValue
private Long id;
//.. 중략
@OneToOne
@JoinColumn(name = "DELIVERY_ID")
private Delivery delivery;
//.. 중략
}
중략된 코드는 깃헙을 참고해주세요.
- Orders는 일대일(1:1) 관계에서 주테이블로써 연관관계의 주인으로 결정된 상황.
- @JoinColumn(name = “DELIVERY_ID”)를 선언하여 외래키를 지정한다.
✅ Category와 Item N:M 관계 설정
@Entity
public class Category {
@Id
@GeneratedValue
private Long id;
private String name;
@ManyToOne
@JoinColumn(name = "PARENT_ID")
private Category parent;
@OneToMany(mappedBy = "parent")
private List<Category> child = new ArrayList<>();
/*
@OneToMany(mappedBy = "team")
private List<Member> members = new ArrayList<>(); 예시
*/
@ManyToMany
@JoinTable(name = "CATEGORY_ITEM", // 중간 테이블을 지정
joinColumns = @JoinColumn(name = "CATEGORY_ID"), // CATEGORY 테이블 입장에서 조인해야하는 컬럼명
inverseJoinColumns = @JoinColumn(name = "ITEM_ID") // 반대쪽 조인해야하는 컬럼명
)
private List<Item> items = new ArrayList<>();
}
- JPA는 셀프 관계설정을 지원한다.
- 여기서 주목해야하는 부분은 items 객체.
- 위 부분이 이해가 되지 않으면 해당 사진을 다시 한번 살펴보자.
@Entity
public class Item {
@Id
@GeneratedValue
private Long id;
@ManyToMany(mappedBy = "items")
private List<Category> categories = new ArrayList<>();
}
- 마지막으로 Item 엔티티는 categories 연관관계의 주인으로 설정한다.
⚡ N:M 관계는 1:N, N:1 관계로 적용
- 테이블의 N:M(양방향)관계는 중간 테이블을 이용하여 1:N, N:1 관계로 만든다.
- 하지만 실전에서의 중간 테이블이 단순하지 않다.
- @ManyToMany의 제약: 필드 추가 x, 엔티티 테이블 불일치.
- 실전에서는 @ManyToMany 사용 x
✅ @JoinColumn
속성 |
설명 |
기본값 |
name |
매핑할 외래키 이름 |
|
referencedColumnName |
외래키가 참조하는 대상 테이블의 컬럼명 |
|
foreignKey(DDL) |
외래키 제약조건을 직접 지정할 수 있다. 이 속성은 테이블 생성시에만 사용 |
|
unique, nullable insertable, updatable, columnDefinition, table |
@Column 속성과 같다. |
|
@Entity
public class Team {
@Id
@GeneratedValue
private Long id;
@OneToMany(mappedBy = "team") // Member Entity의 team 객체를 바라본다
private List<Member> members = new ArrayList<Member>();
//..중략
}
@Entity
public class Member {
@Id
@GeneratedValue
private Long id;
@ManyToOne
@JoinColumn(name = "TEAM_ID") // 외래키로 매핑될 컬럼을 지정 (TEAM : PK)
private Team team;
}
- 조금 헷갈리는 부분이 있을 수 있으니 다시 한번 살펴보자.
✅ @ManyToOne 주요 속성
속성 |
설명 |
기본값 |
optional |
false로 설정 시 연관된 엔티티가 항상 있어야한다 |
TRUE |
fetch |
글로벌 패치 전략 설정 |
@ManyToOne=FectchType.EAGER, @OneToMany=FetchType.LAZY |
cascade |
영속성 전이 기능을 사용 |
|
targetEntity |
연관된 엔티티의 타입 정보를 설정한다. 이 기능은 거의 사용하지 않는다. 컬렉션을 사용해도 제네릭으로 타입 정보를 알 수 있기 때문에. |
|
✅ @OneToMany 주요 속성
속성 |
설명 |
기본값 |
mappedBy |
연관관계의 주인이 될 필드를 선택한다 |
TRUE |
fetch |
글로벌 패치 전략 설정 |
@ManyToOne=FectchType.EAGER, @OneToMany=FetchType.LAZY |
cascade |
영속성 전이 기능을 사용 |
|
targetEntity |
연관된 엔티티의 타입 정보를 설정한다. 이 기능은 거의 사용하지 않는다. 컬렉션을 사용해도 제네릭으로 타입 정보를 알 수 있기 때문에. |
|
참고 자료
댓글남기기