RDBMS에서는 자바의 객체지향 언어의 특징 중 하나인 상속을 다루는 개념이 존재하지 않습니다.
상속이라는 개념이 없는 대신 슈퍼타입 서브타입 관계 모델링 기법을 사용하여 상속 개념을 유사하게 표현을 하게 됩니다.
위와 같이 모델링을 한 테이블을 실제 물리적인 테이블로 구현할 때는 조인 전략, 단일 테이블, 구현 클래스마다 테이블 세 가지 전략 중에 하나를 선택하여 구현하게 됩니다.

조인 전략은 각 엔티티(이동수단, 승용차, 자전거, 배)를 모두 테이블로 만들고 서브타입 테이블들이 슈퍼타입 테이블의 기본 키를 외래 키로 받아오는 전략입니다. 각 테이블을 조회하는 경우 JOIN을 이용해서 조회합니다.
조인 전략의 주의점은 테이블엔 타입 개념이 없기 때문에 슈퍼타입에 vehicle_type과 같은 구분 컬럼을 추가해서 사용해야합니다.
조인 전략을 사용할 때의 슈퍼타입 엔티티는 다음과 같습니다.
@Entity
@Table(name = "vehicle_table")
@Inheritance(strategy = InheritanceType.JOINED)
@DiscriminatorColumn(name = "vehicle_type", discriminatorType = DiscriminatorType.STRING)
public abstract class Vehicle {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "v_id", nullable = false)
private Long id;
//생성자 및 gettter
}
서브타입 엔티티는 다음처럼 사용합니다.
@Entity
@Table(name = "car_table")
@DiscriminatorValue("CAR")
//@PrimaryKeyJoinColumn(name = "c_id")
public class Car extends Vehicle {
//Car 필드
}
상속받은 서브타입 엔티티는 부모 테이블의 기본 키 컬럼을 그대로 사용합니다. 만약 기본 키 컬럼을 변경하고 싶다면 @PrimaryKeyJoinColumn을 사용합니다.
위 예시에서 사용된 매핑 정보는 다음과 같습니다.
조인 전략을 사용하기 때문에 strategy = InheritanceType.JOINED 사용구분 컬럼을 지정조인 전략의 장단점은 다음과 같습니다.
장점
단점
JOIN 사용에 의한 성능 저하와 쿼리 복잡성 증가INSERT 2회 실행. (ex) CAR 저장 시 CAR 테이블에 한 번, VEHICLE 테이블에 또 한 번)단일 테이블 전략은 슈퍼타입에 해당하는 테이블을 하나만 생성하고 구분 컬럼을 통해 서브타입에 해당하는 데이터를 구분하는 방식입니다.
@Entity
@Table(name = "vehicle_table")
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "vehicle_type", discriminatorType = DiscriminatorType.STRING)
public abstract class Vehicle {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "v_id", nullable = false)
private Long id;
//생성자 및 gettter
}
전략이 바뀌었기 때문에 @Inheritance(strategy = InheritanceType.SINGLE_TABLE)를 반드시 변경해주어야 합니다. 그리고 단일 테이블에 모든 것을 저장하기 때문에 구분 컬럼을 반드시 사용해야합니다.
그리고 서브타입 엔티티를 정의할 때 서브타입 엔티티의 매핑 컬럼은 반드시 null을 허용해야합니다. 하나의 테이블에 모든 정보를 저장하기 때문입니다.
예를들어 승용차에는 악셀러레이터가 있지만 배나 자전거에는 존재하지 않습니다. 이런 경우를 대비하기 위해 서브타입 엔티티들의 매핑 컬럼은 반드시 null을 허용해야합니다.
@Entity
@Table(name = "car_table")
@DiscriminatorValue("CAR")
//@PrimaryKeyJoinColumn(name = "c_id")
public class Car extends Vehicle {
//Car 필드
//반드시 서브타입 엔티티 매핑 필드는 nullable = true
}
단일 테이블 전략의 장단점은 다음과 같습니다.
장점
JOIN이 사용되지 않아 조회 연산이 빠르고 쿼리가 단순하다단점
구현 클래스마다 테이블 전략은 서브타입 엔티티마다 테이블을 만드는 방식입니다.
@Entity
@Table(name = "vehicle_table")
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class Vehicle {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "v_id", nullable = false)
private Long id;
//생성자 및 gettter
}
이 방식은 모든 서브타입 마다 각자의 테이블을 생성하는 방식입니다. 그렇기 때문에 그다지 권장되는 전략은 아니어서 이전에 등장한 조인 전략 or 단일 테이블 전략을 사용합니다.
조인 전략에서는 PK와 공통 필드를 슈퍼타입으로 부터 받아오고 서브타입 엔티티에는 서브 속성들만 저장했었습니다.
구현 클래스마다 테이블 전략은 모든 서브타입 엔티티에 공통 속성을 계속해서 써주는 비효율적인 방식을 이용합니다.
위에서 소개한 세 가지 전략은 슈퍼타입 클래스도 서브타입 클래스도 모두 데이터베이스 테이블과 매핑했습니다.
만약 부모타입 클래스는 매핑하지 않고 상속받는 서브타입 클래스에 매핑 정보만 전달하고 싶으면 @MappedSuperclass를 사용합니다.
@Entity는@Entity또는@MappedSuperclass가 붙은 클래스만 상속받을 수 있습니다.
다음 예시를 통해 @MappedSuperclass에 대해 알아보겠습니다.
@MappedSuperclass
public class MemberBase {
private long id;
private String name;
}
@Entity
public class Student extends MemberBase {
private String major;
}
@Entity
public class Employee extends MemberBase {
private String department;
}
Student와 Employee는 직접적인 연관관계가 없습니다. 하지만 id, name이라는 공통된 속성을 가지고 있습니다. 따라서 이들에 대한 공통 속성을 정의한 슈퍼 클래스 MemberBase를 선언하고 공통 속성을 사용하는 클래스들에 상속을 해주었습니다.
이런 경우에 슈퍼 클래스는 테이블과 매핑할 필요없이 매핑 정보(id, name 속성)만 제공하면 됩니다. 이렇게 슈퍼 클래스의 공통 속성 매핑 정보만 제공하고 싶은 경우 슈퍼 클래스에 @MappedSuperclass를 정의하면 됩니다.
위와같은 공통 속성 클래스가 실제로 인스턴스화되어 사용될 일은 없기 때문에
추상 클래스로 정의하는 것이 권장됩니다.
만약 상속받은 서브 클래스에서 속성을 재정의하려면 @AttributeOverride, 연관관계 재정의 '@AssociationOverride'를 사용합니다.
@Entity
@AttributeOverride(name = '부모 컬럼명', column = @Column(name = '변경할 컬럼명'))
public class Student extends MemberBase {
private String major;
}
//or 여러 속성 변경 시
@Entity
@AttributeOverrides({
@AttributeOverride(name ="", column = @Column()),
@AttributeOverride(name ="", column = @Column()),
})
public class Student extends MemberBase {
private String major;
}
다음은 @AssociationOverride 예제 코드입니다.
@Embeddable
public class Address {
@ManyToOne
@JoinColumn(name = "c_id")
private City city;
}
@Entity
@AssociationOverride(
name = "officeAddress.city", // 임베디드 필드
joinColumns = @JoinColumn(name = "oc_id")
)
public class Office {
@Id
@GeneratedValue
private Long id;
@Embedded
private Address officeAddress;
}
//마찬가지로 여러 연관관계 변경시 @AssociationOverrides() 사용