# 일대일

* 일대일 관계는 그 반대도 일대일이다.
  * 그래서 외래키를 어디든 넣을 수 있다.
  * 주 테이블이든 대상 테이블이든 둘 중 하나만 넣으면 된다.
* 외래키에 데이터베이스 유니크 제약 조건이 추가되어야 한다.

## 주 테이블 외래키 단방향

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

* 회원은 라커 하나만 가질 수 있다.
* 멤버가 PK로 locker\_id를 가지고 유니크를 걸어도 되고, 라커가 PK로 member\_id를 가져고 유니크를 걸어도 된다.

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

    @Id
    @GeneratedValue
    private Long id;
    private String username;

    @OneToOne
    // member에 PK를 둔다.
    @JoinColumn(name = "LOCKER_ID")
    private Locker locker;
}
```
{% endtab %}

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

    @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%2F5e77deb892e8ebcffbd68485f3e30d46ebc5932c.png?generation=1616220500244851\&alt=media)

* 다대일 양방향 매핑처럼 외래키가 있는 곳이 연관 관계의 주인이 된다.

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

    @Id
    @GeneratedValue
    private Long id;
    private String username;

    @OneToOne
    @JoinColumn(name = "LOCKER_ID")
    private Locker locker;
}
```
{% endtab %}

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

    @Id
    @GeneratedValue
    private Long id;

    private String name;

    // 일대일에도 연관 관계 주인은 똑같은 원리로 적용된다.
    @OneToOne(mappedBy = "locker")
    private Member member;
}
```
{% endtab %}
{% endtabs %}

* 연관 관계의 주인은 외래키가 있는 곳이 되고 반대편은 `mappedBy`가 적용되었다.

## 대상 테이블 외래키 단방향

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

* 대상 테이블에 외래키가 있는 단방향
  * Member가 연관 관계의 주인이 되고 싶은데 테이블에서는 Locker에 FK가 있는 상태
* 이런 형태는 JPA에서 아예 지원 하지 않는다.

## 대상 테이블 외래키 양방향

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

* 대신 양방향은 지원을 한다.
* Locker.member를 연관 관계 주인으로 잡아서 매핑하면 된다.
* 일대일은 내가 내것만 관리할 수 있는 방식이다.
  * 내 엔티티에 있는 외래키는 내가 직접 관리한다.

1:1은 외래키를 어디에 둬도 상관이 없다면, member가 들고 있는 게 좋을까 locker에 있는 게 좋을까?

만약 미래에 회원 한 명이 여러 라커를 가질 수 있도록 바뀐다면, locker에 외래키를 두는 양방향이 좋을 것이다. locker에 있는 member\_id의 유니크 제약 조건만\`alter로 제거하면 되기 때문이다.

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

하지만 member에 외래키가 있으면 locker에 FK 칼럼을 추가해야 하고 코드 변경 포인트가 많아진다.

솔루션 마다 방향이 반대가 될 수 있고 딜레마가 있기 마련이지만, 보통 member에 locker\_id가 있는 게 더 유리하다. 비즈니스에서는 회원 조회가 많이 일어나기 때문에, 라커 정보를 가져올 때도 조인할 필요 없이 쿼리 한 방으로 라커 정보를 알 수 있는 성능 상의 이점이 있다.

## 정리

### 주 테이블(주로 많이 액세스 하는 테이블)에 외래키를 두는 방법

* Member가 locker\_id를 FK로 가진다.
* 주 객체가 대상 객체의 참조를 가지는 것처럼 주 테이블에 외래키를 두고 대상 테이블을 찾는다.
* 객체 지향 개발자가 선호한다.
* JPA 매핑이 편리하다.
* 주 테이블만 조회해도 대상 테이블에 데이터가 있는지 확인 가능하다.
* 값이 없으면 외래 키에 null을 허용하는 단점이 있다.

### 대상 테이블에 외래키를 두는 방법

* Locker가 member\_id를 FK로 가진다.
* 대상 테이블에 외래키가 존재한다.
* 전통적인 DB 개발자가 선호한다.
* 주 테이블과 대상 테이블을 일대일에서 일대다 관계로 변경할 때 테이블 구조를 유지할 수 있다.
* 프록시 기능의 한계로 지연 로딩으로 설정해도 **항상 즉시 로딩**된다.
  * Member.locker를 조회할 때, JPA는 프록시를 만들기 위해 Member.locker에 값이 있는지 미리 알아야 한다.
  * 근데 이 값을 알려면 member만 조회하는 게 아니라 locker 테이블도 어차피 함께 쿼리해야 한다.
  * 어차피 쿼리가 나갔으니 프록시로 만들 이유가 없어서 즉시 로딩된다.
