JPA 고급매핑의 마지막 시간이다.

조인 테이블

데이터베이스 테이블의 연관관계를 설계하는 방법은 크게 2가지이다.
1. 조인 컬럼 사용
2. 조인 테이블 사용

조인 컬럼 사용

테이블 간에 관계는 주로 조인 컬럼이라 부르는 외래 키 컬럼을 사용해서 관리한다.
예를 들어 회원과 사물함이 있는데 각각 테이블에 데이터를 등록했다가 회원이 원할 때 사물함을 선택할 수 있다고 가정해보자. 회원이 사물함을 사용하기 전까지는 아직 둘 사이에 관계가 없으므로 Member 테이블의 외래 키에 null을 입력해두어야 한다. 이렇게 외래 키에 null을 허용하는 관계를 선택적 비식별 관계라 한다.

조인 테이블 사용

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

일대일 조인 테이블

@Entity
@Data
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;
}

@Entity
@Data
public class Child {

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

  private String name;

}

부모 엔티티를 보면 @JoinColumn 대신 @JoinTable을 사용했다. @JoinTable의 속성을 다음과 같다.
name : 매핑할 조인 테이블 이름
joinColumns : 현재 엔티티를 참조하는 외래 키
inverseJoinColumns : 반대방향 엔티티를 참조하는 외래 키

일대다 조인 테이블

일대다 관계를 만들려면 조인 테이블의 컬럼 중 다(N)와 관련된 컬럼인 CHILD_ID에 유니크 제약 조건을 걸어야 한다.(CHILD_ID 는 기본 키이므로 유니크 제약 조건이 걸려있다.)

@Entity
@Data
public class Parent {
  @Id
  @GeneratedValue
  @Column(name = "PARENT_ID")
  private Long id;

  private String name;

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

@Entity
@Data
public class Child {

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

  private String name;
}

다대일 조인 테이블

다대일은 일대다에서 방향만 반대이므로 조인 테이블 모양은 일대다에서 설명한 것도 같다.

@Entity
@Data
public class Parent {
  @Id
  @GeneratedValue
  @Column(name = "PARENT_ID")
  private Long id;

  private String name;

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

@Entity
@Data
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;
}

다대다 조인 테이블

다대다 관계를 만들려면 조인 테이블의 두 컬럼을 합해서 하나의 복합 유니크 제약조건을 걸어야 한다.(PARENT_ID, CHILD_ID 는 복합 기본키이므로 유니크 제약 조건이 걸려 있다.)

@Entity
@Data
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<>();

}

@Entity
@Data
public class Child {

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

  private String name;
}

조인 테이블에 컬럼을 추가하면 @JoinTable 전략을 사용할 수 없다. 대신에 새로운 엔티티를 만들어서 조인 테이블과 매핑해야 한다.

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

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

@Entity
@Data
@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속성은 다음과 같다.
name : 매핑할 다른 테이블의 이름, 위에서는 BOARD_DETAIL로 지정 했다.
pkJoinColumns : 매핑할 다른 테이블의 기본 키 컬럼 속성, 위에서는 BOARD_DETAIL_ID로 지정했다.

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

content 필드는 @Column(table = “BOARD_DETAIL”)을 사용해서 BOARD_DETAIL 테이블의 컬럼에 매핑했다. title 필드처럼 테이블을 지정하지 않으면 기본 테이블인 BOARD에 매핑 된다.
더 많은 데이블을 매핑하려면 @SecondaryTables를 사용하면 된다.

이렇게 고급매핑에 대해서 알아봤다. 쓰면서 이해는 됐는데 다시 천천히 봐야겠다.

출처 : 자바 ORM 표준 JPA 프로그래밍 (김영한)