# 다대다

* @ManyToMany
  * 실무에서는 사용하지 않는다.
  * 관계형 데이터베이스는 정규화된 테이블 2개라 다대다 관계를 표현할 수 없기 때문이다.
* 연결 테이블을 추가해서 일대다, 다대일 관계로 풀어내야 한다.

![](https://389280719-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LxjHkZu4T9MzJ5fEMNe%2Fsync%2Fcd8cde454907d19571397952d8199f3b4be00fcd.png?generation=1616223195811523\&alt=media)

* DB는 테이블로 다대다를 만들 수 없기 때문에 중간에 member\_product 테이블을 만들어 풀어낸다.

![](https://389280719-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LxjHkZu4T9MzJ5fEMNe%2Fsync%2F8a06691878766f3b51aa8a7b920bde173e84e1c9.png?generation=1616223193829668\&alt=media)

* 객체는 컬렉션을 사용해 객체 2개로 다대다 관계를 표현할 수 있다.
* member가 List를, Product가 List를 가질 수 있다.

## 단방향 매핑

{% tabs %}
{% tab title="Member.java" %}

```java
@Entity
public class Member {

    ...

    @ManyToMany
    // @JoinTable에 연결 테이블의 이름을 적어준다.
    // 그럼 알아서 member_id와 product_id를 pk, fk로 갖는다.
    @JoinTable(name = "MEMBER_PRODUCT")
    private List<Product> products = new ArrayList<>();
}
```

{% endtab %}

{% tab title="Product.java" %}

```java
@Entity
public class Product {
    @Id
    @GeneratedValue
    private Long id;

    private String name;
}
```

{% endtab %}
{% endtabs %}

![](https://389280719-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LxjHkZu4T9MzJ5fEMNe%2Fsync%2F0e73113347c39d705e639380bf8b43fa07331d00.png?generation=1616223192667816\&alt=media)

`MEMBER_PRODUCT`라는 테이블이 자동으로 생성되었다.

## 양방향 매핑

{% tabs %}
{% tab title="Member.java" %}

```java
@Entity
public class Member {

    @Id
    @GeneratedValue
    private Long id;

    @ManyToMany
    @JoinTable(name = "MEMBER_PRODUCT")
    private List<Product> products = new ArrayList<>();
}
```

{% endtab %}

{% tab title="Product.java" %}

```java
@Entity
public class Product {
    @Id
    @GeneratedValue
    private Long id;
    private String name;

    // 일반적인 양방향 매핑과 같이 mappedBy를 해준다.
    @ManyToMany(mappedBy = "products")
    private List<Member> members = new ArrayList<>();
}
```

{% endtab %}
{% endtabs %}

* members.products가 연관 관계의 주인임을 mappedBy로 표현한다.

## 한계

![](https://389280719-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LxjHkZu4T9MzJ5fEMNe%2Fsync%2Ff4412e2777e5358e43add54f630f3ac4e7089a4f.png?generation=1616223194713234\&alt=media)

* 편해 보이지만 실무에서 쓰면 안된다.
  * 실무에서는 연결 테이블이 단순이 연결하는 기능만 하지 않는다.
    * 주문 시간, 수량 같은 추가적인 데이터가 필요해도 위의 그림처럼 테이블에 데이터를 추가할 수 없다.
  * 예상치 못한 쿼리가 나갈 수 있다.
    * 중간 테이블이과 조인해서 나가야 하는데, 중간 테이블이 숨겨져 있기 때문에 내가 모르는 쿼리가 나가서 문제가 생길 수 있다.

## 극복 방법

* 연결용 테이블 Entity를 추가한다.
* 즉, 연결 테이블을 Entity로 승격하고 일대다, 다대일로 풀어낸다.

{% tabs %}
{% tab title="Member.java" %}

```java
@Entity
public class Member {

    @Id
    @GeneratedValue
    private Long id;

    @OneToMany(mappedBy = "member")
    private List<MemberProduct> memberProducts = new ArrayList<>();
}
```

{% endtab %}

{% tab title="MemberProduct.java" %}

```java
// 중간 테이블
@Entity
public class MemberProduct {
    @Id
    @GeneratedValue
    private Long id;

    @ManyToOne
    @JoinColumn(name = "MEMBER_ID")
    private Member member;

    @ManyToOne
    @JoinColumn(name = "PRODUCT_ID")
    private Product product;

    private int orderAmount;
    private LocalDateTime orderDateTime;
}
```

{% endtab %}

{% tab title="Product.java" %}

```java
@Entity
public class Product {
    @Id
    @GeneratedValue
    private Long id;
    private String name;

    @OneToMany(mappedBy = "product")
    private List<MemberProduct> memberProducts = new ArrayList<>();
}
```

{% endtab %}
{% endtabs %}

![](https://389280719-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LxjHkZu4T9MzJ5fEMNe%2Fsync%2Ff315432a6d174bd60f559b63ba67ebb2f31d4997.png?generation=1616223197937926\&alt=media)

* 중간 테이블을 연관 관계의 주인으로 두고 푼다.
* 중간 테이블의 이름은 Orders 등 의미있는 이름으로 변경해도 된다.

![](https://389280719-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LxjHkZu4T9MzJ5fEMNe%2Fsync%2Ff4412e2777e5358e43add54f630f3ac4e7089a4f.png?generation=1616223194713234\&alt=media)

* FK인 member\_id와 product\_id 둘을 묶어 PK가 되게 잡을 수도 있다.
* 하지만 웬만하면 PK는 자동 생성 되는 의미 없는 값을 써야 유연성이 생긴다.
  * id가 어디에 종속되면 걸림돌이 되는 일이 많다.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://dodeon.gitbook.io/study/kimyounghan-orm-jpa/06-various-relationship-mapping/n-n.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
