티스토리 뷰

JPA

[JPA] 다대다 연관관계

시리어스강 2023. 6. 18. 22:39

다대다 관계의 경우 실무에서는 지양해야 하는 방식이라고 하지만, 그냥 사용하지 않는 것이 아니라 그 이유는 알고 있어야 하므로 다대다 관계에 대해서 정리 해보려고 한다. 샘플 코드는 가장 아래에 있는 링크를 참조하면 된다.

 

그래도 지양해야 하는 이유는 이해하자

 

 

1. 객체와 테이블의 차이

객체

객체는 컬렉션을 사용해서 객체 두 개로 다대다 관계 가능하다.

 

 

관계형 DB

관계형 DB는 정규화된 테이블 두 개로 다대다 관계를 표현할 수 없다. 그러므로 연결 테이블을 추가해서 일대다 - 다대일 관계로 풀어내야 한다.

주문과 상품 테이블만으로는 다대다 관계를 표현할 수 없다
연결 테이블(Order_Item)을 사용해 다대다 관계를 표현할 수 있다

 

 

 

2. 단방향

@ManyToMany 어노테이션을 사용하여 다대다 관계를 매핑한다.

 

엔티티 예시

@JoinTable연결 테이블 지정

@Entity
@Table(name = "ORDERS")
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Order {
    @Id @GeneratedValue
    private Long id;
    @ManyToMany
    @JoinTable(name = "ORDER_ITEM")
    private List<Item> items = new ArrayList<>();
}
@Entity
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Item {
    @Id @GeneratedValue
    private Long id;
    private String name;
}

 

연결 테이블 생성 확인

 

3. 양방향

엔티티 예시

@Entity
@Table(name = "ORDERS")
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Order {
    @Id @GeneratedValue
    private Long id;
    @ManyToMany
    @JoinTable(name = "ORDER_ITEM")
    private List<Item> items = new ArrayList<>();
}

 

양방향 관계를 위한 mappedBy 속성 추가

@Entity
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Item {
    @Id @GeneratedValue
    private Long id;
    private String name;
    @ManyToMany(mappedBy = "items")
    private List<Order> orders = new ArrayList<>();
}

 

 

4. 한계와 극복

한계

우선, 연결 테이블은 실무에서 단순히 연결만 하고 끝나지 않을 가능성이 높다. 예를 들어, 주문 시간이나 수량과 같은 데이터가 추가될 수 있다. 이렇게 컬럼을 추가하면 더는 @ManyToMany를 사용 할 수 없다. 왜냐하면 새로 추가될 컬럼들은 엔티티에 매핑할 수 없기 때문이다.

 

극복 방법

이러한 한계를 극복하기 위한 방법은 연결 테이블용 엔티티를 추가하는 것이다. 즉, 연결 테이블을 엔티티로 취급한다. 그리고, 다대다 관계를 일대다 - 다대일 관계으로 변경한다.

 

엔티티 예시

@Entity
@Table(name = "ORDERS")
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Order {
    @Id @GeneratedValue
    private Long id;
    @OneToMany(mappedBy = "order")
    private List<OrderItem> orderItems = new ArrayList<>();
}

연결 테이블용 엔티티

@Entity
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class OrderItem {
    @Id @GeneratedValue
    private Long id;
    @ManyToOne
    @JoinColumn(name = "order_id")
    private Order order;
    @ManyToOne
    @JoinColumn(name = "item_id")
    private Item item;
}
@Entity
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Item {
    @Id @GeneratedValue
    private Long id;
    private String name;
    @OneToMany(mappedBy = "item")
    private List<OrderItem> orderItems = new ArrayList<>();
}

 

ERD 예시

연결 테이블과 달리, 별도의 id가 존재한다.

 

 

테이블 생성 확인

쿼리를 통해서도 id를 확인할 수 있다.

 

 


샘플 코드 🤓

 

참고 자료 🙇‍♂️

 

'JPA' 카테고리의 다른 글

[JPA] 값 타입(value object) 컬렉션  (0) 2023.06.06
댓글