# Chapter 5: 연관관계 매핑 기초

<br>

## 1. 단방향 연관 관계

### 객체 연관관계와 테이블 연관관계의 가장 큰 차이

* `참조`를 통한 연관관계는 언제나 `단방향`이다
  * 객체간에 `양방향` 으로 만들고 싶으면 반대쪽에도 필드를 추가해서 참조를 보관해야한다
    * 결국 연관관계를 하나 더 만들어야 한다
  * 이렇게 양쪽에서 `서로 참조`하는 것을 `양방향 연관관계`라 한다
    * 하지만 정확히 이야기하면, 이것은 양방향 관계가 아니라 서로 다른 `단방향 관계 2개`다
    * 반면, 테이블은 `외래 키` 하나로 양방향으로 조인할 수 있다

### 객체 연관관계 vs 테이블 연관관계

* 객체
  * `참조 (주소)` 로 연관관계를 맺는다
  * 연관된 데이터를 `참조` 를 사용해 조회한다
    * 연관관계를 참조를 사용해서 탐색하는 것을 `객체 그래프 탐색` 이라 한다
  * 참조를 사용하는 객체의 연관관계는 `단방향` 이다
    * 객체를 양방향으로 참조하려면, 단방향 연관관계를 2개 만들어야 한다
* 테이블
  * `외래 키` 로 연관관계를 맺는다
  * 연관된 데이터를 `JOIN` 을 통해 조회한다
    * 외래 키를 사용해서 연관관계를 탐색하는 것을 `JOIN` 이라 한다
  * 외래 키를 사용하는 테이블의 연관관계는 `양방향` 이다

### `@JoinColumn`

* 외래 키를 매핑할 때 사용한다
* `속성`

  | 속성                                                                  | 기능                                             | 기본값                           |
  | ------------------------------------------------------------------- | ---------------------------------------------- | ----------------------------- |
  | name                                                                | 매핑할 외래 키 이름을 지정한다                              | 필드명 + \_ + 참조하는 테이블의 기본 키 컬럼명 |
  | referencedColumnName                                                | 외래 키가 참조하는 대상 테이블의 컬럼명                         | 차모하는 테이블의 기본 키 컬럼명            |
  | foreignKey (DDL)                                                    | 외래 키 제약조건을 직접 지정할 수 있다 (이 속성은 테이블을 생성할 때만 사용!) |                               |
  | unique , nullable , insertable , updatable, columnDefinition, table | @Column 의 속성과 같다                               |                               |
* `@JoinColumn` 생략
  * 해당 어노테이션을 생략하면 외래 키를 찾을 때 `기본 전략`을 사용한다
  * 기본 전략
    * 필드명 + \_ + 참조하는 테이블의 컬럼명

### `@ManyToOne`

* 이름 그대로 다대일 (N:1) 관계라는 매핑 정보다
* 연관관계를 매핑할 때 이렇게 다중성을 나타내는 어노테이션을 필수로 사용해야 한다
* `속성`

  | 속성           | 기능                                                                          | 기본값             |
  | ------------ | --------------------------------------------------------------------------- | --------------- |
  | optional     | false로 설정하면 연관된 엔티티가 항상 있어야 한다                                              | true            |
  | fetch        | global fetch 전략을 설정                                                         | FetchType.EAGER |
  | cascade      | 영속성 전이 기능을 사용                                                               |                 |
  | targetEntity | 연관된 엔티티의 타입 정보를 설정한다 (이 기능은 거의 사용하지 않는다! 컬렉션을 사용해도 제네릭으로 타입 정보를 알 수 있기 때문!) |                 |

## 2. 연관관계 사용

### 2-1. 저장

* JPA에서 엔티티를 저장할 때 연관된 모든 엔티티는 `영속 상태` 여야 한다
* JPA는 참조한 객체의 `식별자` 를 `외래 키` 로 사용해서 적절한 `등록 쿼리` 를 생성한다

### 2-2. 조회

연관관계가 있는 엔티티를 조회하는 방법

1. 객체 그래프 탐색
   * 객체 연관관계를 사용한 조회
2. 객체지향 쿼리 사용 (JPQL)
   * 예를 들어 회원을 대상으로 조회하는데 팀1에 소속된 회원만 조회하려면, 회원과 연관된 팀 엔티티를 검색 조건으로 사용해야 한다
     * SQL은 연관된 테이블을 JOIN 해서 검색조건을 사용하면 된다
     * JPQL도 `JOIN`을 지원한다 (문법은 약간 다름!)
   * JPQL은 객체(엔티티)를 대상으로 하고, SQL보다 간결하다

### 2-3. 수정

* 수정은 em.update() 같은 method가 없다
* 단순히 불러온 `엔티티의 값만 변경`해두면, 트랜잭션을 `커밋` 할 때 `플러시` 가 일어나면서 `변경 감지` 기능이 작동한다
  * 그리고 변경사항을 데이터베이스에 자동으로 반영한다
* 연관관계를 수정할 때도 같은데, `참조하는 대상` 만 변경하면 나머지는 JPA가 자동으로 처리한다

### 2-4. 연관관계 제거

: 연관관계를 `null`로 설정

### 2-5. 연관된 엔티티 삭제

