Lee's Grow up

[JAVA/JPA] 상속관계 매핑 본문

PROGRAMMING/JAVA

[JAVA/JPA] 상속관계 매핑

효기로그 2020. 4. 13. 14:36
반응형

해당 내용은 인프런의 자바 ORM 표준 JPA 프로그래밍 - 기본편, 김영한 의 내용을 기반으로 정리해서 작성한 글입니다.
자세한 내용은 해당 강의 또는 책을 구매하시는걸 추천합니다.

1. 테이블의 상속

상속을 테이블로 구현하기 위해서는 3가지 방법이 존재

  • 조인 전략
  • 싱글 테이블 전략
  • 테이블마다 구현 전략

JPA에서는 기본 전략으로 싱글 테이블을 따름

@Entity public class Item {
    // 필드
 }

@Entity public Book extends Item{
 // 필드
}

실제 테이블 생성 쿼리를 보면 Item, Book 클래스의 필드를 모두 가지는 테이블을 생성

2. 조인 전략

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

@Entity public Book extends Item{
 // 필드
}

이렇게 사용하면 하위 클래스에 매핑되는 테이블에 자동으로 외래키가 기본키로 생성됨, 정규화되어서 테이블을 생성해준다. 실제 JPA 엔티티 설계와 제일 비슷한 모양으로 테이블이 설계가 됨

그래서 하위 클래스에 값을 넣어도 상위 클래스에도 테이블에도 값이 들어감

    Book book = new Book();
    book.setName("연습용 책");
    book.set...
    ...

위와 같은 로직을 수행하면 Book,Item에 동일한 key 값이 들어가고 각각 맞는 컬럼에 값이 추가된다. 또한 하위 클래스를 셀렉트할 경우 상위 클래스를 조인해서 값을 조회해온다.

추가로 @DiscriminatorColumn(name="사용할 컬럼명") 으로 사용할 경우 부모 테이블에 DTYPE가 자동으로 추가
DTYPE이 있을 경우 ITEM 테이블만 가지고 운영또는 조회할경우 어느 하위 테이블의 타입 객체인지 알 수 있기 때문

DTYPE에 들어가는 이름은 기본적으로 Entity 네임이 들어감 변경하고 싶은 경우, 하위 클래스에서 @DiscriiminatorValue("설정할 값") 으로 설정가능

3. 싱글테이블 전략 ( 기본전략이기도함 )

기본전략으로 나와있기 때문에 설명은 생략. 다만 @DiscriminatorColumn은 필수로 넣어야지 어떤 타입인지 알 수 있기 때문에 필수로 선언할 것, 기본적으로 해당 하위 객체 컬럼의 값이 있냐 없냐등의 로직으로 구분 가능하겠지만 불필요한 작업

타입을 추가해서 해당 테이블의 데이터가 Book인지 Movie인지 즉 어떤 타입인지 알 수 있게 추가가 필요함

테이블당 구현 전략

@Entity 
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class Item {
    // 필드
 }

@Entity public Book extends Item{
 // 필드
}

하위 클래스를 전부다 테이블로 만드는 전략으로 상위 클래스를 추상클래스로 정의, 전략을 TABLE\_PER\_CLASS로 변경
@DiscriminatorColumn은 의미가 없어서 사용해도 적용안됨 -> 테이블 자체가 구분이 되기 때문

그러나 상위 클래스 즉 ITEM이 없는 테이블이기 때문에 객체지향적으로 ITEM으로 셀렉트할 경우, 모든 테이블을 다 조회 union후 있는 값을 리턴해주는 불필요하고, 어려운 로직이 동작됨. 얼핏 간단하고 쉬워보이지만 단점이 많다.

정리
  • 조인 전략
    • 장점
      • 테이블 정규화
      • 외래 키 참조 무결성 제약조건 활용가능
      • 저장공간 효율화
    • 단점
      • 조회시 조인을 많이 사용. 성능 저하
      • 조회 쿼리가 복잡
      • 데이터 저장시 insert가 부모 클래스 만큼 동작 ex) 예제에선 2번
  • 단일 테이블 전략
    • 장점
      • 조인이 필요x 성능이 제일 빠름, 쿼리가 단순
    • 단점
      • 자식 entity 의 컬럼을 모두 null 허용 해야함.. 데이터 무결성에 문제가 생김
      • 오히려 테이블의 컬럼이 너무 많아져서 더 느려질 수 도 있다.
  • 구현 클래스마다 테이블 전략 ( 사용하면 안됨 )
    • 장점
      • 테이블 하나하나로 조회할 땐 효과적, not null 제약조건 가능
        • 단점
      • 자식 테이블 관리가 힘들다, 쿼리가 복잡해진다. 유지보수성이 어렵다

@MappedSuperclass

실제 상속이나 DB는 서로 관계가 없지만, 공통으로 들어가는 컬럼을 추가할 때 사용
ex) 등록, 수정, 삭제,수정일, 수정자, 등등이 모든 테이블에 필요하다는 조건이 있을 경우

상속 관계 매핑이 아님, 매핑 정보만 제공 하기 때문에 BaseEntity 조회나 검색이 안됨 (find 사용 불가능), 그래서 추상 클래스로 사용을 권장, 그래야 사용 시점에 해당 클래스를 조작하지 않고 구현체를 조작하기 때문에

@MappedSuperclass
public abstract class BaseEntity{
    @Column(name ="createMemberId")
    private String createdBy;
    private LocalDateTime createdDate;
    private String lastModifiedBy;
    private LocalDateTime lastModifiedDate;

    // 필요하면 getter // setter 추가
}

// 각각의 클래스에 BaseEntity를 상속받는다.
// 단독으로 저장할 필요가 없을 경우 추상 클래스로 구현하는것을 고려!! Itemp이든 BaseEntity처럼 말이다.
정리
  • 상속관계의 매핑이 아니다.
  • 엔티티처럼 @Column등 사용 가능하지만, 엔티티가 아니고, 테이블과 매핑이 되지 않는다.
  • 부모 클래스를 상속 받는 자식 클래스에게 매핑 정보만 제공
  • 조회나, 검색 등 불가, 같은 이유로 직접 사용할 이유가 없기 때문에 추상 클래스로 선언 권장
반응형
Comments