상속 관계 매핑
- 객체는 상속 개념이 존재하지만, 관계형 데이터베이스는 상속 관계라는 개념이 존재하지 않음
- 대신 슈퍼타입 서브타입이라는 모델링 기법이 존재
- 객체의 상속 구조와 DB의 슈퍼타입 서브타입 관계를 매핑하는 것이 상속 관계 매핑!
★ 상속 관계 매핑
- 슈퍼타입 서브타입 논리 모델을 실제 물리 모델인 테이블로 구현하는 것
- 각각의 테이블로 변환 : 각각을 모두 테이블로 만들고 조회할 때 조인을 사용하는 조인 전략
- 통합 테이블로 변환 : 테이블을 하나만 사용해서 통합하는 단일 테이블 전략
- 서브타입 테이블로 변환 : 서브 타입마다 하나의 테이블을 만드는 구현 클래스마다 테이블 전략
★ 조인 전략
- 엔티티 각각을 모두 테이블로 만들고 자식 테이블이 부모 테이블의 기본 키를 받아서 기본 키 + 외래 키로 사용하는 전략
- 상속 관계를 매핑할 때는 부모 클래스를 추상 클래스로 만들어 주고 엔티티 클래스를 똑같이 각각 생성해준다.
- 여기서 주목할 부분은, 자식 클래스는 PK 값을 별도로 설정하지 않아도 부모 클래스의 PK를 그대로 가져간다.
⭐︎ 추가 어노테이션
- @Inheritance(strategy = Inheritance.JOINED) : 상속 매핑은 부모 클래스에 @Inheritance 어노테이션을 사용해서 어떤 전략을 사용할지를 지정해줘야 한다. 현재 사용한 전략은 조인 전략으로 .JOINED 속성을 선택한다.
- @DiscriminatorColumn(name = "...") : 구분 컬럼은 꼭 지정하지 않더라도 상속 관계 매핑은 정상적으로 이루어질 수 있으나 슈퍼타입 입장에서는 현재 들어온 값이 어떤 테이블의 값인지 모르므로 구분 컬럼을 항상 지정해주는 것이 좋다. 기본값은 DTYPE 이며 별도로 컬럼명을 설정할 수 있다.
- @DiscriminatorValue("...") : 구분 컬럼에 들어갈 값을 지정할 수 있다. 이 어노테이션을 달지 않으면 기본값으로 엔티티 명과 같은 이름을 저장하며, 다른 이름을 자체적으로 설정하고 싶을 때는 어노테이션을 달아서 "M" 이런 식으로 적용해주면 된다.
참고로 자식 테이블의 기본 키 컬럼명을 변경하고 싶을 땐 부모 클래스에 @PrimaryKeyJoinColumn을 사용하여 (name = "...")을 사용해주면 된다.
⭐︎ 장점
- 테이블 정규화가 이루어지며, 외래 키 참조 무결성 제약조건을 활용할 수 있다.
⭐︎ 단점
- 조회시 조인을 많이 사용할 수 있으며 단일 테이블보다는 쿼리가 복잡하다.
★ 단일 테이블 전략
- 테이블 하나만 사용하면 구분 컬럼으로 어떤 자식 데이터가 저장되었는지 구분하는 전략
- 상속 전략을 SINGLE_TABLE로만 변경해주면 된다.
- 싱글 테이블은 DTYPE을 필수!
⭐︎ 장점
- 조인이 필요하지 않으며 조회 성능이 빠르고 조회 쿼리가 단순하다.
⭐︎ 단점
- 자식 엔티티가 매핑한 컬럼은 모두 Null을 허용해야 한다.
★ 구현 클래스마다 테이블 전략
- 자식 엔티티마다 테이블을 만들고, 자식 테이블 각각에 필요한 컬럼이 모두 있는 전략
- ITEM 테이블은 존재하지 않으며 자식 테이블만 생성한다.
- 구분 컬럼이 필요하지 않으므로 어노테이션을 달아놔도 적용이 되지는 않는다.
⭐︎ 장점
- 서브 타입을 명확하게 구분해서 처리할 수 있으며 not null 제약 조건을 사용할 수 있다.
⭐︎ 단점
- 여러 자식 테이블을 함께 조회할 때 성능이 느리며 UNION을 이용해서 조회해와야 하는 경우가 있을 수도 있으므로 매우 복잡하다.
- 통합해서 관리하는 테이블이 없으므로 뭔가를 조회하려고 할 때 모든 자식 테이블을 뒤져서 값을 찾아야 한다. 예를 들어 총 가격을 알고자 한다면 Movie, Book, Album 테이블을 전부 조회해서 가격을 가지고 온 후에 그 값들을 더해야 총 가격을 알 수 있다.
※ 데이터베이스 설계자도 ORM 전문가도 둘 다 이 방법은 추천하지 않는다고 한다!! ※
-> 조인 전략을 기본 베이스로 가지고 가되, 소규모 프로젝트거나 상황에 따라 단일 테이블 전략을 가져가는 것이 좋을 듯하다.