관계형 데이터베이스에는 객체지향 언어에서 다루는 상속이라는 개념이 없다. 위 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에서 사용할 수 없다.
- 이 클래스를 직접 생성해서 사용할 일은 거의 없으므로 추상 클래스를 만드는 것을 권장한다.