# 실전 예제

## 요구 사항

![](/files/-MVxWKSm1wtNJN8Mamif)

* 회원은 상품을 주문할 수 있다.
* 주문 시 여러 종류의 상품을 선택할 수 있다.

## 도메인 설계

![](/files/-MVxWKSoFRIjQ2wNhmfr)

* 회원과 주문
  * 일대다 관계
  * 회원은 여러 번 주문할 수 있다.
* 주문과 상품
  * 주문할 때 여러 상품을 선택할 수 있다.
  * 같은 상품도 여러번 주문될 수 있다.
  * 다대다 관계를 일대다, 다대일 관계로 풀어낸다.

## 테이블 설계

![](/files/-MVxWKSp9V7dgH_wHPp-)

## Entity 설계와 매핑

![](/files/-MVxWKSqFqrYv6H0owwi)

보통 처음에는 테이블과 Entity를 똑같이 설계한다. 이렇게 됐을 때의 문제점이 무엇일지 살펴보자.

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

```java
@Entity
// 인덱스나 length 같은 칼럼 제약 조건은 매핑 애너테이션을 이용해
// 코드에 표시해주는게 좋다. DB를 까보지 않고도 알 수 있기 때문이다.
// 예를 들어 JPQL 짤 때 인덱스가 필요하면 코드만 보고 사용할 수 있다.
public class Member {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "member_id")
    private Long id;
    private String name;
    private String city;
    private String street;
    private String zipcode;
}
```

{% endtab %}

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

```java
@Entity
// DB에 order가 예약어로 걸려있어서 테이블명을 따로 지정해준다.
@Table(name = "ORDERS")
public class Order {
    @Id
    @GeneratedValue
    @Column(name = "order_id")
    private Long id;

    // 누가 주문했는지 알기 위한 용도
    @Column(name = "member_id")
    private Long memberId;

    private LocalDateTime orderDate;

    // enum은 꼭 STRING으로 해준다.
    @Enumerated(EnumType.STRING)
    private OrderStatus status;
}
```

{% endtab %}

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

```java
@Entity
public class OrderItem {
    @Id
    @GeneratedValue
    @Column(name = "order_item_id")
    private Long id;

    @Column(name = "order_id")
    private Long orderId;

    @Column(name = "item_id")
    private Long itemId;

    @Enumerated(EnumType.STRING)
    private OrderStatus status;
}
```

{% endtab %}

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

```java
@Entity
public class Item {
    @Id
    @GeneratedValue
    @Column(name = "item_id")
    private Long id;

    private String name;

    private int price;

    private int stockQuantity;
}
```

{% endtab %}

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

```java
public class JpaMain {
    public static void main(String[] args) {
        EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("hello");
        EntityManager em = entityManagerFactory.createEntityManager();
        EntityTransaction tx = em.getTransaction();
        tx.begin();

        try {
            Order order = em.find(Order.class, 1L);
            Long memberId = order.getMemberId();

            Member member = em.find(Member.class, memberId);

            tx.commit();
        } catch (Exception e) {
            tx.rollback();
        } finally {
            em.close();
        }

        entityManagerFactory.close();
    }
}
```

{% endtab %}
{% endtabs %}

* `JpaMain`에서는 주문한 회원을 찾기 위해
  * `order` 조회 후
  * 거기서 `memberId`를 찾아
  * 다시 `member`를 조회해야 하는 번거로움이 있다.

객체는 참조로 쭉쭉 찾아가야 하는데 식별자를 통해서 해야 하면 끊겨버린다. 뭔가 객체지향스럽지 않다. 이런 설계는 관계형 DB에 객체를 맞춘 것이다.

## 데이터 중심 설계의 문제점

* 테이블의 외래 키를 객체에 그대로 가져온다.
* 객체 그래프 탐색이 불가능하다.
* 참조가 없으므로 앞에서 만든 UML도 사실 잘못된 것이다.
  * Entity 매핑 다이어그램을 보면 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/04-entity-mapping/example.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.
