일대일

  • 일대일 관계는 그 반대도 일대일이다.

    • 그래서 외래키를 어디든 넣을 수 있다.

    • 주 테이블이든 대상 테이블이든 둘 중 하나만 넣으면 된다.

  • 외래키에 데이터베이스 유니크 제약 조건이 추가되어야 한다.

주 테이블 외래키 단방향

  • 회원은 라커 하나만 가질 수 있다.

  • 멤버가 PK로 locker_id를 가지고 유니크를 걸어도 되고, 라커가 PK로 member_id를 가져고 유니크를 걸어도 된다.

@Entity
public class Member {

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

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

다대일 양방향 매핑과 유사하다.

주 테이블 외래키 양방향

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

@Entity
public class Member {

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

    @OneToOne
    @JoinColumn(name = "LOCKER_ID")
    private Locker locker;
}
  • 연관 관계의 주인은 외래키가 있는 곳이 되고 반대편은 mappedBy가 적용되었다.

대상 테이블 외래키 단방향

  • 대상 테이블에 외래키가 있는 단방향

    • Member가 연관 관계의 주인이 되고 싶은데 테이블에서는 Locker에 FK가 있는 상태

  • 이런 형태는 JPA에서 아예 지원 하지 않는다.

대상 테이블 외래키 양방향

  • 대신 양방향은 지원을 한다.

  • Locker.member를 연관 관계 주인으로 잡아서 매핑하면 된다.

  • 일대일은 내가 내것만 관리할 수 있는 방식이다.

    • 내 엔티티에 있는 외래키는 내가 직접 관리한다.

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

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

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

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

정리

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

  • Member가 locker_id를 FK로 가진다.

  • 주 객체가 대상 객체의 참조를 가지는 것처럼 주 테이블에 외래키를 두고 대상 테이블을 찾는다.

  • 객체 지향 개발자가 선호한다.

  • JPA 매핑이 편리하다.

  • 주 테이블만 조회해도 대상 테이블에 데이터가 있는지 확인 가능하다.

  • 값이 없으면 외래 키에 null을 허용하는 단점이 있다.

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

  • Locker가 member_id를 FK로 가진다.

  • 대상 테이블에 외래키가 존재한다.

  • 전통적인 DB 개발자가 선호한다.

  • 주 테이블과 대상 테이블을 일대일에서 일대다 관계로 변경할 때 테이블 구조를 유지할 수 있다.

  • 프록시 기능의 한계로 지연 로딩으로 설정해도 항상 즉시 로딩된다.

    • Member.locker를 조회할 때, JPA는 프록시를 만들기 위해 Member.locker에 값이 있는지 미리 알아야 한다.

    • 근데 이 값을 알려면 member만 조회하는 게 아니라 locker 테이블도 어차피 함께 쿼리해야 한다.

    • 어차피 쿼리가 나갔으니 프록시로 만들 이유가 없어서 즉시 로딩된다.

Last updated