# 일대다

* 1이 연관 관계의 주인
  * 추천하지 않는 방법이다.

## 단방향

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

* team을 중심으로 member를 관리한다.
  * member는 team을 모른다.
* DB 입장에서는 무조건 N에 외래키가 들어간다.
  * 1에 해당하는 team에 member\_id가 PK로 있으면 team을 계속 insert 해야 해서 중복되는 이상한 상황이 나온다.
* 일대다는 객체에선 team이 중심인데도 DB 입장에서는 N에 외래키가 있으므로 member 테이블을 건드려야 한다.
  * Team.members를 수정하면 team 테이블 대신 member 테이블에 있는 외래키 `team_id`를 업데이트 한다.

{% tabs %}
{% tab title="Team.java" %}
```java
@Entity
public class Team {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "TEAM_ID")
    private Long id;

    // 1에서 N을 참조한다.
    @OneToMany
    @JoinColumn(name = "TEAM_ID")
    private List<Member> members = new ArrayList<>();
}
```
{% endtab %}

{% tab title="Member.java" %}
```java
@Entity
public class Member {

    // 단방향이므로 Team 참조가 없다.
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
    private String name;
}
```
{% endtab %}

{% tab title="JpaMain.java" %}
```java
public class JpaMain {

    public static void main(String[] args) {
        Member member = new Member();
        member.setUsername("member1");

        em.persist(member);

        Team team = new Team();
        team.setName("teamA");
        // 수정은 Team 객체에서 했는데
        // 외래 키가 있는 member 테이블로 가서 업데이트 해야 한다.
        team.getMembers().add(member);

        em.persist(team);

        tx.commit();
    }
}
```
{% endtab %}
{% endtabs %}

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

일단 각각 새 데이터를 만들었으니 insert 쿼리가 나간다.

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

이후 member 테이블에 대한 update 쿼리가 찍힌다. 비즈니스 로직만 보면 team만 손을 댄 것 같은데 쿼리를 보니 엉뚱한 member 테이블 update 쿼리가 나가서 혼란이 생긴다. 실무에서는 수 많은 테이블이 있기 때문에 더 다루기 힘들다.

일대다 대신 다대일로 하면 member가 team을 참조할 일이 없을 경우 객체 지향으로는 손해다. 하지만 JPA에서는 잘 사용할 수 있다. DB의 방향에 조금 더 맞춰 유지 보수를 호율적으로 할 수 있다.

### 정리

* 1:N에서 1이 연관 관계의 주인이다.
* 하지만 테이블의 1:N 관계는 항상 N에 외래키가 있다.
  * 객체와 테이블의 차이 때문에 반대편 테이블의 외래 키를 관리하는 특이한 구조가 된다.
* @JoinColumn을 꼭 사용해야 한다.
  * 사용하지 않으면 조인 테이블 방식이 적용된다.
    * team\_member 라는 중간 테이블이 생겨버린다.
    * 테이블이 하나 더 생기면 운영이 힘들다.
* 엔티티가 관리하는 외래키가 다른 테이블에 있어 update SQL을 추가로 실행해야 하는 단점이 있다.
* 일대다 단방향 매핑이 필요하다면 다대일 양방향 매핑을 사용하자.

## 양방향

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

* Team.members가 연관 관계의 주인이다.
* Member가 team을 읽기 전용으로 참조하려면 약간의 야매가 필요하다.

{% tabs %}
{% tab title="Team.java" %}
```java
@Entity
public class Team {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "TEAM_ID")
    private Long id;

    @OneToMany
    @JoinColumn(name = "TEAM_ID")
    private List<Member> members = new ArrayList<>();
}
```
{% endtab %}

{% tab title="Member.java" %}
```java
@Entity
public class Member {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
    private String name;

    @ManyToOne
    // 양방향 참조를 억지로 만들면서 읽기 전용으로 강제한다.
    @JoinColumn(name = "TEAM_ID", insertable = false, updateble = false)
    private Team team;
}
```
{% endtab %}
{% endtabs %}

* 둘 다 team\_id를 참조하는 연관 관계의 주인이 되어버린다.
* 이러면 어디가 기준인지 모르기 때문에 insertable과 updateble로 읽기 전용을 강제한다.

### 정리

* 이런 매핑은 공식적으로 존재하지 않는다.
* 읽기 전용 필드를 사용해 양방향처럼 사용한다.
* 그냥 다대일 양방향을 사용하자.
