Spring/JPA & Hibernate

고급 매핑

이준스 2019. 12. 17. 10:01
반응형

 상속관계 매핑

• 관계형 데이터베이스는 상속 관계 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();
	}
}

 

 

추가된 요구사항

  1. 상품의 종류는 음반, 도서, 영화가 있고 이후 더 확장될 수 있다. 
  2. 모든 데이터는 등록일과 수정일이 필수다.

단일 테이블 전략


@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();
	}
}

 

반응형