김영한님의 JPA 강의를 보고 정리한 내용입니다.
객체지향에는 클래스끼리 상속이 가능하다. 하지만 관계형 데이터베이스는 상속관계를 지원하지 않는다.
그 대신 상속 관계와 비슷한 논리 모델링 기법인 슈퍼타입, 서브타입 관계를 통해 객체의 상속 관계를 매핑할 수 있다.
슈퍼타입, 서브타입을 실제 테이블로 구현하는 방법엔 3가지 방법이 있다.
1. 조인 전략
2. 단일 테이블 전략
3. 구현 클래스마다 테이블 전략
위의 3가지 방법 모두 JPA의 어노테이션인 @Inheritance(strategy = InheritanceType.XXX)를 통해 구현할 수 있다. 이때 기본 값은 단일 테이블 전략이다.
JOINEDSINGLE_TABLETABLE_PER_CLASS구분자 컬럼(DTYPE)이 필요한 경우 부모 클래스에 @DiscriminatorColumn으로 DTYPE을 설정할 수 있고 name 속성으로 DTYPE의 컬럼명을 바꿀 수 있다. 기본 값은 DTYPE이다.
자식 클래스엔 @DiscriminatorValue 어노테이션으로 DTYPE이 저장되는 값을 바꿀 수 있다. 기본 값은 클래스명이다.
어떤 전략을 사용하든지 크게 바뀌는 코드는 없다. 단지 어노테이션만 바꿔주면 JPA가 알아서 해당하는 전략에 맞게 테이블을 매핑해준다.
조인 전략은 슈퍼타입, 서브타입 논리 모델을 각각 테이블로 옮긴 방식이다. 테이블이 구분되어 있기 때문에 데이터를 조회할 때 조인이 필요해서 조인 전략이라고 부른다.
조인 테이블 전략은 구분자 컬럼이 반드시 필요하진 않지만 구분자 컬럼을 설정하는 것이 부모 테이블 조회만 해도 어떤 데이터 타입을 가지고 있는지 확인할 수 있어서 설정하는 것이 좋다.
장점
단점
insert 쿼리 2번 호출단점으로 성능이 떨어질 수 있다고 했지만 데이터가 엄청 많은게 아닌 이상 성능에 대한 이슈가 있지는 않다.
조인 전략은 상속 관계를 매핑하는 전략 중 데이터베이스의 관점에서 가장 정규화된 깔끔하고 정석적인 선택이다.
단일 테이블 전략은 이름 그대로 하나의 테이블에 모든 데이터를 몰아넣는 전략이다.
조인 전략에서는 구분자 컬럼이 필수는 아니었지만 단일 테이블 전략은 하나의 테이블에 모든 데이터가 들어가기 때문에 구분자 컬럼이 필수다. (개발자가 지정해주지 않아도 JPA가 자동으로 만들어 준다)
장점
단점
null 값을 허용해야 한다.조인 전략과 비교해서 성능이 더 느려지려면 임계점을 넘어야 하는데 보통 넘을 일은 없다.
단일 테이블 전략은 조인 전략과는 다르게 테이블 하나만 사용하기 때문에 가볍고, 덜 복잡한 프로젝트와 잘 어울리는 전략이다. 프로젝트가 덜 복잡하고, 바뀌지 않을 것 같다면 추천한다.
조인 전략과 좀 비슷하지만 부모 객체를 없애버리고, 부모 객체에 있는 속성들을 자식 객체에 내리는 전략이다.
이때 부모 객체는 테이블이 만들어지면 해당 객체만 독단적으로 쓰는 일도 있다는 것이기 때문에 아예 만들어 지지 않게 추상 클래스로 정의한다.
장점
not null 제약 조건 사용 가능단점
UNION 쿼리를 사용함구현 클래스마다 테이블 전략은 변경이라는 관점에서 볼 때 매우 안좋다. 시스템에 새로운 것이 추가 될 때마다 굉장히 많은 걸 뜯어내야 한다.
구현 클래스마다 테이블 전략은 DBA와 개발자 모두 선호하지 않는 전략이다. 선택을 하게 되면 먼 미래에 언젠가 큰 후회를 하게 된다. 따라서 사용하지 않도록 하자.
공통 매핑 정보가 필요할 때 사용한다. 주로 등록일, 수정일, 등록자, 수정자 같은 전체 엔티티에서 공통으로 적용하는 정보를 모을 때 사용한다.
아래 예시를 보면 공통으로 들어가는 정보인 createdBy, createdAt, lastModifiedBy, lastModifiedAt를 BaseEntity 클래스에 모아서 사용하는 것을 볼 수 있다.
@Getter
@MappedSuperClass
public abstract class BaseEntity {
private String createdBy;
private LocalDateTime createdAt;
private String lastModifiedBy;
private LocalDateTime lastModifiedAt;
}
@Entity
public class Post extends BaseEntity {
...
}
@MappedSuperClass는 상속관계 매핑이 아니니 헷갈리지 말자.
@MappedSuperClass는 엔티티가 아니라 그저 속성만 내려주는 클래스이다. 그래서 해당 어노테이션이 적용되어 있는 클래스는 테이블과 매핑이 되지 않고, 부모 클래스를 상속받는 자식 클래스에 매핑 정보만 제공한다.
같은 맥락으로 해당 어노테이션이 적용된 클래스의 타입으로는 조회와 검색이 불가능하다. 이것도 상속관계 매핑과 다른 점이라고 볼 수 있다. (상속관계 매핑은 가능)
해당 클래스는 직접 사용할 일이 없으므로 추상 클래스로 만드는 것을 권장한다.
@Entity클래스는 같은@Entity클래스나@MappedSuperClass로 지정한 클래스만 상속 가능하다.