JPA - 고급 매핑(1)

DevSeoRex·2022년 11월 6일
2
post-thumbnail

상속 관계 매핑

관계형 데이터베이스에는 객체지향 언어에서 다루는 상속이라는 개념이 없다.
관계형 데이터베이스에서는 슈퍼타입 - 서브타입 관계 라는 모델링 기법이 객체의 상속 개념과 가장 유사하다.


슈퍼타입 - 서브타입 논리 모델


객체 상속 모델

ORM에서 이야기하는 상속 관계 매핑은 객체의 상속 구조와 데이터베이스의 슈퍼타입 서브타입 관계매핑은 객체의 상속 구조와 데이터베이스의 슈퍼타입 서브타입 관계를 매핑하는 것이다.

슈퍼타입 서브타입 -> 실제 물리 모델 테이블로 구현하는 방법 3가지

  • 각각의 테이블로 변환 : 각각을 모두 테이블로 만들고 조회할 때 조인을 사용한다. JPA에서는 조인 전략이라고 한다.

  • 통합 테이블로 변환 : 테이블을 하나만 사용해서 통합한다. JPA에서는 단일 테이블 전략이라 한다.

  • 서브타입 테이블로 변환 : 서브 타입마다 하나의 테이블을 만든다. JPA에서는 구현 클래스마다 테이블 전략이라 한다.

조인 전략(Joined Strategy)

  • 조인 전략은 엔티티 각각을 모두 테이블로 만들고, 자식 테이블이 부모 테이블의 기본 키를 받아서 기본 키 + 외래 키로 사용하는 전략이다.
  • 조회할 때 조인을 자주 사용한다.
  • 객체는 타입으로 구분할 수 있지만 테이블은 타입의 개념이 없다.
    - 타입을 구분하는 컬럼을 추가해야 한다. DTYPE 컬럼을 구분 컬럼으로 사용한다.
// 조인 전략 매핑 예제
@Entity
@Inheritance(strategy = InheritanceType.JOINED)
@DiscriminatorColumn(name = "DTYPE")
public abstract class Item {
	
    @Id @GeneratedValue
    @Column(name = "ITEM_ID")
    private Long id;
    
    private String name; // 이름
    private int price; 	// 가격
    ...
}

@Entity
@DiscriminatorValue("A")
public class Album extends Item {
	private String artist;
    ...
}

@Entity
@DiscriminatorValue("M")
public class Movie extends Item {
	private String director;
    private String actor;
    ...
}
  • 상속 매핑은 부모 클래스에 @Inheritance 애너테이션을 사용해야 한다.
  • 조인 전략을 사용하므로 inheritanceType.JOINED를 사용해야 한다.
  • @DiscriminatorColumn(name = "DTYPE")를 사용해서 부모 클래스의 구분 컬럼을 지정한다.
  • 기본값이 DTYPE이므로 @DiscriminatorColumn으로 줄여서 사용 가능하다.
  • @DiscriminatorValue("M")을 사용하면 엔티티를 저장할 때 구분 컬럼에 입력할 값을 지정할 수 있다.
  • 영화 엔티티를 저장할 경우, 구분 컬럼인 DTYPE에 값 M이 저장된다.

자식 테이블의 기본 키 컬럼명 변경

기본값으로 자식 테이블은 부모 테이블의 ID 컬럼명을 그대로 사용한다.
자식 테이블의 기본 키 컬럼명을 변경하고 싶다면 @PrimaryKeyJoinColumn을 사용하면 된다.

// ID 재정의 예제
@Entity
@DiscriminatorValue("B")
@PrimaryKeyJoinColumn(name = "BOOK_ID") // ID 재정의
public class Book extends Item {
	private String author; 	// 작가
    private String isbn; 	// ISBN
    ...
}

부모 클래스인 Item 클래스의 ID 컬럼명인 ITEM_ID 대신에
@PrimaryKeyJoinColumn을 사용하여 BOOK_ID로 기본 키 컬럼명을 재정의했다.

