[TIL] DB의 상속 관계 매핑

·2023년 1월 4일
0

TIL

목록 보기
8/8
post-thumbnail

객체 지향 언어에는 상속이라는 관계성이 존재하지만 관계형 데이터베이스에는 상속 개념 없다. 그렇다면 테이블에서 공통된 부분을 별도로 분리해서 사용하는 방법은 없을까?🤔

슈퍼-서브 타입 데이터 모델링 기법

Extended ER모델이라고도 불리는 슈퍼-서브 타입 데이터 모델은 데이터의 공통점, 차이점 등의 특징을 고려하여 효과적으로 데이터 모델링을 할 수 있는 기법이다.

🧐 Extended ER모델...Extended...☝️🤓

공통의 부분은 슈퍼타입으로 모델링하고, 공통으로부터 상속 받아 다른 엔티티와 차이가 있는 속성은 서브타입으로 구분해 구성한다.

서브타입은 속성에 따라 배타적(exclusive), 포괄적(inclusive)서브 카테고리로 나눌 수 있다.
1. 배타적(exclusive) : 서브타입간에 교집합이 존재하지 않을 경우
2. 포괄적(inclusive) : 서브타입간에 교집합이 존재할 경우

업무의 모습을 정확하게 표현하고 물리적 데이터 모델로 변환할 때 선택의 폭을 넓힐 수 있는 장점이 있다.

해당 모델링 기법은 논리적 데이터 모델에서 이용되는 형태이고, 분석 단계에서 많이 사용된다. 물리적 데이터 모델 설계를 위해서는 슈퍼-서브 타입 데이터 모델을 일정한 기준으로 변환해야 한다.

테이블 변환 전략

논리적 모델

아래의 관계를 JPA를 이용해 객체에 적용하여 DB와 매핑할 수 있다.

애너테이션

@Inheritance

  • 상속 매핑을 할 때, 부모 클래스에 어떤 전략을 사용할 것인지 설정하는 애너테이션
  • strategy = InheritanceType.SINGLE_TABLE : Rollup(Single-Type)
  • strategy = InheritanceType.TABLE_PER_CLASS : Rolldown(Plus-Type)
  • InheritanceType.JOINED : Identity(One-To-One Type)

@DiscriminatorColumn(name="DTYPE")

  • 부모 클래스가 매핑된 테이블에서 자식을 구분하기 위해 사용한다.
  • name 속성은 DTYPE이 기본값으로 지정된다.

@DiscriminatorValue("")

  • 자식 타입을 구분할 수 있도록 구분 컬럼을 설정해주는 애너테이션
  • 값을 지정하지 않으면, 기본 설정으로 엔티티 이름을 구분값으로 지정

Rollup(Single-Type)

  • 하나의 테이블로 통합
  • 슈퍼타입에 서브타입의 모든 컬럼을 통합하여 하나의 테이블로 생성
  • 자식 테이블을 구분하기 위해 DB에 구분 컬럼(DTYPE)을 사용한다.

장점

  • 단일 매핑이기 때문에 조인이 일어나지 않고 일반적으로 가장 빠르다.
  • 조회 쿼리가 단순해진다.

단점

  • 자식 컬럼을 다 가지고 있어야 하기 때문에 사용되지 않는 컬럼은 null 값을 허용해야 한다. 따라서 자식 컬럼은 모두 nullable해야 한다.
  • 자식이 많아지면, 테이블이 커져 성능이 저하될 가능성이 높다.

부모 클래스

@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DisCriminatorColumn(name = "DTYPE")
public class Item {
	
    @Id
    @GeneratedValue
    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 {
	...	
}

Rolldown(Plus-Type)

  • 여러개의 테이블로 분리
  • 슈퍼타입의 컬럼을 각각의 서브타입 컬럼에 추가하여 서브타입별로 테이블 생성
  • 자식 엔티티마다 하나의 테이블을 만든다.
  • 구분 컬럼을 사용하지 않는다.
  • 부모 클래스는 실제 테이블이 생성되지 않으므로 추상 클래스로 만든다.
  • @Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)을 사용할 때, @Id 생성 전략을 IDENTITYAUTO로 할 경우, 에러가 발생하므로 반드시 TABLE로 변경해야 한다.

장점

  • 서브타입을 구분해서 처리할 때 효과적이며, not null 제약 조건을 사용할 수 있다.

단점

  • 여러 자식 테이블을 혼합해서 조회할 때 상대적으로 속도가 느리다.
  • 자식 테이블을 통합해서 쿼리하기 어렵다.

추천하지 않는 전략!

부모 클래스

@Entity
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class Item {

   @Id
   @GeneratedValue(strategy = GenerationType.TABLE)
   private Long id;

   private String name;
   private int price;
}

자식 클래스

@Entity
public class Album extends Item {
	@Id
   	@GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    // 부모 클래스 컬럼
    private String Name;
    private int price;
    ...
}

@Entity
public class Movie extends Item {
	@Id
   	@GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    // 부모 클래스 컬럼
    private String Name;
    private int price;
    ...	
}

@Entity
public class Book extends Item {
	@Id
   	@GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    // 부모 클래스 컬럼
    private String Name;
    private int price;
    ...	
}

Identity(One-To-One Type)

  • Join 사용
  • 각각의 객체를 모두 테이블로 만들고, 조회할 때는 조인을 활용
  • 슈퍼타입과 서브타입 각각을 테이블로 생성
  • 자식 테이블들은 부모 테이블의 기본키를 받아 기본키 겸 외래키가 된다.
  • 자식 테이블을 구분하기 위해 DB에 구분 컬럼(DTYPE)을 사용한다.

장점

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

단점

  • 조회할 때, 조인을 사용하므로 쿼리가 복잡해지고 성능이 저하될 수 있다.
  • 데이터를 등록할 때 INSERT문이 두번 실행된다.

부모 클래스

@Entity
@Inheritance(strategy = InheritanceType.JOINED)
@DicriminatorColumn(name = "DTYPE")
public class Item {
    
    @Id
    @GeneratedValue
    @Column (name = "ITEM_ID")
    private Long id;
    
    private String name;
    private int price;
}

자식 클래스

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


@Entity
@DicriminatorValue("M")
public class Movie extends Item {
    
    private String director;
    private String actor;
    ...
}


@Entity
@DiscriminatorValue("B")
@PrimaryKeyJoinColumn(name = "BOOK_ID")
public class Book extends Item {
    
    private String author;
    private String isbn;
    
}

참고
[JPA] 상속관계 매핑(1): 상속 전략
데이터베이스 구조와 성능
[JPA] 상속관계 매핑 전략

profile
🧑‍💻백엔드 개발자, 조금씩 꾸준하게

0개의 댓글