Table per class hierarchy

특징

  • 전체 상속 구조 하나를 단일 클래스로 맵핑한다.
  • 하위 클래스의 객체들은 타입 구분자 Type Discriminator 컬럼의 값으로 구분한다.
  • 구분자를 추가할 수 없는 상황에서는 formula를 사용한다. SQL의 CASE/WHERN

장점

  • 다형적이거나 그렇지 않은 쿼리를 수행하는 성능이 좋다. <- 조인이나 Union을 하지 않으니까.
  • 구현하기도 간편하다. <- 스키마가 간단하니까.

단점

  • 하위 클래스의 속성들은 반드시 nullable이어야 한다.
  • 정규화가 되지 않는다. 키가 아닌 값에 종속성에 생기므로, 3차 정규화를 깨트렸다. DBA가 싫어할 것이다.

맵핑하기

  • 상위 클래스(discriminator 사용)
@Entity
@Table(name="BILLING_DETAIL")
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name="BILLING_DETAILS_TYPE", discriminatorType = DiscriminatorType.STRING)
public abstract class BillingDetails {

@Id
@GeneratedValue(strategy=GenerationType.SEQUENCE)
@Column(name = "BILLING_DETAILS_ID")
private Long id = null;

@Column(name = "OWNER", nullable = false)
private String owner;
  • 상위 클래스(formula 사용)
@Entity
@Table(name="BILLING_DETAIL")
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorFormula("case when CC_NUMBER is not null then 'CC' else 'BA' end")
public abstract class BillingDetails {
  • 하위 클래스
@Entity
@DiscriminatorValue("CC")
public class CreditCard extends BillingDetails {

@Column(name = "CC_NUMBER")
private String number;

쿼리 분석

  • 상위 타입으로 가져올 때는 조건 없이 모든 컬럼 가져온다.
List<BillingDetails> bdList = session.createQuery("from BillingDetails").list();

select billingdet0_.BILLING_DETAILS_ID as BILLING2_0_, billingdet0_.OWNER as OWNER0_, billingdet0_.ACCOUNT as ACCOUNT0_, billingdet0_.CC_NUMBER as CC5_0_, billingdet0_.BILLING_DETAILS_TYPE as BILLING1_0_
from BILLING_DETAIL billingdet0_
  • 하위 타입으로 가져올 때는 Discriminator 사용해서 조건을 준다.
List<CreditCard> bdList = session.createQuery("from CreditCard").list();

select creditcard0_.BILLING_DETAILS_ID as BILLING2_0_, creditcard0_.OWNER as OWNER0_, creditcard0_.CC_NUMBER as CC5_0_
from BILLING_DETAIL creditcard0_
where creditcard0_.BILLING_DETAILS_TYPE='CC'