본문 바로가기

Dot Programming/JPA

[JPA] 상속관계 매핑(조인, 단일 테이블)과 @MappedSuperclass

    1. 상속관계 매핑

    상속관계 매핑은 객체의 상속 구조와 DB의 슈퍼타입 - 서브타입 관계를 매핑하는 것이다.

     

     

     

    좌: DB(슈퍼타입-서브타입), 우: 객체 상속

     

    ☛ 관계형 DB에는 상속 관계라는 개념이 없기때문에 대신에 객체 상속과 유사하게 슈퍼타입 - 서브타입 관계라는 모델링 기법이 존재한다.

     


     

    슈퍼타입 - 서브타입 논리 모델을 실제 물리 모델로 구현하는 방법은 다음과 같다.

    1) 각각 테이블로 변환 → 조인 전략

    조인 전략

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

     

     

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

    단일 테이블 전략

    장점 단점
     • 조인이 필요 없으므로 일반적으로 조회 성능이 빠름
     • 조회 쿼리가 단순함
     • 자식 엔티티가 매핑한 컬럼은 모두 null 허용
     • 단일 테이블에 모든 것을 저장하므로 테이블이 커질 수 있다.
        → 상황에 따라서 조회 성능이 오히려 느려질 수 있다.

     

     

     

    3) 서브타입 테이블로 변환 → 구현 클래스마다 테이블 전략 (권장 X)

    ‣ 이 전략은 데이터베이스 설계자와 ORM 전문가 둘 다 추천X

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

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

    ☛ 장점에 비해 단점이 너무 크다. 테이블 조회 성능이랑 수정될 때 DB 설계 관점에서 좋지 않다.

     

     

    주요 어노테이션

    @Inheritance(strategy=InheritanceType.XXX)  • JOINED: 조인 전략
     • SINGLE_TABLE: 단일 테이블 전략

     • TABLE_PER_CLASS: 구현 클래스마다 테이블 전략
    @DiscriminatorColumn(name=“DTYPE”) DTYPE으로 하위 칼럼을 구분
    @DiscriminatorValue(“XXX”) default : entity name

     

    @Entity
    @Inheritance(strategy = InheritanceType.JOINED) // 조인전략 
    @DiscriminatorColumn
    public class Item {
    
        @Id @GeneratedValue
        private Long id;
    
        private String name;
        private int price;
    
    }
    
    
    @Entity
    @DiscriminatorValue("B")
    public class Book extends Item {
        private String author;
        private String isbn;
    }
    
    
    @Entity
    @DiscriminatorValue("M")
    public class Movie extends Item {
        private String director;
        private String actor;
    
    }
    
    
    @Entity
    @DiscriminatorValue("A")
    public class Album extends Item{
        private String artist;
    
    }
    
    

     

     

    조인 전략 예시

    예시로 조인전략을 사용하여 데이터를 삽입해봤다.

    Movie movie = new Movie();
    movie.setDirector("a2aaa");
    movie.setActor("bbb2b");
    movie.setName("라라2라");
    movie.setPrice(10000);
    
    em.persist(movie);
    
    em.flush();
    em.clear();
    
    Movie findMovie = em.find(Movie.class, movie.getId());
    System.out.println("findMovie = " + findMovie);

     

    쿼리를 보면 INSERT 쿼리가 2번 실행되는 것을 볼 수 있다.

    삽입 쿼리

    DType은 지정한대로 M으로 저장되었다. 테이블이 정규화되어 잘 저장되어 있는 것을 볼 수 있다.

    Dtype 실행예시

     

    조회 시 Inner Join을 사용하여 쿼리문이 복잡한 것을 볼 수 있다.

    조회 쿼리

     

    싱글테이블 전략 예시

    @Entity
    @Inheritance(strategy = InheritanceType.SINGLE_TABLE)
    @DiscriminatorColumn
    public class Item {
       //...
    }

     

    단일 테이블에 모든 것을 저장하므로 테이블이 커질 상황을 조심해야 한다. 그러면 조회 성능이 오히려 느려질 수도 있다.

    삽입 쿼리

    단일 테이블에 모여있기 때문에 자식 엔티티가 매핑한 컬럼은 모두 null 허용을 해야한다. 

     

    그래도 조인 전략에 비해 조회 쿼리가 매우 간단함을 볼 수 있다.

    조회 쿼리

     

     

     

    상속관계 매핑 정리

    정규화된 방식으로 조인 전략을 사용하는 것이 정석이지만 실무에서 상속관계가 쓰이는 일은 굉장히 흔하지 않다. 그래서 그 상속관계 매핑이 되게 단순하다 싶으면(데이터 크기, 중요성, 확장성 등)이  단일 테이블 전략을 사용하는 것이 좋다.

     

    (구현 클래스마다 테이블 전략은 고민도 하지마라)

     

     

     

    2. @MappedSuperclass - 매핑 정보 상속

    공동 매핑 정보가 필요할 때 사용한다.

    예시) member.id, name같은 속성이 계속 반복해서 나올 때 @MappedSuperclass를 사용

     

    @MappedSuperclass

    • 상속관계 매핑이 아님
    • 엔티티 아님 → 그래서 테이블과 매핑이 되지 않음

    • 부모 클래스를 상속 받는 자식 클래스에 매핑 정보만 제공

    • 조회, 검색 불가(em.find(BaseEntity) 불가)
    • 직접 생성해서 사용할 일이 없으므로 추상 클래스 권장 (설계상 좋음)

     

     

    예를 들어 생성날짜, 생성일과 같이 중복되는 부분을 아래 코드와 같이 @MappedSuperclass를 이용하여 간단하게 만들 수 있다.

    @MappedSuperclass
    public abstract class BaseEntity {
        private String createdBy;
        private LocalDateTime createdDate;
        private String lastModifiedBy;
        private LocalDateTime lastModifiedDate;
        
        // getter, setter
     }
     
     
    @Entity
    public class Member extends BaseEntity {
    
    	//...
      
    }
    
    @Entity
    public class Team extends BaseEntity{
    
    	//...
      
    }

     

    그러면 아래와 같이 Member클래스에 해당 속성이 생성되는 것을 볼 수 있다.

     

     

     

     

    @MappedSuperclass가 선언된 클래스는 테이블과는 관계가 없고 단순히 엔티티가 공통으로 사용하는 매핑 정보를 모으는 역할만 해준다. 주로 등록일, 수정일, 등록자, 수정자같은 전체 엔티티에서 공통으로 적용하는 정보를 모을 때 주로 사용한다고 한다.

     

    추가로 @Entity클래스는 엔티니나 @MappedSuperclass로 지정한 클래스만 상속이 가능하니 참고하자. 


    ❈출처

    인프런 - 자바 ORM 표준 JPA 프로그래밍

     

    'Dot Programming > JPA' 카테고리의 다른 글

    [JPA] 값 타입  (0) 2021.05.23
    [JPA] 프록시와 연관관계 관리  (0) 2021.05.21
    [JPA] 다양한 연관관계 매핑  (0) 2021.05.14
    [JPA] 연관관계 매핑 기초  (0) 2021.05.11
    [JPA] 엔티티 매핑  (0) 2021.05.07