고급 매핑
상속관계 매핑
• 관계형 데이터베이스는 상속 관계 X
• 슈퍼 타입 서브타입 관계라는 모델링 기법이 객체 상속과 유사
• 상속관계 매핑: 객체의 상속과 구조와 DB의 슈퍼타입 서브타입 관계를 매핑
위와 같은 슈퍼타입 서브타입 논리 모델을 실제 물리 모델로 구현하는 세 가지 방법
• 각각 테이블로 변환 -> 조인 전략
• 통합 테이블로 변환 -> 단일 테이블 전략
• 서브타입 테이블로 변환 -> 구현 클래스마다 테이블 전
• @Inheritance(strategy=InheritanceType.XXX)
• JOINED: 조인 전략
• SINGLE_TABLE: 단일 테이블 전략
• TABLE_PER_CLASS: 구현 클래스마다 테이블 전략
• @DiscriminatorColumn(name=“DTYPE”)
• @DiscriminatorValue(“XXX”)
1. 조인 전략 (정석)
데이터를 생성할 때, 공통적인 속성(NAME, PRICE, DTYPE)은 ITEM 테이블에 INSERT, 서브 테이블의 속성은 서브 테이블에 INSERT
데이터를 조회할 때, PK, FK로 조인하여 가져온다. (DTYPE을 보고 어떤 테이블에 존재하는지 알 수 있다.)
- 장점
테이블 정규화
외래 키 참조 무결성 제약조건 활용 가능
저장공간 효율화 - 단점
조회 시 조인을 많이 사용, 성능 저하 (미약함)
조회 쿼리가 복잡함
데이터 저장 시 INSERT SQL 2번 호출 (갠춘)
@Entity
@Inheritance( strategy = InheritanceType.JOINED )
public class Item {
@Id
@GeneratedValue
@Column( name = "ITEM_ID" )
private Long id;
private String name;
private int price;
}
@Entity
public class Movie extends Item {
private String director;
private String actor;
}
public class JpaMain {
public static void main( final String[] args ) {
EntityManagerFactory emf = Persistence.createEntityManagerFactory( "hello" );
EntityManager em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
try {
Movie movie = new Movie();
movie.setActor( "olaf" );
movie.setDirector( "di" );
movie.setName( "frozon" );
movie.setPrice( 10000 );
em.persist( movie );
em.flush();
em.clear();
Movie findMovie = em.find( Movie.class, movie.getId() );
System.out.println( "findMovie = " + findMovie );
tx.commit();
} catch ( Exception e ) {
// transaction rollback
tx.rollback();
} finally {
em.close();
}
emf.close();
}
}
@DiscriminatorColumn 추가 시에 DTYPE 자동으로 생성됨
@Entity
@Inheritance( strategy = InheritanceType.JOINED )
@DiscriminatorColumn
public class Item {
@Id
@GeneratedValue
@Column( name = "ITEM_ID" )
private Long id;
private String name;
private int price;
}
자식 클래스에서 @DiscriminatorValue로 생성되는 Type의 value를 명시할 수도 있음
@Entity
@DiscriminatorValue( "MOVIE" )
public class Movie extends Item {
private String director;
private String actor;
}
2. 단일 테이블 전략
- 장점
조인이 필요 없으므로 일반적으로 조회 성능이 빠름
INSERT 쿼리 한 번이면 됨
SELECT 쿼리가 단순함 - 단점
자식 엔티티가 매핑한 칼럼은 모두 null 허용해야 함으로 데이터 무결성을 깨뜨릴 수 있음 (예에서는 name, price)
단일 테이블에 모든 것을 저장하므로 테이블이 커질 수 있다. 상황에 따라서 조회 성능이 오히려 느려질 수 있다.
@Entity
@Inheritance( strategy = InheritanceType.SINGLE_TABLE )
public class Item {
@Id
@GeneratedValue
@Column( name = "ITEM_ID" )
private Long id;
private String name;
private int price;
}
단일 테이블 전략에서는 DTYPE이 없으면 어떤 클래스의 데이터인지 알 수 없기 때문에,
@DiscriminatorColumn을 붙이지 않아도 DTYPE을 JPA가 자동으로 생성해준다.
3. 구현 클래스마다 테이블 전략
- 이 전략은 데이터베이스 설계자와 ORM 전문가 둘 다 추천 X
- 장점
서브 타입을 명확하게 구분해서 처리할 때 효과적
not null 제약조건 사용 가능 - 단점
여러 자식 테이블을 함께 조회할 때 성능이 느림(UNION SQL 필요)
자식 테이블을 통합해서 쿼리 하기 어려움
@Entity
@Inheritance( strategy = InheritanceType.TABLE_PER_CLASS )
public abstract class Item {
@Id
@GeneratedValue
private Long id;
private String name;
private int price;
}
부모 클래스로 조회할 경우, 모든 자식 클래스의 테이블에 INSERT 쿼리를 수행해야 한다는 단점이 있다.
public class JpaMain {
public static void main( final String[] args ) {
EntityManagerFactory emf = Persistence.createEntityManagerFactory( "hello" );
EntityManager em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
try {
Movie movie = new Movie();
movie.setActor( "olaf" );
movie.setDirector( "di" );
movie.setName( "frozon" );
movie.setPrice( 10000 );
em.persist( movie );
em.flush();
em.clear();
Item item = em.find( Item.class, movie.getId() );
System.out.println( "item = " + item );
tx.commit();
} catch ( Exception e ) {
// transaction rollback
tx.rollback();
} finally {
em.close();
}
emf.close();
}
}
추가된 요구사항
- 상품의 종류는 음반, 도서, 영화가 있고 이후 더 확장될 수 있다.
- 모든 데이터는 등록일과 수정일이 필수다.
단일 테이블 전략
@Entity
@Inheritance( strategy = InheritanceType.SINGLE_TABLE )
@DiscriminatorColumn
public abstract class Item {
@Id
@GeneratedValue
private Long id;
private String name;
private int price;
}
public class JpaMain {
public static void main( final String[] args ) {
EntityManagerFactory emf = Persistence.createEntityManagerFactory( "hello" );
EntityManager em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
try {
Book book = new Book();
book.setName( "JPA" );
book.setAuthor( "jj" );
em.persist( book );
em.flush();
em.clear();
Item item = em.find( Item.class, book.getId() );
System.out.println( "book = " + item );
tx.commit();
} catch ( Exception e ) {
// transaction rollback
tx.rollback();
} finally {
em.close();
}
emf.close();
}
}