본문 바로가기

JPA

[JPA] 상속관계 전략 (Joined, Single Table, Inheritance)

 관계형 데이터베이스에는 객체지향 언어에서 다루는 상속이라는 개념이 없다. 위 Item 을 상속받는 3개의 클래스 Album, Movie, Book 

을 상속관계가 없는 관계형 데이터 베이스에 적용 하려면 객체의 상속개념과 가장 유사한 슈퍼타입, 서브타입 관계라는 모델링 기법을 사용한다.

 

객체의 상속관계를 슈퍼타입, 서브타입이라는 테이블로 구현할때는 3가지 방법을 선택할 수 있다. 

1) Joined Table - 조인전략 

2) Single Table - 단일 테이블 전략 

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

4) MapptedSuperclass - 위 세가지와는 성격이 조금 다르다.

테이블과는 관계가 없고 단순히 엔티티가 공통으로 사용하는 매핑 정보를 모아주는 역할을 할 뿐이다. 부모 클래스(Item)이 아닌 상속받는 엔티티들만 Entity 와 매핑하게 된다.

 

1. 조인전략 (Joined Strategy) 

 

 

엔티티 각각을 모두 테이블로 만들고 자식 테이블이 부모 테이블의 기본키를 받아서 기본키 + 외래키 (식별자) 로 사용하는 전략이다. 

부모 테이블에 식별자(Discriminator) 와 DTYPE을 두고 자식테이블을 따로 생성하기 때문에 조회할때 조인을 자주 사용할 수 있다.

 

객체는 타입을 구분할 수 있지만 테이블은 불가능 하기 때문에 타입을 구분하는 컬럼 추가가 필수 이다. 

 

1.1 관련 애노테이션 

 @Inheritance 

 @DiscriminatorColumn(name="DTYPE")

 @DiscriminatorValue("M") 

 

1.2 코드 예시

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

@Entity 
@DiscriminatorValue("B")
@PrimaryKeyJoinColumn(name="BOOK_ID") // ID 재정의
public class BOOK extends Item {
	private String author;  
	private String isbn; 
}

1.3 장점 

 - 테이블이 정규화 된다. 

 - 외래키 참조 무결성 제약조건을 활용 할 수 있다. 

 - 저장 공간을 효율적으로 사용할 수 있다. 

1.4 단점 

 - 조회할 때 조인이 많이 사용되므로 성능이 저하될 수 있다. 

 - 조회 쿼리가 복잡하다. 

 - 데이터를 등록할 INSERT SQL 을 두번 실행한다. 

 

 

2. Single Table ( 단일 테이블 전략 ) 

 - 테이블을 하나만 사용한다. 하나의 테이블에 구분 컬럼을 두고 어떤 자식 데이터가 저장 되었는지 확인한다.

2.1 관련 애노테이션

 - @Inheritance(strategy = InheritanceType.SINGLE_TABLE)

 - @DiscriminatorColumn 

 - @DiscriminaterValue 

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

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


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


@Entity
@DiscriminaterValue("B") 
public class Book extends Item { ... }

2.3 장점 

 - 조인이 필요 없으므로 조회성능이 빠르며 쿼리가 단순하다. 

2.4 단점 

 - 자식 엔티티가 매핑한 컬럼은 모두 Null 을 허용해야 한다. 

 - 단일 테이블에 모든것을 저장하므로 테이블이 커질 수 있다. 

 - 그러므로 상황에 따라서는 조회 성능이 더 느려 질 수 있다. 

 

3. Table per Class 구현 클래스마다 테이블 전략 ( 일반적으로 추천하지 않는다. 1,2 추천) 

 

 자식 엔티티마다 테이블을 만든다. 자식 테이블 각각에 필요한 컬럼이 모두 있기 때문에 구분 컬럼을 사용하지 않는다. 

@Entity 
@Inheritance(stragegy = 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 Albumn extends Item { ... } 

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

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

 3.3 장점 

 - 서브 타입을 구분해서 처리할때 효과적이다. 

 - Not Null 제약조건 사용이 가능하다. 

3.4 단점 

 - 여러 자식 테이블을 함께 조회할때 성능이 느리다. (SQL 에 UNION 을 사용해야 한다. 

  - 자식 테이블을 통합해서 쿼리하기 어렵다.

 

4. @MappedSuperclass

부모 클래스는 테이블과 매핑하지 않고 부모 클래스를 상속 받는 자식 클래스에게 매핑 정보만 제공하고 싶으면 @MappedSuperClass를 사용한다. 

@Mappedsuperclass 
public abstract class baseEntity {
	@Id @GeneratedValue
    private Long id ;
    private String name; 
    ... 
}

@Entity
@AttributeOberride(name="id",column =@Column(name="MEMBER_ID"))
public class Member extends BaseEntity {
 // ID 상속 
 // NAME 상속
 private String email; 
 ...
}

@Entity 
public lass Seller extends BaseEntity {
	// ID 상속 
    // NAME 상속
	private String shopName;
}

4.2 특징

- 테이블과 매핑되지 않고 자식 클래스에 엔티티의 매핑 정보를 상속하기 위해 사용한다. 

- @MappedSuperclass로 지정한 클래스는 엔티티가 아니므로 em.find() 나 JPQA에서 사용할 수 없다. 

- 이 클래스를 직접 생성해서 사용할 일은 거의 없으므로 추상 클래스를 만드는 것을 권장한다. 

반응형