[자바 ORM 표준 JPA 프로그래밍 - 기본편] 고급매핑

이재표·2023년 9월 29일
0

상속관계 매핑

객체에는 상속관계가 존재하지만 관계형 DB에는 상속관계가 없다.
그래도 관계형 데이터베이스에 존재하는 상속슈퍼타입, 서브타입 관계 모델링 기법으로 상속을 나타내보자!

상속 관계매핑 : 객체의 상속과 구조와 db의 슈퍼타입 서브타입 관계를 매핑
관계형 db설계는 논리모델과 물리 모델리 있는데, 논리모델은 아이템과 그 아래의 구체적인 음반 영화 책이 있으면 공통적이 속성들(이름, 가격)같은 것을 아이템의 필드에 두고, 각각의 특성을 밑으로 내리는 것을 슈퍼타입 서브타입이라는 논리모델 구성이 가능하다

객체는 상속관계가 있어서 아이템이라는 추상타입을 만들고, 구체적으로 상속관계로 가져갈수 있다.

슈퍼타입 서브타입 논리 모델을 실제 물리모델로 구현하는 법은 3가지가 있다.

  • 조인 전략
  • 단일테이블 전략
  • 구현 클래스마다 테이블 생성전략

조인 전략


상위 테이블을 두고, 특성에 따라 테이블을 나누어 필요할때 조인으로 가져오는 방법. 아이템에는 아이템id를 두고, 앨범의 특성만 앨범테이블에 넣어서 두번의 insert를 하고, pk,fk를 조인해서 가져오는것이다. 그럼 아래의 여러 테이블(앨범,영화,북 등)이 있으면 타입을 통해 나눠서 가져오게된다.
가장 정규화가 잘 된 JPA와 유사한 방식이다.

코드구현

@Entity
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;
}

다음과 같은 쿼리가 나가게된다.

하지만 쿼리는 여러 테이블이 아닌 단일 테이블이 만들어지는것을 알수 있다.

기본전략은 단일테이블로 만드는 방식이기 때문이다!!

그럼 우리가 처음에 원하던 정규화된 방식으로 만들수 있을까?
@Inheritance(strategy = InheritanceType.JOINED)라는 옵션을 넣어주면 원하는대로 테이블이 나뉘는것을 알수 있다.

@Entity
@Inheritance(strategy = InheritanceType.JOINED)
public class Item {
	...
}

다음 옵션을 넣으면 테이블이 나뉘어 지는것을 볼수 있는데, 테이블을 나누후 데이터를 저장을 시키면 insert쿼리가 두번 나가는 것을 알수 있다. 왜냐하면 상위테이블에 한번, 하위 테이블에 한번 값을 넣어야하기 때문이다.

그리고 조회할때는 join하여 값을 가져온다.

이때 테이블을 보면 그림과 다르게 DTYPE필드가 없다. DTYPE을 넣어주고 싶을때는 상위테이블에 @DiscriminatorColumn어노테이션을 붙혀주면 된다. 가능하면 넣어두는것이 좋다. 이때 자식클래스의 dtype에 들어가는 이름을 엔티티명(디폴트값)이 아닌 특정명을 사용해야할때 @DiscriminatorValue("")을 붙히면 된다.

정리
장점

  • 테이블 정규화
  • 외래키 참조 무결성 제약조건 활용가능
  • 저장공간 효율화
    단점
  • 조회시 join을 많이 사용하여 성능 저하
  • 조회 쿼리 복잡
  • 데이터 저장시 insert쿼리 2번 호출

    join도 잘 맞추면 그렇게 저하되지 않고, insert 2번정도는 그리 큰문제 아님. 복잡하다는것이 가장 큰 단점. 그래도 저장공간이 효율적이기에 어느 부분에는 좋고, 가장 정석방법

단일테이블 전략

단순하게 나누지 않고 그냥 하나의 테이블을 만들고, 타입필드(DTYPE)를 통해 구분하는 방법

그냥 값을 다 넣고 DTYPE을 통해 무엇인지 구별하는전략으로 구현할때는 객체는 상속관계를 두고, 상위클래스에 @Inheritance(strategy = InheritanceType.SINGLE_TABLE)어노테이션을 붙히면 된다.

한번에 INSERT되고, JOIN할 필요도 없기 때문에 성능이 가장 좋고, 상속관계 매핑가 달리 @DiscriminatorColumn없어도 DTYPE이 생긴다.

주요하게 볼것은 상속관계매핑와 디비설계는 바꼈지만 코드는 별로 바뀐게 없다. JPA의 큰장점으로 장점을 이용하면 성능테스트를 한후에 전략을 바꿔도 코드에서는 크게 변화가 없어진다.

정리
장점

  • JOIN이 필요없으므로 일반적으로 성능이 가장 좋음
  • 조회 쿼리가 단순함
    단점
  • 자식 엔티티가 매핑한 컬럼은 모두 NULL허용해줘야한다. 왜냐면 자식에 따라 값이 들어갈수도 안들어갈수도 있기때문에
  • 단일 테이블에 모든 것을 저장하여 테이블이 너무 커질수 있고, 그에따라 조회 성능이 악화될수 있다.

    데이터 무결성

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

각각 상위 테이블을 만드는 것이 아닌 그냥 각각 테이블을 다 만드는 것

@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)을 상위클래스에 붙히면 된다. 그리고 상위클래스의 경우 단독적으로 사용할것이 아니기 때문에 abstract클래스이어야한다(위의 두전략또한 동일).
해당 전략은 DTYPE이 필요없기때문에 @DiscriminatorColumn은 없어도됨. 하지만 단순하게 값을 넣고 뺄때는 좋지만 명확한 타입을 주는것이 아닌 부모타입으로 조회할때 모든 데이터(모든 type)를 다 조회하기에 성능이 매우 악화된다.
정리
장점

  • 서브타입을 명확하게 구분해서 처리할때 효과적
  • NOT NULL제약조건 사용가능
    단점
  • 여러 자식 테이블을 함께 조회할 때 성능이 느림
  • 자식 테이블을 통합해서 쿼리하기 어려움

    이 방법은 쓰면 안된다.

MappedSuperclass

상속관계 매핑과는 크게 상관은 없다.

객체입장에서 중복되는 속성을 상속받아 쓰고싶어서 공통매핑을 상속받고 싶을때 사용한다.

@Getter @Setter
@MappedSuperclass
public class BaseEntity {
    private String createdBy;
    private String lastModifiedBy;
    private LocalDateTime createdDate;
    private LocalDateTime lastModifiedDate;
}

다음과 같이 공통정보가 들어있는 객체를 만들고 필요한 엔티티에서 상속받아 사용한다.
@MappedSuperclass은 매핑정보만 받을때 사용한다.

  • 직접 사용할일이 별로 없기때문에 추상클래스로 사용하는것을 권장하면, 보통 수정날짜, 생성날짜 등에서 많이 사용한다.
  • 엔티티가 상속받을때는 @Entity가 있거나 @MappedSuperclass가 있거나 할때만 상속 가능

0개의 댓글