2 분 소요

📌 다대다 [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 연관된 엔티티의 타입 정보를 설정한다. 이 기능은 거의 사용하지 않는다. 컬렉션을 사용해도 제네릭으로 타입 정보를 알 수 있기 때문에.  

참고 자료

댓글남기기