7. 고급 매핑


고급 매핑

  • 상속 관계 매핑
    • 객체의 상속 관계를 데이터베이스에 어떻게 매핑하는지
  • @MappedSuperclass
    • 등록일, 수정일 같이 여러 엔티티에서 공통으로 사용하는 매핑 정보만 상속받고 싶을 때 사용
  • 복합 키와 식별 관계 매핑
    • 데이터베이스의 식별자가 하나 이상일 때 매핑하는 방법
    • 데이터베이스 설계에서 이야기하는 식별, 비식별 관계에 대해서
  • 조인 테이블
    • 테이블을 외래키 하나로 연관관계 맺는 것이 아닌 연결 테이블을 이용해서 매핑하는 방법
  • 엔티티 하나에 여러 테이블 매핑하기
    • 엔티티 하나에 여러 테이블을 매핑하는 방법



1. 상속 관계 매핑

  • ORM에서 이야기하는 상속 매핑에 가장 적합한 매핑 방법
  • 데이터베이스에는 객체지향의 상속이란 개념이 없음
  • 대신 아래 왼쪽 그림처럼 슈퍼타입-서브타입 관계가 존재, 오른쪽은 객체의 상속 관계
  • 1
  • 슈퍼타입-서브타입 논리 모델을 테이블로 구현할 때?
    • 각각의 테이블로 변환 : 조인 전략
    • 통합 테이블로 변환 : 단일 테이블 전략
    • 서브타입 테이블로 변환 : 구현 클래스마다 테이블 전략


