# 실전 예제

![](/files/-MWAE8P_4-caCdLEGmgg)

테이블 구조는 이전과 같다. 아이템엔 FK가 없다. 주문에서 어떤 아이템이 있는지는 조회해도 아이템 당 어떤 주문인지는 잘 파악하지 않기 때문이다.

![](/files/-MWAE8PZu22owi4OEpWz)

객체 구조는 참조를 사용하도록 변경되었다.

![](/files/-MWAE8P_4-caCdLEGmgg)

단방향 연관 관계 매핑과 연관 관계 주인을 설정하는 것이 중요하다. 테이블 구조를 다시 보고 외래키만 잘 넣어주면 된다.

## 단방향 연관관계 설정

{% tabs %}
{% tab title="before" %}

```java
@Entity
@Table(name = "ORDERS")
public class Order {

    @Id
    @GeneratedValue
    @Column(name = "order_id")
    private Long id;

    // FK만 들어가있다.
    @Column(name = "member_id")
    private Long memberId;

    private LocalDateTime orderDate;

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

{% endtab %}

{% tab title="after" %}

```java
@Entity
@Table(name = "ORDERS")
public class Order {

    @Id
    @GeneratedValue
    @Column(name = "order_id")
    private Long id;

    // Member 객체 자체를 참조하고 FK를 기준으로 join 한다.
    @ManyToOne
    @JoinColumn(name = "MEMBER_ID")
    private Member member;

    private LocalDateTime orderDate;

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

{% endtab %}
{% endtabs %}

* 테이블 중심의 설계에서 객체를 참조하는 형태로 변경했다.

{% tabs %}
{% tab title="before" %}

```java
@Entity
public class OrderItem {

    @Id
    @GeneratedValue
    @Column(name = "order_item_id")
    private Long id;

    // order와 item 모두 FK만 존재한다.
    @Column(name = "order_id")
    private Long orderId;

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

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

{% endtab %}

{% tab title="after" %}

```java
@Entity
public class OrderItem {

    @Id
    @GeneratedValue
    @Column(name = "order_item_id")
    private Long id;

    // 외래키를 관리하니까 OrderItem.order와 OrderItem.item이 연관 관계의 주인이 된다.
    @ManyToOne
    @JoinColumn(name = "ORDER_ID")
    private Order order;

    @ManyToOne
    @JoinColumn(name = "ITEM_ID")
    private Item item;

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

{% endtab %}
{% endtabs %}

일단 단방향으로 설계를 완료한다. 그 후에 양방향이 필요한 곳이 있는지 확인해보자.

### 주문 조회 설계

![](/files/-MWAE8P_4-caCdLEGmgg)

* 특정 회원의 주문을 보고 싶다고 회원이 주문을 가지고 있는 건 좋은 설계가 아니다.
  * member를 찾아서 `getOrders()` 하는 단계를 거치는 건 설계 미스다.
  * 주문이 필요하면 주문에서 시작해야 한다. 회원에서 주문을 찾는 건 관심사를 제대로 못 끊어낸 것이다.
* 어차피 DB 다이어그램을 보면 order에 member\_id가 외래 키로 있어 조회 가능하다.

## 양방향 연관 관계 설정

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

```java
@Entity
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;

    // 연관 관계의 주인은 Order.member가 된다.
    @OneToMany(mappedBy = "member")
    private List<Order> orders = new ArrayList<>();
}
```

{% endtab %}
{% endtabs %}

필요하진 않지만 연습을 위해 적용해본다.

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

```java
@Entity
@Table(name = "ORDERS")
public class Order {

    @Id
    @GeneratedValue
    @Column(name = "order_id")
    private Long id;

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

    // 비즈니스적으로 가치가 있는 값은 양방향으로 설정한다.
    // OrderItem.order가 연관 관계의 주인이다.
    @OneToMany(mappedBy = "order")
    private List<OrderItem> orderItems = new ArrayList<>();

    private LocalDateTime orderDate;

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

{% endtab %}
{% endtabs %}

주문은 주문과 연관된 아이템 목록을 가져오는 기능이 필요하므로 양방향을 설정해준다.

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

```java
@Entity
@Table(name = "ORDERS")
public class Order {

    ...

    // Order와 OrderItem이 양방향 연관 관계이므로 연관 관계 편의 메서드를 구현한다.
    public void addOrderItem(OrderItem orderItem) {
        orderItems.add(orderItem);
        orderItem.setOrder(this);
    }
}
```

{% endtab %}

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

```java
public class JpaMain {

    public static void main(String[] args) {
        try {
            Order order = new Order();

            // 연관 관계 편의 메서드
            order.addOrderItem(new OrderItem());

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

```

{% endtab %}
{% endtabs %}

연관 관계 메서드를 통해 양 쪽 모두 값을 세팅한다.

```java
public class JpaMain {

    public static void main(String[] args) {
        try {
            Order order = new Order();
            em.persist(order);

            // 양방향이 아니어도 이렇게 진행할 수 있다.
            OrderItem orderItem = new OrderItem();
            orderItem.setOrder(order);
            em.persist(orderItem);

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

        entityManagerFactory.close();
    }
}
```

물론 단방향 그대로 유지해도 OrderItem을 가져와서 세팅해주면 된다. 양방향 설정이 굳이 필요없다. 즉, Order에 OrderItem 참조를 추가하지 않아도 된다.

최대한 단방향으로 하고 실무에서 JPQL을 사용할 때나 비즈니스 상 양쪽으로 참조가 있어야 순조로울 때가 오면 양방향으로 설정하자.


---

# 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/05-relationship-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.
