Album, Movie, Book은 모두 공통적으로 Item 테이블의 컬럼들을 가지고 있다.
// 따로 읽어볼 내용: 슈퍼타입 서브타입?
슈퍼타입 서브타입 논리 모델을 실제 물리 모델로 구현하는 방법은 3가지가 있다.
// Item.java
@Entity
public class Item {
@Id @GeneratedValue
private Long id;
private String name;
private int price;
// getter, setter
}
// Album.java
@Entity
public class Album extends Item {
private String artist;
// getter, setter
}
// Movie.java
@Entity
public class Movie extends Item {
private String director;
private String actor;
// getter, setter
}
// Book.java
@Entity
public class Book extends Item {
private String author;
private String isbn;
// getter, setter
}
이를 그냥 실행 시 결과는 다음과 같이 단일 테이블로 create 된다.
@Inheritance
, @DiscriminatorColumn
을 Item 엔티티에 추가하여 실행하면 아래와 같이 조인 전략으로 매핑할 수 있다.
// Item.java
@Entity
@Inheritance(strategy = InheritanceType.JOINED)
@DiscriminatorColumn
public class Item {
@Id @GeneratedValue
private Long id;
private String name;
private int price;
// getter, setter
}
// Album.java
@Entity
public class Album extends Item {
private String artist;
// getter, setter
}
// Movie.java
@Entity
public class Movie extends Item {
private String director;
private String actor;
// getter, setter
}
// Book.java
@Entity
public class Book extends Item {
private String author;
private String isbn;
// getter, setter
}
import domain.*;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;
public class JpaMain {
public static void main(String[] args) {
EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello3");
EntityManager em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
try {
Movie movie = new Movie();
movie.setDirector("Christopher Nolan");
movie.setActor("Heath Ledger");
movie.setName("Dark Night");
movie.setPrice(10000);
em.persist(movie);
tx.commit();
} catch (Exception e) {
tx.rollback();
} finally {
em.close();
}
emf.close();
}
}
위와 같이 값을 넣어보면 Item과 Movie에 각각 한 번씩 insert query가 실행된다.
@Inheritance(strategy=InheritanceType.XXX)
: 부모 클래스에 상속 매핑 지정@DiscriminatorColumn(name="DTYPE")
: 부모 클래스에 구분 컬럼 지정. 저장된 자식 테이블을 구분할 수 있다.@DiscriminatorValue("XXX")
: 엔티티를 저장할 때 구분 컬럼에 입력할 값을 지정.기본 값으로 자식 테이블은 부모 테이블의 ID 컬럼명을 그대로 사용하는데, 만약 자식 테이블의 기본 키 컬럼명을 변경하고 싶다면, @PrimaryKeyJoinColumn
애노테이션을 사용하면 된다.
조인 전략은 다음과 같은 특징이 있다.
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
로 지정하면 된다.
실행 결과는 다음과 같다.
테이블은 Item 테이블 하나로(단일 테이블)로 매핑되고, insert query가 1번 만 실행된다. 또한 @DiscriminatorColumn
을 기입하지 않아도 자동으로 들어간다.
정리하자면,
자식 엔티티마다 테이블을 만드는 방식이다.
@Entity
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class Item {
@Id @GeneratedValue
@Column(name = "ITEM_ID")
private Long id;
private String name;
private int price;
// getter, setter
}
@Entity
public class Album extends Item { ... }
@Entity
public class Moive extends Item { ... }
@Entity
public class Book extends Item { ... }
Item 테이블을 생성하지 않고, Album, Book, Movie 테이블에서 각각 id, name, price 필드를 가지고 있다. Item을 조회하면 Album, Book, Movie 세 개의 테이블을 UNION ALL
로 전부 조회해서 가져온다.
Hibernate:
select
item0_.ITEM_ID as ITEM_ID1_2_0_,
item0_.name as name2_2_0_,
item0_.price as price3_2_0_,
item0_.artist as artist1_0_0_,
item0_.actor as actor1_3_0_,
item0_.director as director2_3_0_,
item0_.author as author1_1_0_,
item0_.isbn as isbn2_1_0_,
item0_.clazz_ as clazz_0_
from
( select
ITEM_ID,
name,
price,
artist,
null as actor,
null as director,
null as author,
null as isbn,
1 as clazz_
from
Album
union
all select
ITEM_ID,
name,
price,
null as artist,
actor,
director,
null as author,
null as isbn,
2 as clazz_
from
Movie
union
all select
ITEM_ID,
name,
price,
null as artist,
null as actor,
null as director,
author,
isbn,
3 as clazz_
from
Book
) item0_
where
item0_.ITEM_ID=?
item = domain.Movie@2370ac7a
1월 31, 2022 11:45:57 오전 org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl stop
INFO: HHH10001008: Cleaning up connection pool [jdbc:h2:tcp://localhost/~/test3]
이 전략은 데이터베이스 설계자와 ORM 전문가 둘 다 추천하지 않는 방식이다.(쓰면 안됨)
// 조인 전략(정석)이나 단일 테이블 전략(데이터가 많지 않고 단순할 때)을 고려하자.
테이블과 관계 없고, 단순히 엔티티가 공통 매핑 정보가 필요할 때 사용한다.(id, name) 주로 등록일, 수정일, 등록자, 수정자 같은 전체 엔티티에서 공통으로 적용하는 정보를 모을 때 사용한다.
// ex) 모든 테이블에 row 생성일, 수정일을 등록해야 하는 경우(createdAt, updatedAt)
@MappedSuperclass
public abstract class BaseEntity {
@Id @GeneratedValue
private Long id;
private String name;
...
}
@Entity
public class Member extends BaseEntity {
// ID 상속
// Name 상속
private String email;
...
}
@Entity
public class Seller extends BaseEntity {
// ID 상속
// Name 상속
private String shopName;
...
}
em.find(BaseEntity))
불가)// 참고: @Entity
클래스는 @MappedSuperclass
로 지정한 클래스만 상속 가능함.