조인 테이블

데이터베이스 테이블의 연관관계를 설계하는 방법은 크게 2가지 입니다.

  • 조인 컬럼 사용(외래 키)
  • 조인 테이블 사용(테이블 사용)

테이블 간에 관계는 주로 조인 컬럼이라 부르는 외래 키 컬럼을 사용해서 관리합니다.

스크린샷 2019-10-30 오전 1 16 48

조인 컬럼 사용

스크린샷 2019-10-30 오전 1 13 19

조인 컬럼 데이터

위의 그림에서 회원과 사물함이 있고 각각 테이블에 데이터를 등록했다가 회원이 원할 때 사물함을 선택할 수 있다고 가정해보겠습니다. 회원이 사물함을 사용하기 전까지 아직 둘 사이에 관계가 없으므로 MEMBER 테이블의 LOCKER_ID 외래 키에 null을 입력해두어야 합니다. 이렇게 외래 키에 null을 허용하는 관계를 선택적 비식별 관계라고 합니다.

선택적 비식별 관계는 외래 키에 null을 허용하므로 회원과 사물함을 조인할 때 외부 조인을 사용해야 합니다. 실수로 내부 조인을 사용하면 사물함과 관계 없는 회원은 조회되지 않습니다. 그리고 회원과 사물함이 아주 가끔 관계를 맺는다면 외래 키 값 대부분이 null로 지정되는 단점이 있습니다.

조인 테이블 사용

아래 그림은 조인 컬럼을 사용하는 대신에 조인 테이블을 사용해서 연관관계를 관리하고 있습니다.

스크린샷 2019-10-30 오전 1 38 42

조인 테이블 사용

스크린샷 2019-10-30 오전 1 44 41

조인 테이블 데이터

이 방법은 조인 테이블이라는 별도의 테이블을 사용해서 연관관계를 관리합니다.
조인 컬럼을 사용하는 방법은 단순히 외래 키 컬럼만 추가해서 연관관계를 맺지만 조인 테이블을 사용하는 방법은 연관관계를 관리하는 조인 테이블을 추가하고 여기서 두 테이블의 외래 키를 가지고 연관관계를 관리합니다. 따라서 MEMBER, LOCKER 테이블에는 연관관계를 관리하기 위한 외래 키 컬럼이 없습니다.

조인 테이블의 가장 큰 단점은 테이블을 하나 추가해야 한다는 점입니다. 따라서 관리해야 하는 테이블이 늘어나고 회원과 사물함 두 테이블을 조인하려면 MEMBER_LOCKER 테이블까지 추가로 조인해야 합니다. 따라서 기본은 조인 컬럼을 사용하고 필요하다고 판단되면 조인 테이블을 사용하면 됩니다.

일대일 조인 테이블

조인 테이블 일대일에서 조인 테이블을 살펴보겠습니다. 일대일 관계를 만들려면 조인 테이블의 외래 키 컬럼 각각에 총 2개의 유니크 제약조건을 걸어야 합니다.

@Setter
@Getter
@Entity
public class Parent {

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

    @OneToOne
    @JoinTable(name = "PARENT_CHILD",
        joinColumns = @JoinColumn(name = "PARENT_ID"),
        inverseJoinColumns = @JoinColumn(name = "CHILD_ID")
    )
    private Child child;
}

@Getter
@Setter
@Entity
public class Child {

    @Id @GeneratedValue
    @Column(name = "CHILD_ID")
    private Long id;
    private String name;
}






@Test
@Transactional
@Rollback(false)
public void 식별자_테스트() throws Exception {

        Parent parent = new Parent();
        parent.setName("임종수");

        Child child = new Child();
        child.setName("임준영");

        parent.setChild(child);
        em.persist(parent);
        em.persist(child);

}

다대일 조인 테이블

@Setter
@Getter
@Entity
public class Parent {

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

    @OneToMany
    private List<Child> child = new ArrayList<Child>();
}


@Getter
@Setter
@Entity
public class Child {

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


    @ManyToOne(optional = false)
    @JoinTable(name = "PARENT_CHILD",
            joinColumns = @JoinColumn(name = "CHILD_ID"),
            inverseJoinColumns = @JoinColumn(name = "PARENT_ID")
    )
    private Parent parent;

}

다대다 조인 테이블

다대다 관계를 만들려면 조인 테이블의 두 컬럼을 합해서 하나의 복합 유니크 제약조건을 걸어야 합니다.

@Setter
@Getter
@Entity
public class Parent {

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

    @ManyToMany
    @JoinTable(name = "PARENT_CHILD",
            joinColumns = @JoinColumn(name = "PARENT_ID"),
            inverseJoinColumns = @JoinColumn(name = "CHILD_ID")
    )
    private List<Child> child = new ArrayList<Child>();
}

@Getter
@Setter
@Entity
public class Child {

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

}

엔티티 하나에 여러 테이블 매핑

잘 사용하지는 않지만 @SecondaryTable을 사용하면 한 엔티티에 여러 테이블을 매핑할 수 있습니다.

스크린샷 2019-10-30 오전 2 39 20

@Entity
@Table(name = "BOARD")
@SecondaryTable(name = "BOARD_DETAIL",
pkJoinColumns = @PrimaryKeyJoinColumn(name = "BOARD_DETAIL_ID"))
public class Board{

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

    private String title;

    @Column(table = "BOARD_DETAIL")
    private String content;

}

위의 코드를 살펴보면 Board 엔티티는 @Table을 사용해서 BOARD 테이블과 매핑했습니다. 그리고 @SecondaryTable을 사용해서 BOARD_DETAIL 테이블을 추가로 매핑했습니다.

@SecondaryTable 속성은 다음과 같습니다.

@SecondaryTable.name: 매핑할 다른 테이블의 이름, 예제에서는 테이블명을 BOARD_DETAIL로 지정했습니다.

@SecondaryTable.pkJoinColumn: 매핑할 다른 테이블의 기본 키 컬럼 속성, 예제에서는 기본 키 컬럼명을 BOARD_DETAIL_ID로 지정했습니다.

@Column(table = "BOARD_DETAIL")
private String content;

content 필드는 @Column(table = "BOARD_DETAIL")을 사용해서 BOARD_DETAIL 테이블의 컬럼에 매핑했습니다. title 필드처럼 테이블을 지정하지 않으면 기본 테이블인 BOARD에 매핑됩니다.

@SecondaryTables({

    @SecondaryTable(name = "BOARD_DETAIL"),
    @SecondaryTable(name = "BOARD_FILE")

})

더 많은 테이블을 매핑하려면 @SecondaryTables를 사용하면 됩니다.
하지만 이 방법은 항상 두 테이블을 조회하므로 최적화하기가 어렵습니다. 반면에 일대일 매핑은 원하는 부분만 조회 할 수 있고 필요하면 둘다 함께 조회됩니다.

참고: 김영한의 JAVA ORM 표준 JPA 프로그래밍

'SpringFramework > JPA' 카테고리의 다른 글

지연로딩과 즉시로딩  (0) 2019.11.23
프록시와 연관관계 관리  (0) 2019.11.22
복합 키와 식별관계 매핑  (0) 2019.10.29
고급매핑 - 상속관계 매핑  (0) 2019.10.28
스프링 부트 테스트 : @DataJpaTest  (0) 2019.10.09

+ Recent posts