티스토리 뷰

ORM/JPA

[JPA] 5. 고급 매핑

mandykr 2021. 12. 1. 14:04

목차

1. 상속관계 매핑

2. Mapped Superclass 매핑 정보 상속

 

 

1. 상속관계 매핑

관계형 데이터베이스는 상속 관계가 없지만

슈퍼타입 서브타입 관계라는 모델링 기법이 객체 상속과 유사

상속관계 매핑 : 객체의 상속 구조와 DB의 슈퍼타입 서브타입 관계를 매핑

@Entity
@NoArgsConstructor
@Getter @Setter
public class Album extends Item {
    private String artist;
}

@Entity
@NoArgsConstructor
@Getter
@Setter
public class Book extends Item {
    private String author;
}

@Entity
@NoArgsConstructor
@Getter
@Setter
public class Movie extends Item {
    private String actor;
}

 

Item 을 상속하는 3개의 엔티티를 정의할 때 상속관계 매핑 설정에 따라 테이블 생성 전략이 달라진다.

@Inheritance(strategy=InheritanceType.XXX)

  • 조인 전략 : JOINED
  • 단일 테이블 전략 : SINGLE_TABLE  -> default 설정 값
  • 구현 클래스마다 테이블 전략 : TABLE_PER_CLASS

> 테스트 코드로 쿼리 확인 : Item - Movie 매핑에서만 테스트

@SpringBootTest
@Transactional
class AdvancedmappingTest {
    @Autowired
    EntityManager em;

    @Test
    @Commit
    void test() {
        Movie movie = new Movie();
        movie.setActor("강동원");
        movie.setName("검은사제들");
        movie.setPrice(15000);

        em.persist(movie);

        em.flush();
        em.clear();

        em.find(Movie.class, movie.getId());
    }
}

1) 조인 전략 : 각각 테이블로 변환 (권장)

@Entity
@Getter
@Setter
@Inheritance(strategy = InheritanceType.JOINED)
@DiscriminatorColumn // dtype 컬럼 생성
public abstract class Item {
    @Id @GeneratedValue
    private Long id;

    private String name;
    private int price;
}

> 실행 결과

  • ITEM, MOVIE(, ALBUM, BOOK) 테이블 각각 생성
  • ITEM 테이블에 DTYPE 컬럼 생성 : default 값은 엔티티 이름이 저장됨
  • Movie 엔티티를 조회할 때 ITEM과 MOVIE 테이블을 조인

 

> 장점

  • 테이블 정규화
  • 외래 키 참조 무결성 제약조건 활용가능
  • 저장공간 효율화

> 단점 (성능은 실제로 크게 저하되지 않음)

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

 

2) 단일 테이블 전략 : 통합 테이블로 변환

@Entity
@Getter
@Setter
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
public abstract class Item {
    @Id @GeneratedValue
    private Long id;

    private String name;
    private int price;
}

> 실행 결과

  • ITEM 테이블만 생성
  • 기본적으로(@DiscriminatorColumn 이 없어도) DTYPE 컬럼 생성 : default 값은 엔티티 이름이 저장됨
  • Album, Book 과 관련된 컬럼은 null 이 저장됨

> 장점

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

 

> 단점

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

 

 

3) 구현 클래스마다 테이블 전략 : 서브타입 테이블로 변환

@Entity
@Getter
@Setter
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class Item {
    @Id @GeneratedValue
    private Long id;

    private String name;
    private int price;
}

> 실행 결과

  • ITEM 테이블은 생성되지 않음
  • MOVIE 테이블이 Item 엔티티에 정의된 모든 컬럼을 갖는다
//em.find(Movie.class, movie.getId());
em.find(Item.class, movie.getId()); // 부모 클래스의 타입으로 찾기

--> 부모 클래스의 타입으로 데이터를 조회할 때 하위 모든 테이블(ALBUM, BOOK, MOVIE)에서 찾아 비효율적임

    select
        item0_.id as id1_2_0_,
        item0_.name as name2_2_0_,
        item0_.price as price3_2_0_,
        item0_.artist as artist1_0_0_,
        item0_.author as author1_1_0_,
        item0_.actor as actor1_3_0_,
        item0_.clazz_ as clazz_0_ 
    from
        ( select
            id,
            name,
            price,
            artist,
            null as author,
            null as actor,
            1 as clazz_ 
        from
            album 
        union
        all select
            id,
            name,
            price,
            null as artist,
            author,
            null as actor,
            2 as clazz_ 
        from
            book 
        union
        all select
            id,
            name,
            price,
            null as artist,
            null as author,
            actor,
            3 as clazz_ 
        from
            movie 
    ) item0_ 
where
    item0_.id=?

> 장점

  • 서브 타입을 명확하게 구분해서 처리할 때 효과적
  • not null 제약조건 사용 가능

 

> 단점

  • 여러 자식 테이블을 함께 조회할 때 성능이 느림(UNION SQL 필요)
  • 자식 테이블을 통합해서 쿼리하기 어려움
  • 쓰지 말자

 

조인 전략을 기본적으로 사용하고

확장 가능성이 없이 단순한 경우 단일 테이블 전략을 사용해볼 수 있다.

 

 

2. MappedSupperclass

테이블과 관계 없이 단순히 자식 엔티티에 공통으로 사용하는 매핑 정보를 제공할 때 사용

@MappedSuperclass
@Getter
@Setter
public abstract class BaseEntity {

    private String createBy;
    private LocalDateTime createDate;
    private String lastModifiedBy;
    private LocalDateTime lastModifiedDate;
}

@Entity
@NoArgsConstructor
@Getter
@Setter
public class Member extends BaseEntity {
    @Id @GeneratedValue
    private Long id;

    private String name;
}

생성자 ID, 생성일시, 수정자 ID, 수정일시 등의 컬럼에서 사용

ID, 일시 정보를 자동으로 저장하는 방법은 추후에 업데이트 예정

 

 

 

 

출처

https://www.inflearn.com/course/ORM-JPA-Basic 자바 ORM 표준 JPA 프로그래밍 - 기본편(김영한)

728x90

'ORM > JPA' 카테고리의 다른 글

[JPA] 7. 값 타입  (0) 2022.03.02
[JPA] 6. 프록시와 연관관계 관리  (0) 2021.12.14
[JPA] 4. 다양한 연관관계 매핑  (0) 2021.11.29
[JPA] 3. 연관관계 매핑  (0) 2021.11.09
[JPA] 2. 엔티티 매핑  (0) 2021.11.07