데이터베이스에는 상속 관계가 없고 대신 슈퍼타입과 서브타입 관계가 존재한다.
그렇다면 객체 상속 관계를 데이터베이스의 테이블로 어떻게 매핑할까?
- 조인 전략 -> 각각의 테이블로 변환
- 단일 테이블 전략 -> 통합 테이블로 변환
- 테이블 전략 -> 서브타입 테이블로 변환
위 방법들을 자세히 알아보자. 다음은 상속 관계 예시이다.
조인 전략
엔티티 각각 모두 테이블로 만든다.
자식 테이블의 기본케는 부모 테이블의 기본키를 받아 기본키+외래키로 사용하는 전략이다.
특징
- 조회할 때 조인을 자주 사용한다.
- 테이블을 구분하기 위한 @DiscriminatorColumn을 지정해줘야 한다.
- 서브 타입의 기본키는 슈퍼 타입의 기본키를 그대로 가져와 사용한다.
- 변경하고 싶으면 @PrimaryKeyJoinColumn을 지정해주면 된다
@Entity
@Inheritance(strategy = InheritanceType.JOINED)
@DiscriminatorColumn(name = "DTYPE")
public abstract class Item {
@Id
@GeneratedValue
private Long id;
private String name;
private int price;
public void setName(String name) {
this.name = name;
}
...
}
@Entity
@DiscriminatorValue("A")
public class Album extends Item{
private String artist;
public String getArtist() {
return artist;
}
public void setArtist(String artist) {
this.artist = artist;
}
}
@Entity
@DiscriminatorValue("M")
@PrimaryKeyJoinColumn(name = "MOVIE_ID")
public class Movie extends Item {
private String actor;
}
이제 어떻게 저장되는지 더미 데이터 삽입을 통해 확인해보자.
Album album = new Album();
album.setName("tistory");
album.setArtist("me");
em.persist(album);
Movie movie = new Movie();
movie.setName("오펜하이머");
em.persist(movie);
DTYPE을 따로 만들지 않았지만 서브 타입을 구분하기 위해 자동으로 생성되었고
ITEM의 기본키가 그대로 ALBUM과 MOVIE에 동일하게 들어가 있음을 확인해볼 수 있다.
MOVIE에서는 @PrimaryKeyJoinColumn(name = "MOVIE_ID")을 통해 기본키 이름을 변경하였다.
장점
- 테이블이 정규화
- 외래 키 참조 무결성 제약조건 활용 가능
- 저장공간을 효율적으로 사용
단점
- 조회할 때 조인이 많이 사용 되어 성능 정하
- 조회 쿼리가 복잡
- 데이터 등록 시 INSERT QUERY 2번 실행
단일 테이블 전략
테이블을 하나만 사용
즉, 슈퍼 타입에 서브 타입 필드를 모두 모아넣어서 관리한다.
특징
- 구분 컬럼을 사용해 어떤 자식 데이터가 저장되었는지 구분
- 조인을 사용하지 않아 일반적으로 가장 빠르다
- 자식 엔티티가 매핑한 컬럼은 모두 null을 허용해야 한다.
위와 같은 코드에서 ITEM의 상속 전략만 다음과 같이 변경해주면 된다.
@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "DTYPE")
public abstract class Item {
...
}
같은 더미 데이터 코드를 실행했을 때 결과다.
ITEM이라는 슈퍼타입의 싱글 테이블로 모아서 저장했기 때문에 ALBUM과 MOVIE 테이블은 존재하지 않는다.
조회된 테이블을 확인해보면 알겠지만 사용하지 않는 컬럼이 존재하게 되고 모두 null로 처리되어있다.
장점
- 조인이 필요 없어 일반적으로 조회 성능이 빠름
- 조회 쿼리가 단순
단점
- 자식 엔티티가 매핑한 컬럼으 모두 null을 허용
- 단일 테이블에 모든 것을 저장하므로 테이블이 커질 수 있다.
- 따라서 상황에 따라 조회 성능이 오히려 늦어질 수 있다.
테이블 전략
구현 클래스(자식 클래스)마다 테이블을 만들어 관리하다.
일반적으로 추천하지 않는 전략이다.
마찬가지로 @Inheritance(strategy = InheritanceType.TABLE_PER_CLASS) 부분만 변경해주면 된다.
@Entity
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
@DiscriminatorColumn(name = "DTYPE")
public abstract class Item {
...
}
ALBUM과 MOVIE 테이블에도 ITEM에서 내려받은 필드들이 모두 존재함을 확인해볼 수 있다.
장점
- 서브 타입을 구분해서 처리할 때 효과적
- not null 제약 조건을 사용 가능
단점
- 여러 자식 테이블을 함꼐 조회 시 성능이 느리다. (SQL에 UNION을 사용해야 한다.)
- 자식 테이블을 통합해서 쿼리하기가 어렵다
특징
구분 컬럼을 사용하지 않는다
여기까지 상속 관계에 테이블을 어떻게 매핑하는 지 알아봤다.
그렇다면 상속 관계는 모두 부모 클래스와 자식 클래스를 모두 테이블에 매핑해야 할까??
그렇지 않다. @MappedSuperClass를 사용하면 자식 클래스에게 매핑 정보만 제공할 수 있다.
@MappedSuperClass
부모 클래스는 테이블과 매핑하지 않고 상속받는 자손 클래스에게 매핑 정보만 제공
위 예시를 그대로 ITEM은 테이블에 매핑하지 않고 ALBUM과 MOVIE에게 ITEM의 필드인 id, name, price 정보만 제공해주는 코드이다.
@MappedSuperclass
public abstract class Item {
@Id
@GeneratedValue
private Long id;
private String name;
private int price;
...
}
@Entity
public class Album extends Item{
private String artist;
...
}
@Entity
public class Movie extends Item {
private String actor;
...
}
더미데이터 삽입 결과를 보면 정보만 제공한 부모 클래스는 테이블이 존재하지 않고 서브 테이블에서 부모클래스의 필드를 확인해볼 수 있다.
'Study > JPA' 카테고리의 다른 글
[JPA] 프록시와 연관관계 (0) | 2023.09.08 |
---|---|
[객체지향쿼리] JPQL, Criteria, 네이티브 SQL, QueryDSL, JDBC Connection (0) | 2023.08.29 |
[JPA] 다대다 매핑 (0) | 2023.08.26 |
[JPA] 연관 관계의 주인과 양방향 연관 관계의 주의점 (0) | 2023.08.26 |
JPA, JPQL 이란? (1) | 2023.08.24 |