JPA

상속관계 매핑

salmon16 2023. 8. 29. 23:45
  • 관계형 데이터베이스에는 상속 관계가 없다.
  • 슈퍼타입 서브타입 관계라는 모델링 기법이 객체 상속과 유사하다.
  • 상속관계 매핑 : 객체의 상속과 구조의 DB의 슈퍼타입 서브타입관계를 매핑

DB의 논리모델
객체의 상속관계

상속관계 매핑

  • 슈퍼타입 서브타입 논리 모델을 실제 물리 모델로 구현하는 방법
    • 각각 테이블로 변환 -> 조인 전략
    • 통합 테이블로 변환 -> 단일 테이블 전략
    • 서브타입 테이블로 변환 ->구현 클래스마다 테이블 전략

위 3개의 방법 어느 것을 사용하더라도 JPA에서는 매핑이 가능하다. 

각 Entity를 만들어 보자

@Entity
public class Item {

    @Id @GeneratedValue
    private Long id;

    private String name;
    private int price;
}
@Entity
public class Book extends Item{
    private String author;
    private String isbn;
}
@Entity
public class Album extends Item{
    private String artist;
}
@Entity
public class Movie extends Item{

    private String director;
    private String actor;
}

상속을 받는 클래스들은 Id도 상속받기 때문에 상속받는 클래스 안에서 선언을 할 필요가 없다.

조인 전략

 

각 테이블을 만들고 Join으로 데이터를 들고 온다. 

 

만약 ALBUM의 데이터가 들어오면 insert 2번으로 이름, 가격은 ITEM테이블 ARTIST는 ALBUM테이블에 들어간다.

ALBUM데이터의 조회는 PK가 같기 때문에 ITEM, ALBUM 테이블을 Join해서 데이터를 가져온다.

 

ITEM테이블의 DTYPE 칼럼을 추가해서 무슨 ITEM인지 구별해서 조인을 한다.

 

조인 전략을 사용하려면 @Inheritance 어노테이션에 JOINED 속성을 추가한다.

@Inheritance(strategy = InheritanceType.JOINED)
@DiscriminatorColumn // Join 전략에서는 없어도 되지만 있는게 좋다 DTYPE 칼럼을 만든다.

 

 

@Entity
@Inheritance(strategy = InheritanceType.JOINED)
@DiscriminatorColumn // Join 전략에서는 없어도 되지만 있는게 좋다 DTYPE 칼럼을 만든다.
public class Item {

    @Id @GeneratedValue
    private Long id;

    private String name;
    private int price;

각 테이블이 생성된다. 

메인 코드를 작성하고 DB를 보자 

Movie movie = new Movie();
movie.setDirector("aaaa");
movie.setActor("bbbb");
movie.setName("hello");
movie.setPrice(10000);

em.persist(movie);

값이 잘 들어가 있는 것을 확인할 수 있다.

MOVIE 의 ID 값이 ITEM의 ID 값과 동일한 것을 확인할 수 있다.

MOVIE의 ID값은 PK이면서 FK이다.

 

em.find를 해보자 

Movie findMoive = em.find(Movie.class, movie.getId());
System.out.println("findMoive : " + findMoive);

JPA가 알아서 Join을 해주는 것을 볼 수 있다. 

단일 테이블 전략

단일 테이블 전략은 논리모델을 1개의 테이블에 다 합치는 것이다.

칼럼 DTYPE을 통해 어떤 데이터인지 구분한다.

SINGLE_TABLE 속성을 사용하면 된다.

strategy 속성을 사용하지 않았을 때 기본값이 된다. 

@Inheritance(strategy = InheritanceType.SINGLE_TABLE)

단일 테이블 전략 테이블 생성

조인 전략에서 돌렸던 Main코드를 돌리면 ITEM 테이블이 위 사진처럼 생성된다.

join 필요가없고 데이터 저장할 때 insert SQL도 한 번만 나가게 된다. 

@DiscriminatorColumn 어노테이션이 없어도 자동으로 생성된다.

 

구현 클래스마다 테이블 전략

3개의 테이블을 만든 후 그냥 ITEM의  정보를 다 들고 있는다.

 

TABLE_PER_CALSS 속성 사용 

@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)

그리고 Item class를 추상 클래스로 만들어야 한다.

public abstract class Item {

    @Id @GeneratedValue
    private Long id;

    private String name;
    private int price;

추상 클래스로 안만들면 Item Entity도 단독적으로 생성될 수 있다고 생각하기 때문에 Item 테이블이 생성된다.

테이블 자체가 다르기 때문에 @DiscriminatorColumn 에노테이션을 사용할 이유가 없어진다. 

하지만 이 전략은 find할 때 단점이 있다.

Main코드를 객체지향적으로 바꾸어 보자

Movie findMoive = em.find(Movie.class, movie.getId());

위 코드를 아래 코드로 변경

Item item = em.find(Item.class, movie.getId());

코드가 변경되면 3개의 테이블 Moive, Book, Album 테이블을 다 탐색해야 한다.

Item 입장에서 3개의 테이블 중 어디에 있을지 모르기 때문이다. 

 

각 전략의 장단점

Join 전략

장점

  • 정교화가 되어있다. 
  • 외래 키 참조 무결성 제약조건 활용가능
  • 만약 Item의 값들만 필요하면 Item 테이블만 조회하기 가능
  • 저장공간 효율화

단점

  • 조회시 조인을 많이 사용, 성능 저하
  • 조회 쿼리가 복잡함
  • 데이터 저장시 Insert SQL 2번 호출

단일 테이블 전략

장점

  • 조인이 필요 없으므로 일반적으로 조회 성능이 빠름
  • 조회 쿼리가 단순함

단점

  • 자식 엔티티가 매핑한 칼럼은 모두 Null 허용
  • 단일 테이블에 모든 것을 저장하므로 테이블이 커질 수 있고 상황에 따라서 조회 성능이 오히려 느려질 수 있다.

구현 클래스 마다 테이블 전략 (안 쓰는 것을 권장한다.)

이 전략은 데이터베이스 설계자와 ORM 전문가 둘 다 추천하지 않는다.

장점

  • 서브 타입을 명확하게 구분해서 처리할 때 효과적 
    • Movie findMoive = em.find(Movie.class, movie.getId()); Item 타입이 아닌 서브 타입사용
  • not null 제약 조건 사용가능하다.

단점

  • 여러 자식 테이블을 함께 조회할 때 성능이 느림
  • 자식 테이블을 통합해서 쿼리하기 어려움
  • 변경이라는 관점에서 안 좋다. (새로운 타입 추가)

 

 

'JPA' 카테고리의 다른 글

프록시와 연관관계 관리  (0) 2023.08.30
MappedSuperclass  (0) 2023.08.30
다양한 연관관계 매핑  (0) 2023.08.29
연관관계 매핑, 단방향, 양방향  (0) 2023.08.28
엔티티 매핑, 기본 키 매핑  (0) 2023.08.27