1.1 조인 전략 : 각각의 테이블로 변환

  • 부모, 자식 엔티티를 각각 모두 테이블로 만들고 자식 테이블이 부모 테이블의 기본키를 받아서 기본키 + 외래키로 사용하는 전략
  • 객체는 타입으로 구분이 가능하지만 테이블은 타입이란 개념이 없음
    • DTYPE 컬럼을 추가해서 구분 컬럼으로 사용해야 함
  • 2
  @Entity
  @Inheritance(strategy = InheritanceType.JOINED)
  @DiscriminatorColumn(name = "DTYPE")
  public abstract class Item {
    @Id @GeneratedValue
    @Column(name = "ITEM_ID")
    private Long id;

    private String name;
    private int price;
    ...
  }

  @Entity
  @DiscriminatorValue("A")
  public class Album extends Item {
    private String artist;
    ...
  }

  @Entity
  @DiscriminatorValue("M")
  public class Movie extends Item {
    private String director;
    private String actor;
    ...
  }
  • 매핑 정보들
    • @Inheritance(strategy = InheritanceType.JOINED
      • 상속 매핑 시 부모 클래스에 이 어노테이션을 사용해줘야 함
      • 조인 전략을 사용하므로 InheritanceType.JOINED
    • @DiscriminatorColumn(name = "DTYPE")
      • 부모 클래스에스 구분 컬럼을 지정
      • 이 값으로 자식 테이블 구분 가능
      • 기본값이 DTYPE 이므로 name 부분은 생략 가능
    • @DiscriminatorValue("M")
      • 엔티티를 저장할 때 구분 컬럼에 입력할 값을 지정
      • 구분 컬럼인 DTYPE에 M 값이 저장됨
  • 기본 값으로 부모 테이블의 ID 컬럼명을 바꾸고 싶을 때
    • @PrimaryKeyJoinColumn 사용
    • @Entity
      @DiscriminatorValue("B")
      @PrimaryKeyJoinColumn(name = "BOOK_ID") // ID 재정의
      public class Book extends Item {
        ...
      }
      


  • 조인 전략의 장단점
    • 장점
      • 테이블이 정규화 됨
      • 외래키 참조 무결성 제약조건을 활용할 수 있음
      • 저장공간을 효율적으로 사용
    • 단점
      • 조회할 때 조인이 많이 사용되므로 성능 저하가 가능
      • 조회 쿼리가 복잡함
      • 데이터를 등록할 때 INSERT SQL이 두 번 실행됨
  • 특징
    • JPA 표준 명세는 구분 컬럼을 사용하도록 하지만 하이버네이트를 포함한 몇몇의 구현체에서는 구분 컬럼(@DiscriminatorColumn) 없이도 동작
  • 관련 어노테이션
    • @PrimaryKeyJoinColumn, @DiscriminatorColumn, @DiscriminatorValue


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

  • 테이블을 하나만 사용하고 DTYPE 컬럼으로 어떤 자식 데이터인지 구분(구분 컬럼 필수)
  • 조인을 사용하지 않으므로 일반적으로 가장 빠름
  • 3
  • 자식 엔티티가 매핑한 컬럼들이 모두 null을 허용해야 함
  @Entity
  @Inheritance(strategy = InheritanceType.SINGLE_TABLE)
  @DiscriminatorColumn(name = "DTYPE")
  public abstract class Item {
    @Id @GeneratedValue
    @Column(name = "ITEM_ID")
    private Long id;
    private String name;
    private String price;
    ...
  }

  @Entity
  @DiscriminatorValue("A")
  public class Album extends Item { ... }

  @Entity
  @DiscriminatorValue("M")
  public class Movie extends Item { ... }
  • 단일 테이블 전략의 장단점
    • 장점
      • 조인이 필요 없으므로 일반적으로 조회 성능이 빠름
      • 조회 쿼리가 단순함
    • 단점
      • 자식 엔티티가 매핑한 컬럼은 모두 null을 허용해야 함
      • 테이블이 커질 수 있으므로 조회 성능이 더 느려질 수 있다
  • 특징
    • 구분 컬럼이 필수 (@DiscriminatorColumn)
    • 구분 컬럼을 지정하지 않으면 기본으로 엔티티 이름을 사용


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

  • 자식 엔티티마다 테이블을 만들고 각각에 필요한 컬럼도 만들어주는 전략
  • 4
  • 일반적으로 추천하지 않는 전략
    @Entity
    @Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
    public abstract class Item {
      @Id @GeneratedValue
      @Column(name = "ITEM_ID")
      private Long id;
      private String name;
      private int price;
      ...
    }
    
    @Entity
    public class Album extends Item { ... }
    
    @Entity
    public class Movie extends Item { ... }
    
  • 구현 클래스마다 테이블 전략의 장단점
    • 장점
      • 서브 타입을 구분해서 처리할 때 효과적
      • not null 제약조건을 사용할 수 있음
    • 단점
      • 여러 자식 테이블을 함께 조회할 때 성능이 느림(SQL이 UNION을 사용해야 함)
      • 자식 테이블을 통합해서 쿼리하기 어려움
  • 특징
    • 구분 컬럼을 사용하지 않는다



2. @MappedSuperclass

  • 부모 클래스는 테이블과 매핑하지 않고 자식 클래스에 매핑 정보만 제공하고 싶을 때 사용
  • @Entity는 실제 테이블과 매핑, @MappedSuperclass는 실제 테이블 매핑 X
  • 추상 클래스로 설계 권장 - 상속용, 생성해서 사용할 일이 없기 때문
  • 5
  • BaseEntity를 @MappedSuperclass로 선언하고 Member, Seller가 상속받아서 사용
  @MappedSuperclass
  public abstract class BaseEntity {
    @Id @GeneratedValue
    private Long id;
    private String name;
    ...
  }

  @Entity
  public class Member extends BaseEntity {
    // id, name은 상속받음
    private String email;
    ...
  }

  @Entity
  public class Seller extends BaseEntity {
    // id, name은 상속받음
    private String shopName;
    ...
  }
  • **부모로부터 상속받은 걸 오버라이드 하려면? **
    • 매핑 정보를 오버라이드
      • @AttributeOverrides, @AttributeOverride
      • @Entity
        @AttributeOverride(name = "id", column = @Column(name = "MEMBER_ID"))
        public class Member extends BaseEntity { ... }
        
    • 연관관계를 오버라이드
      • @AssociationOverrides, @AssociationOverride
  • 정리하면?
    • 테이블과 매핑되지 않고 자식에게 물려주기 용도로 사용
    • @MappedSuperclass로 지정한 클래스는 엔티티가 아니므로 em.find()나 JPQL에서 사용 불가
    • 생성해서 사용할 일이 없으므로 추상클래스로 만드는 것이 권장됨



3. 복합 키와 식별 관계 매핑

: 추후 업로드



4. 조인 테이블

: 추후 업로드



5. 엔티티 하나에 여러 테이블 매핑하기

: 추후 업로드




© 2020.02. by blupine