SQL(데이터베이스)와 객체지향언어가 갖는 큰 차이점 중 하나가 바로 상속의 유무 여부이다. 객체지향언어에서는 상속을 지원하지만, 데이터베이스에서는 상속을 지원하지 않는다.
스프링에서는 JPA를 이용하여 ORM 기술을 사용하는데, 클래스 간 상속 관계에 대해서는 어떻게 정의를 하는 지 알아보자.
ORM에서는 상속 관계 매핑에 대해 객체의 상속구조를 데이터베이스의 슈퍼타입 서브타입 관계를 매핑하는 것으로 대체한다.
물리 모델일 데이터 베이스 테이블로 구현하는 방법에는 크게 4가지 방법이 있다.
각각의 방법에 대해서 알아보자.
MappedSuperclass 어노테이션은 부모클래스를 데이터베이스의 테이블로 매핑하지 않고 자식클래스에게 매핑 정보를 제공하고 싶을 때만 사용한다.
@MappedSuperclass
public class Person {
@Id
private long personId;
private String name;
}
해당 클래스는 자체적으로 엔티티가 아니다. 따라서 Entity 어노테이션이 없고, 데이터베이스에 테이블의 형태로 존재하지 않는다.
@Entity
public class MyEmployee extends Person {
private String company;
}
데이터베이스에서는 하위 클래스인 MyEmployee에 Person이 가지고 있던 2개의 Field를 추가하여 3개의 열을 갖는 테이블을 생성한다.
Single Table 전략은 각 클래스 계층에 대해 하나의 테이블을 만든다. 명시적으로 지정하지 않는 경우 JPA 에서는 Default로 해당 전략을 선택한다.
discriminatorType 으로 식별자를 integer로 할 것인지 혹은 다른 타입으로 지정할 것인지 선택할 수 있다. name을 지정하게 되면 해당 식별자의 이름이 지정되어 테이블로 들어간다.
Book에서는 color를 갖지 않고 있는데 테이블에는 모든 필드가 포함되어 있는 모습이다. 만약 Book 데이터가 저장되게 되면 해당 열에는 null 값이 저장된다.
@Entity(name="products")
@Inheritance(strategy = InheritanceType.SINGLE_TABLE) // 상속 관계 매핑
@DiscriminatorColumn(name="product_type",
discriminatorType = DiscriminatorType.INTEGER)
public class MyProduct {
@Id
private long productId;
private String name;
}
@Entity
@DiscriminatorValue("1")
public class Book extends MyProduct {
private String author;
}
@Entity
@DiscriminatorValue("2")
public class Pen extends MyProduct {
private String color;
}
조인 전략은 계층 구조를 띄는 각 클래스가 해당 테이블로 매핑된다. 이 때, 테이블마다 반복되는 열이 하나가 있는 데, 이는 조인할 때 반드시 필요한 식별자이다.
@Entity
@Inheritance(strategy = InheritanceType.JOINED)
public class Animal {
@Id
private long animalId;
private String species;
}
@Entity
public class Pet extends Animal {
private String name;
}
두 테이블 모두 animalId 식별자를 갖는다. Pet은 기본 키에 상위 엔티티의 기본키에 대한 외래 키 제약 조건을 갖는다.
만약 해당열을 사용자 지정하려면 @PrimaryKeyJoinColumn
어노테이션을 추가하여 사용할 수 있다.
@Entity
@PrimaryKeyJoinColumn(name = "petId")
public class Pet extends Animal {
private String name;
}
Table per Class 는 각각의 테이블을 모두 생성하는 전략이다. 결과적으로는 MappedSuperclass를 사용한 것과 유사하다. 그러나 Table per Class 전략에서는 상위 클래스에 대한 엔티티를 정의하기 때문에 ( 클래스 자체로 엔티티임 ) 상위 클래스를 연결 및 쿼리 사용이 가능하다.
@Entity
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public class Vehicle {
@Id
private long vehicleId;
private String manufacturer;
}
자식 클래스는 위의 코드와 거의 유사하다.