관계형 데이터베이스에서 상속 관계를 구현하는 전략은 주로 다음과 같이 세 가지 방법이 사용된다.
1. 조인 전략 (Join Strategy):
조인 전략은 각각의 서브타입에 해당하는 테이블을 별도로 생성하고, 슈퍼타입과 서브타입 간의 관계를 조인을 통해 맺는 방식이다. 이 전략에서는 슈퍼타입과 서브타입의 속성을 각각의 테이블에 저장하며, 서브타입 객체를 조회할 때 조인을 사용하여 필요한 정보를 가져온다. 이 방식은 데이터베이스 정규화를 지원하며, 서브타입의 추가나 삭제가 용이하다. 하지만 매번 조인이 필요하기 때문에 성능에 영향을 줄 수 있다. 저장공간은 효율화되지만 쿼리가 복잡하다.
@Entity
@Inheritance(strategy=InheritanceType.JOINED)
@DiscrimiatorColumn(name="DTYPE")
public abstract class Item {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name="ITEM_ID")
private Long id;
private String name;
private int price;
private int stockQuantity;
}
(@Inheritance를 설정해줘야 한다. JPA는 기본적으로 단일 테이블 전략을 사용하기 때문)
@Entity
public class Album extends Item {
private String artist;
}
어떤 클래스인지는 DTYPE으로 구분된다. (@DiscriminatorColumn을 적용시켜야 DTYPE이 보인다)
2. 단일 테이블 전략 (Single Table Strategy)
단일 테이블 전략은 슈퍼타입과 모든 서브타입의 속성을 하나의 테이블에 통합하여 저장하는 방식이다. 슈퍼타입과 서브타입의 구분을 위한 컬럼을 추가하고, 필요한 속성만을 사용하며 나머지 속성은 NULL로 처리한다. 이 방식은 조회 시 조인이 필요하지 않기 때문에 성능이 우수하며, 단순한 구조를 가지고 있다. 하지만 테이블의 크기가 커질 수 있고, 서브타입이 많아질 경우 불필요한 NULL 값이 발생할 수 있다.
@Entity
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
public abstract class Item {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name="ITEM_ID")
private Long id;
private String name;
private int price;
private int stockQuantity;
}
@Entity
public class Album extends Item {
private String artist;
}
이 경우에 DB에는 ITEM이라는 한 테이블 내에 Album의 속성까지 함께 들어가고, 어떤 클래스인지는 DTYPE으로 구분된다. 단일 테이블 전략은 DTYPE이 필수이다.
3. 구현 클래스마다 테이블 전략 (Class Table Inheritance Strategy):
구현 클래스마다 테이블 전략은 슈퍼타입과 각 서브타입에 해당하는 테이블을 개별적으로 생성하는 방식이다. 슈퍼타입의 속성을 저장하는 테이블과 각 서브타입의 속성을 저장하는 테이블이 별도로 존재하며, 서브타입 객체를 조회할 때는 조인 없이 해당 서브타입의 테이블만 조회한다. 이 방식은 서브타입의 추가나 삭제가 유연하며, 슈퍼타입과 서브타입의 속성을 명확하게 구분할 수 있다. 그러나 슈퍼타입과 서브타입을 조회할 때 조인이 필요하고, 테이블 수가 증가하여 데이터베이스 스키마가 복잡해질 수 있다.
@Entity
@Inheritance(strategy=InheritanceType.TABLE_PER_CLASS)
public abstract class Item {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name="ITEM_ID")
private Long id;
private String name;
private int price;
private int stockQuantity;
}
@Entity
public class Album extends Item {
private String artist;
}
다형성으로 Item item = em.find(albumid) 를 하게되면, 모든 테이블을 찾아봐야해서 성능이 나빠진다.
서브 타입을 명확하게 구분해서 처리할 때 효과적일 수 있다(not null 제약조건도 사용가능)
그러나..
이 전략은 데이터베이스 설계자와 ORM전문가 둘 다 좋아하지 않는 방법이라고 한다.
(자식 테이블을 통합해서 쿼리하기 어렵고 여러 자식 테이블을 함께 조인할 때 성능이 느리다.)
기본적으로는 조인 전략을 추천. 하지만 단순한 프로젝트의 경우 단일테이블이 편리할 수 있다.
댓글