조인 전략 정리

  • 장점

    • 테이블이 정규화된다.
    • 외래 키 참조 무결성 제약조건을 활용할 수 있다.
    • 저장공간을 효율적으로 사용한다.
  • 단점

    • 조회 쿼리가 복잡하다.
    • 데이터를 등록할 INSERT SQL을 두 번 실행한다.
    • 조회할 때 조인이 많이 사용되므로 성능이 저하될 수 있다.
  • 특징

    • 하이버네이트를 포함한 몇몇 구현체는 구분 컬럼 없이도 동작한다.
    • JPA 표준 명세는 구분컬럼을 사용하도록 되어있다.
  • 관련 애너테이션

    • @PrimaryKeyJoinColumn
    • @DiscriminatorColunm
    • @DiscriminatorValue

단일 테이블 전략(Single-Table Strategy)

  • 테이블을 하나만 사용하는 전략이다.
  • 구분 컬럼(DTYPE)으로 어떤 자식 데이터가 저장되었는지 구분한다.
  • 조회할 때 조인을 사용하지 않으므로 일반적으로 가장 빠르다.
  • 자식 엔티티가 매핑한 컬럼은 모두 null을 허용해야 한다는 주의점이 있다.

Book 엔티티를 저장한다고 가정하면, ITEM 테이블의 AUTHOR,ISBN 컬럼만 사용하므로,
다른 엔티티와 매핑된 ARTIST,DIRECTOR,ACTOR 컬럼은 사용하지 않기 때문에 null이 입력된다.

// 단일 테이블 전략 매핑 예제
@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "DTYPE")
public abstract calss Item {
	
    @Id @GeneratedValue
    @Column(name = "ITEM_ID")
    private Long id;
  	private String name;
    private int price;
    ...
    
}

@Entity
@DiscriminatorValue("A")
public class Album extends Item { ... }

@Entity
@DiscriminatorValue("M")
public class Movie extends Item { ... }

@Entity
@DiscriminatorValue("B")
public class Book extends Item { ... }
  • InheritanceType.SINGLE_TABLE로 지정하면 단일 테이블 전략을 사용한다.
  • 테이블 하나에 모든 것을 통합하므로 구분 컬럼을 필수로 사용해야 한다.

단일 테이블 전략 정리

  • 장점

    • 조회 쿼리가 단순하다
    • 조인이 필요 없으므로 일반적으로 조회 성능이 빠르다
  • 단점

    • 자식 엔티티가 배핑한 컬럼은 모두 null을 허용해야 한다.
    • 테이블이 커질 수 있다.
    • 상황에 따라서는 조회 성능이 오히려 느려질 수 있다.
  • 특징

    • 구분 컬럼을 꼭 사용해야 한다(@DiscriminatorColumn을 꼭 설정해야 한다)
    • @DiscriminatorColumn를 지정하지 않으면 기본으로 엔티티 이름을 사용한다.

구현 클래스마다 테이블 전략(Table-per-Concrete-Class Strategy)

  • inheritanceType.TABLE_PER_CLASS를 선택하면 구현 클래스마다 테이블 전략을 사용한다.
  • 자식 엔티티마다 테이블을 만든다.
  • 일반적으로 추천하지 않는 전략이다.
// 구현 클래스마다 테이블 전략 매핑 예제
@Entity
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class Item {
	
    @Id @GeneratedValue
    @Column(name = "ITEM_ID")
    private Long id;
    
    private String name;
    private int price;
   ...
}

@Entity
public class Album extends Item { ... }

@Entity
public class Movie extends Item { ... }

@Entity
public class Book extends Item { ... }
  • 장점
    • not null 제약조건을 사용할 수 있다.
    • 서브 타입을 구분해서 처리할 때 효과적이다.
  • 단점
    • 여러 자식 테이블을 함께 조회할 때 성능이 느리다(SQL에 UNION을 사용해야 한다.)
    • 자식 테이블을 통합해서 쿼리하기 어렵다.
  • 특징
    • 구분 컬럼을 사용하지 않는다.

이 전략은 데이터베이스 설계자와 ORM 전문가 둘 다 추천하지 않는 전략이다.

출처 : 인프런(자바 ORM 표준 JPA 프로그래밍 기본편)

0개의 댓글