* 연관된 엔티티를 삭제하려면 기존에 있던 `연관관계를 먼저 제거`하고 `삭제`해야 한다
  * 그렇지 않으면 외래 키 제약조건으로 인해, DB에서 오류가 발생한다

## 3. 양방향 연관관계

* 데이터베이스 테이블은 외래 키 하나로 양방향으로 조회할 수 있다
* 객체는 연관관계가 맺어진 반대 방향에서도 접근할 수 있게, `컬렉션` 과 `@OneToMany` 매핑 정보, 그리고 `mappedBy` 속성을 사용한다
  * ex)
    * 회원 → 팀 (Member.team)
    * 팀 → 회원 (Team.members)
      * 여기서 members는 list
* `mappedBy` 속성은 양방향 매핑일 때 사용하는데, 반대쪽 매핑의 필드 이름을 값으로 주면 된다

## 4. 연관관계의 주인

* `mappedBy` 가 필요한 이유
  * 테이블은 `외래 키` 하나로 두 테이블의 연관관계를 관리
  * 엔티티를 단방향으로 매핑하면 참조를 하나만 사용하므로 이 참조로 외래키를 관리하면 됨
  * but, 엔티티를 `양방향`으로 매핑하면 양쪽에서 `서로를 참조`하게 됨
    * 따라서 객체의 연관관계를 관리하는 포인트는 2곳으로 늘어난다
    * 객체의 참조는 둘인데, 외래 키는 하나다
  * 이러한 차이로 인해 JPA에서는 두 객체 연관 관계 중 하나를 정해서 테이블의 외래 키를 관리해야 하는데, 이것을 `연관관계의 주인` 이라 한다
* `연관관계의 주인`
  * 연관관계의 주인만이 DB 연관관계와 매핑되고, 외래키를 관리할 수 있다
    * 주인이 아닌 쪽은 읽기만 할 수 있다
  * 주인은 `mappedBy` 속성을 사용하지 않는다
    * 주인이 아니면 `mappedBy` 속성을 사용해서 `속성의 값`으로 `연관관계의 주인`을 `지정`해야 한다
  * 연관관계의 주인을 정한다는 것은 `외래 키 관리자`를 선택하는 것이다
    * 연관관계의 주인은 `외래 키가 있는 곳`을 뜻한다
    * 연관관계의 주인만 데이터베이스 연관관계와 매핑되고, 외래 키를 관리할 수 있다
    * 주인이 아닌 반대편은 읽기만 가능하고, 외래키를 변경하지는 못한다

## 5. 양방향 연관관계 저장

* 양방향 연관관계는 연관관계의 주인이 외래 키를 관리한다
  * 따라서 주인이 아닌 방향은 값을 설정하지 않아도 데이터베이스에 외래 키 값이 정상 입력된다

## 6. 양방향 연관관계 주의점

* 양방향 연관관계를 설정하고 가장 흔히 하는 실수는, 연관관계의 주인에는 값을 입력하지 않고, 주인이 아닌 곳에만 값을 입력하는 것이다
  * `연관관계의 주인`만이 `외래 키`의 `값`을 `변경`할 수 있다는 것을 기억하자!

### 6-1. 순수한 객체까지 고려한 양방향 연관관계

* 연관관계의 주인에만 값을 저장하고 주인이 아닌 곳에는 값을 저장하지 않는 것이 아니라, `객체 관점` 에서 `양쪽 방향`에 모두 값을 입력해주는 것이 가장 안전하다
  * 양쪽 방향 모두 값을 입력하지 않으면 JPA를 사용하지 않는 순수한 객체 상태에서 심각한 문제가 발생할 수 있다
* 결론
  * 객체의 양방향 연관관계는 `양쪽 모두 관계를 맺어주자`!

### 6-2. 연관관계 편의 메소드

* 연관관계 설정 method를 각각 호출하다 보면 실수로 둘 중 하나만 호출해서 양방향이 깨질 수 있다
  * 양방향 관계에서 두 코드는 하나인 것처럼 사용하는 것이 안전한다
* 한 번에 양방향 관게를 설정하는 method를 `연관관계 편의 method` 라 한다

## 7. 정리

* 단방향 매핑과 비교해서 양방향 매핑은 복잡하다
  * 연관관계의 주인도 정해야 하고, 두 개의 단방향 연관관계를 양방향으로 만들기 위해 로직도 잘 관리해야 한다
  * 중요한 사실은 연관관계가 하나인 단방향 매핑은 언제나 연관관계의 주인이라는 점이다!
    * 양방향은 여기에 주인이 아닌 연관관계를 하나 추가했을 뿐이다
  * 결국 단방향과 비교해서 양방향의 장점은 `반대 방향으로 객체 그래프 탐색 기능이 추가된 것` 뿐이다
* 단방향 매핑만으로 테이블과 객체의 연관관계 매핑은 이미 완료되었다
* 단방향을 양방향으로 만들려면 반대방향으로 `객체 그래프 탐색` 기능이 추가된다
* 양방향 연관관계를 매핑하려면 객체에서 `양쪽 방향`을 `모두 관리`해야 한다
* 양방향 매핑은 복잡하다
  * 우선 단방향 매핑을 사용하고, 반대 방향으로 객체 그래프 탐색 기능(JPQL 쿼리 탐색 포함)이 필요할 때 양방향을 사용하도록 코드를 추가해도 된다
