01. 상속관계 매핑
상속관계 매핑 개요
객체에는 상속관계가 있지만, 관계형 데이터베이스에는 상속관계가 없다.
하지만 관계형 데이터베이스에서, 슈퍼타입-서브타입 관계라는 모델링 기법이 객체 상속과 유사하다.
상속관계 매핑
상속관계 매핑이란 슈퍼타입-서브타입 논리 모델을 실제 물리 모델로 구현하는 방법이다. 아래 그림이 논리 모델이다.
아래 그림은 객체의 상속 관계를 보여준다.
상속관계 매핑 방법
상속관계를 매핑하는 방법으로는 3가지가 있다. 조인 전략은 각각을 테이블로 변환하는 것이다. 단일 테이블 전략은 하나의 통합 테이블로 변환하는 것이다. 구현 클래스마다 테이블 전략은 서브타입 테이블로 변환하는 것이다.
02. 상속관계 주요 어노테이션
@Inheritance
상속관계 매핑 시 사용하며 슈퍼타입에 작성하면 된다. 속성으로 strategy가 있다.
InheritanceType.JOINED
은 조인 전략, InheritanceType.SINGLE_TABLE
은 단일 테이블 전략, InheritanceType.TABLE_PER_CLASS
은 구현 클래스마다 테이블 전략이다.
@DiscriminatorColumn
슈퍼타입에서 서브타입을 구분하기 위한 컬럼을 추가한다는 것이다. 속성으로 name이 있고, 이 속성을 작성하면 DB에 해당 name의 컬럼이 나타난다. 아래 예시 코드를 참고하면 이해가 쉬울 것이다.
@DiscriminatorValue
서브타입의 명칭을 따로 지정할 때 사용하는 어노테이션이다. 단순히 @DiscriminatorValue("XXX")
라고 작성하면 된다. 아래 예시 코드를 참고하면 이해가 쉬울 것이다.
03. 조인 전략
각각을 테이블로 변환하는 전략이다.
슈퍼타입에서는 @Inheritance(strategy = InheritanceType.JOINED)
로 조인 전략을 명시한다. 서브타입에서는 extends Item
으로 슈퍼타입 클래스를 확장함을 명시한다. 조인 전략을 표현하면 다음과 같다.
다음은 조인 전략을 코드로 구현한 것이다.
// 슈퍼타입
@Entity
@Inheritance(strategy = InheritanceType.JOINED)
public 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 Book extends Item{
private String author;
private String isbn;
}
@Entity
public class Movie extends Item{
private String director;
private String actor;
}
JpaMain 클래스의 try 부분에 tx.commit();
만 작성하고 실행하면 다음과 같은 결과가 나온다.
위 클래스들에 생성자, getter/setter를 모두 생성한 후에 테이터를 다음과 같이 넣어보면,
Movie movie = new Movie();
movie.setDirector("aaaa");
movie.setActor("bbbb");
movie.setName("바람과함께사라지다");
movie.setPrice(10000);
em.persist(movie);
다음과 같은 결과가 나온다.
조회를 할 때는 join을 해서 가져오는 것을 볼 수 있다. em.flush()
와 em.clear()
로 영속성 컨텍스트에 있는 쿼리문을 DB에 보내면 1차 캐시에 아무것도 남지 않는다.
Movie movie = new Movie();
movie.setDirector("aaaa");
movie.setActor("bbbb");
movie.setName("바람과함께사라지다");
movie.setPrice(10000);
em.persist(movie);
em.flush();
em.clear();
Movie findMovie = em.find(Movie.class, movie.getId());
System.out.println("findMovie = " + findMovie);
이제까지 코드에서는 DTYPE
에 관한 내용이 생략되어 있는데, Item 클래스(슈퍼 타입)에 @DiscriminatorColumn
을 작성하면 된다. DTYPE을 보면 운영할 때, 어떤 entity와 join이 되었는지 쉽게 알 수 있다.
DTYPE이 잘 작성되어 있는 것을 볼 수 있다. DTYPE의 기본값으로 엔티티명이 들어가게 된다.
@DiscriminatorColumn
의 name 속성으로 컬럼명을 변경할 수 있다. 예를 들어 회사 가이드에서, ALBUM 엔티티는 DTYPE에서 A로 표시, MOVIE 엔티티는 M으로, BOOK 엔티티는 B로 표시하라고 되어있으면, 해당 엔티티 (즉, 자식 클래스)에 @DiscriminatorValue("A")
와 같이 작성하면 된다.
아래는 해당 내용을 코드로 작성한 것이다.
@Entity
@DiscriminatorValue("A")
public class Album extends Item { .. }
@Entity
@DiscriminatorValue("B")
public class Book extends Item { .. }
@Entity
@DiscriminatorValue("M")
public class Movie extends Item { .. }
실행 결과로 DTYPE
이 M으로 바뀐 것을 볼 수 있다.
04. 단일 테이블 전략
테이블 하나에 모든 컬럼을 다 넣고, DTYPE으로 구분하는 전략이다.
단일 테이블 전략을 표현하면 다음과 같다.
단일 테이블 전략을 적용하려면, Item 클래스의 @Inheritance(strategy = InheritanceType.JOINED)
를 @Inheritance(strategy = InheritanceType.SINGLE_TABLE)
로 바꾸면 된다.
실행하고 쿼리문을 보면 다음과 같이 하나의 테이블인 Item 엔티티에 모든 컬럼이 들어간 것을 볼 수 있다.
테이블을 모두 drop하고 다시 실행하고 select * from item;
을 실행하면 다음과 같이 데이터가 들어가 있는 것을 볼 수 있다.
단일 테이블 전략에서는, Item에 @DiscriminatorColumn
을 작성하지 않아도 DTYPE이 필수로 생성된다.
05. 구현 클래스마다 테이블 전략
상위 테이블에 있던 속성들을 하위 테이블로 내리고, 상위 테이블은 삭제하는 방식이다.
구현 클래스마다 테이블 전략을 표현하면 다음과 같다.
Item 클래스에서 전략을 바꾸고, 클래스를 abstract로 바꿔야 함에 유의해야 한다. abstract로 만들지 않으면 Item 테이블이 생성된다. @DiscriminatorColumn
는 제거한다.
@Entity
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class Item { .. }
실행 결과로, movie 테이블에 데이터가 잘 들어갔고, item 테이블은 만들어지지 않은 것을 볼 수 있다.
06. 어떤 전략을 사용해야 하는가?
단순하고 확장 가능성이 별로 없으면 단일 테이블 전략을,
비즈니스적으로 중요하고 복잡하면 조인 전략을 사용하면 된다.
'JPA' 카테고리의 다른 글
프록시 (0) | 2024.01.12 |
---|---|
@MappedSuperclass - 매핑 정보 상속 (0) | 2024.01.11 |
다양한 연관관계 매핑 (0) | 2024.01.07 |
양방향 연관관계와 연관관계의 주인 (2) | 2024.01.06 |
단방향 연관관계 (2) | 2024.01.06 |