상세 컨텐츠

본문 제목

JPA 조인테이블 (@ joinColumn, @inverseJoinColumn, @SecondaryTable)

기록 - 프로그래밍/Java

by wjjun 2024. 2. 8. 01:10

본문

테이블 연관관계를 설계하는 방법은 2가지가 있습니다.

1) 외래 키를 이용한 조인 컬럼을 사용

2) 테이블을 이용한 조인 테이블 사용

 

조인 컬럼 사용

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

외래 키에 null을 허용하는 관계를 선택적 비식별 관계라고 합니다.

 

선택적 비실별 관계는 외래 키에 Null을 허용하므로 외부조인 OUTER JOIN을 사용해야 합니다. 실수로 내부 조인을 사용하면 관계가 없는 테이블은 조회되지 않습니다. 두 테이블이 아주 가끔 관계를 맺는다면 외래 키 값 대부분이 null로 저장되는 단점이 발생합니다.

 

조인 테이블 사용

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

 

회원과 사물함 테이블이 있고 데이터를 각각 등록했다가 회원이 원할 때 사물함을 선택하면 MEMBER_LOCKER (조인테이블) 테이블에만 값을 추가하면 됩니다. 조인테이블의 가장 큰 단점은 테이블을 하나 추가해야 합니다. 그래서 관리 테이블이 늘어나고 회원과 사물함 테이블을 조인하려면 MEMER_LOCKER 테이블까지 추가로 조인하면 됩니다.

 

 

일대일 조인 테이블 

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

@Entity
public class Parent {

	@Id
    @GeneratedValue
    private Long id;
    private String name;
    
    @OneToOne
    @JoinTable(name = "PARENT_CHILD", 
    			joinColumn = @JoinColumn(name = "PARENT_ID"),
    			inverseJoinColumns = @JoinColumn(name = "CHILD_ID")
	)
    private Child child;
    ...
}

@Entity
public class Child {

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

부모 엔티티에 @JoinColumn 대신 @JoinTable을 사용했습니다. @JoinTable 속성 정보는 다음과 같습니다.

name : 매핑할 조인 테이블 이름

joinColumn : 현재 엔티티를 참조하는 외래키

inverseJoinColumn : 반대방향 엔티티를 참조하는 외래키

 

양방향으로 매핑하려면 다음 코드를 추가해야 합니다.

public class Child {
	...
    @OneToOne(mappedBy = "child")
    private Parent parent;
}

 

일대다 조인 테이블

조인 테이블의 컬럼 중 다(N)와 관련된 컬럼인 CHILD_ID에 유니크 제약조건을 걸어야 합니다.

@Entity
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"),
        inverseJoinColumn = @JoinColumn(name = "CHILD_ID")
    }
    private List<Child> child = new ArrayList<Child>();
    ...
}

@Entity
public class Child {
	
    @Id @GeneratedValue
    @Column(name = "CHILD_ID")
    private Long id;
    private String name;
    ...
}

 

다대일 조인 테이블

다대일은 일대다에서 방향만 반대이므로 조인 테이블 모양은 일대다와 같습니다.

@Entity
public class Parent {

    @Id @GeneratedValue
    @Column(name = "PARENT_ID")
    private Long id;
    private String name;
    
    @OneToMany(mappedBy = "parent")
    private List<Child> child = new ArrayList<Child>();
    ...
}

@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"),
        inversJoinColumns = @JoinColumns (name = "PARENT_ID")
    }
    private Parent parent;
    ...
}

 

다대다 조인 테이블

조인 테이블의 두 컬럼을 합해 하나의 복합 유니크 제약조건을 걸어야 합니다.

(PARENT_ID, CHILD_ID는 복합 기본키로 유니크 제약조건이 걸려 있습니다.)

@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>();
    ...
}

@Entity
public class Child {
    
    @Id @GeneratedValue
    @Column(name = "CHILD_ID")
    private Long id;
    private String name;
    ...
}

 

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

@SecondaryTable을 이용해서 엔티티에 여러 테이블을 매핑할 수 있습니다.

@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.pkJoinColumns : 매핑할 다른 테이블의 기본 키 컬럼 속성 예제에서는 기본 키 컬럼명을 BOARD_DETAIL_ID로 지정했습니다.

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

 

더 많은 테이블을 매핑하려면 @SecondaryTables를 사용하면 됩니다.

@SecondaryTables({
    @SecondaryTable(name = "BOARD_DETAIL"),
    @SecondaryTable(name = "BOARD_FILE")
})

@SecondaryTable을 사용해서 두 테이블을 하나의 엔티티에 매핑하는 방법보다는 테이블당 엔티티를 각각 만들어서 일대일 매핑하는 것을 권장합니다. 이 방법은 항상 두 테이블을 조회하므로 최적화하기가 어렵습니다. 반면 일대일 매핑은 원하는 부분만 조회할 수 있고 필요하면 둘을 함께 조회할 수 있습니다.

관련글 더보기

댓글 영역