✏️ JPA 고급 매핑

박상민·2023년 10월 3일
0

JPA

목록 보기
11/24
post-thumbnail

객체는 상속관계가 있습니다.
그런데 관계형 데이터베이스에는 상속관계와 비슷하게 지원하는 기능이 있긴 하지만 객체의 상속관계랑은 조금 다르다.

⭐️ 상속관계 매핑(테이블 설계)

  • RDB는 상속 관계가 없다.
  • 그런데 슈퍼타입 서브타입 관계라는 모델링 기법이 객체 상속과 유사
  • 상속관계 매핑: 객체의 상속과 구조와 DB의 슈퍼타입 서브타입 관계를 매핑

관계형 데이터베이스에는 객체지향 언어에서의 상속 개념이 없다.
대신 관계형 데이터베이스에서는 슈퍼타입 서브타입 관계라는 모델링 기법이 상속과 유사하다.

상속관계 매핑

  • 슈퍼타입 서브타입 논리 모델을 실제 물리 모델로 구현하는 방법
    - 조인 전략: 각각 테이블을 만들고 조회할때 조인한다.
    - 통합 테이블로 변환: 단일 테이블을 사용해서 통합한다. JPA에서 단일 테이블 전략이라고도 말한다.
    - 서브타입 테이블로 변환: 서브 타입마다 하나의 테이블로 만든다. JPA에서 구현 클래스마다 테이블 전략이라고 한다.

객체의 상속구조와 DB의 슈퍼타입, 서브타입 관계를 맵핑하는 것

위의 사진처럼 물리 모델로 구현을 할 수가 있는데 첫번째가 조인 전략이라는 방식이다.

📌 Join 전략

엔티티마다 모두 테이블로 만들어주고 부모의 기본키를 기본키 + 외래키로 사용한다. 때문에 조회할 때 조인을 사용한다.
다만 객체는 타입이 있는데 테이블은 타입에 개념이 없으니 따로 DTYPE 등의 컬럼을 추가해야한다.

// Item 엔티티
@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;
	
	...
}

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

// Movie 엔티티
@Entity
@DiscriminatorValue("M")
public class Movie extends Item{
	private String director;
	private String actor;
	...
}
  1. Item 엔티티에서 상속 매핑을 한다고 지정하고(@Inheritance) 전략으로 조인전략을 사용한다고 지정한다.(Inheritance.JOINED)
  2. 그리고 구분 부모에서 구분 컬럼을 지정한다.
  3. 부모 테이블에 DTYPE 이름으로 컬럼이 생성된다.
  4. 자식들은 구분 칼럼에 어떻게 저장되는지 작성한다. Album 엔티티는 부모에게 DTYPE에 A라는 값으로 저장된다.
  5. Album 데이터를 추가하면 그 앨범의 이름, 가격 등은 Item 테이블에 들어가고 artist 등의 정보는 앨범 테이블에 들어가게 하는 것이다.
  6. 아이템과 앨범에 따로 데이터를 넣기 때문에 insert 두 번을 하고, PK, FK로 조인을 해서 데이터를 가지고 온다.

장점

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

단점

  • 조회할 대 조인이 많아 성능 저하
  • 조회 쿼리가 복잡
  • 테이블을 등록할 때 Insert SQL을 두 번 실행

특징

  • JPA 표준 명세는 구분 컬럼을 사용하도록 하지만 하이버네이트를 포함한 몇 구현체는 구분 칼럼없이도 동작함.
  • 관련 어노테이션
    @PrimaryKeyJoinColumn, @DiscriminatorColumn, @DiscriminatorValue

📌 단일 테이블 전략

단일 테이블 전략은 논리 모델을 한 테이블로 다 합쳐버리는 것이다.
앨범, 영화, 책의 컬럼 들을 모두 ITEM 테이블에 넣어버리는 것이다. 이때 구분 컬럼(DTYPE)을 추가해 어떤 자식 데이터가 저장되어 있는지 구분 가능하다.

EX) DTYPE로 컬럼명을 놓고 테이블의 데이터가 앨범, 영화, 책 중 어디에 해당하는 데이터인지 나타내준다.

// Item 엔티티
@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DisciminatorColumn(name = "DTYPE")
...

// 자식 엔티티
@Entity
@DiscriminatorValue("A")
...

@Entity
@DiscriminatorValue("M")
...

조인전략과 형식은 비슷한데 Inheritance.SINGLE_TABLE을 사용한다. 또한 모든 테이블이 하나로 통합되어있기 때문에 구분 컬럼은 필수다.

장점

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

단점

  • 자식 엔티티가 매핑한 컬럼은 모두 null 허용
  • 단일 테이블에 모든 것을 저장하므로 테이블이 커질 수 있다. 상황에 따라서 조회 성능이 오히려 느려질 수 있다.
    특징
  • 구분 컬럼이 필수다.(@DiscriminatorColumn)
  • 구분 컬럼 값(@DiscriminatorValue)을 지정하지 않으면 엔티티 이름을 그대로 사용한다.(Move, Album)

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

구현 클래스마다 테이블 전략은 자식 테이블이 부모 테이블의 모든 것을 다 가지고 있는 형태다. 그래서 자식 엔티티마다 테이블을 다 만들어 준다.

구현 클래스마다 테이블 전략은 조인 전략과 비슷해보이지만 다르다.
앨범, 무비, 북 테이블이 Name, Price 정보를 각각 다 들고 있는 것이다.
데이터가 중복되긴 하지만 이런 방법도 존재한다. DB 입장에서는 어떤 방법으로 구현하더라도 JPA에서는 다 맵핑이 가능하다.

// Item 엔티티
@Entity
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
...

// 자식 엔티티
@Entity
@DiscriminatorValue("A")
...

@Entity
@DiscriminatorValue("M")
...

InheritanceType.TABLE_PER_CLASS 속성을 지정해주면 된다.

그러면 구현 클래스마다 테이블 전략을 사용한다. 일반적으로 사용하지 않는다.

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

장점

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

단점

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

특징

  • 구분 컬럼을 사용하지 않음.

⭐️ @MappedSuperclass

위 방법들은 모두 부모 자식 클래스 모두 데이터베이스 테이블과 매핑했는데,
부모 클래스는 테이블과 매핑하지 않고 자식 클래스에게 매핑 정보만 제공하고 싶을때 @MappedSuperclass를 사용한다.
@MappedSuperclass는 추상 클래스와 비슷한데 @Entity는 실제 테이블과 매핑되지만 이는 그렇지 않다. 단순히 매핑 정보를 상속할 목적으로만 사용된다.

  • 상속관계 매핑X
  • 엔티티X, 테이블과 매핑X
  • 부모 클래스를 상속 받는 자식 클래스에 매핑 정보만 제공
  • 조회, 검색 불가(em.find(BaseEntity) 불가)
  • 직접 생성해서 사용할 일이 없으므로 추상 클래스 권장


Member 테이블과 Seller 테이블에 공통 속성을 부모 클래스로 모으고 객체 상속 관계로 나타낼수 있다.

// BaseEntity
@MappedSuperclass
public abstract class BaseEntity{
	@Id @GeneratedValue
	private Long id;
	
	private String name;
	...
}

// Member 엔티티
@Entity
public class Member exteds BaseEntity{
	// ID, NAME 상속
	private String email;
	...
}

// Seller 엔티티
@Entity
public class Seller exteds BaseEntity{
	// ID, NAME 상속
	private String shopName;
	...
}

BaseEntity에서 객체들의 공통 매핑 정보를 정의하였고 자식 엔티티들이 이를 상속해 매핑 정보를 받았다. 여기서 BaseEntity는 테이블과 매핑할 필요가 없고 자식 엔티티에게 공통 매핑 정보만 제공한다.

@AttributeOverrides를 사용해 부모로부터 받은 매핑 정보를 재정의할 수 있고

@AssociationOverrides를 사용해 연관관계를 재정의 할수도 있다.

@MappedSupperclass 특징

  • 테이블과 관계 없고, 단순히 엔티티가 공통으로 사용하는 매핑 정보를 모으는 역할
  • 테이블과 매핑되지 않고 자식 클래스에 엔티티 매핑 정보를 상속하기 위해 사용.
  • @MappedSupperclass로 지정한 클래스는 엔티티가 아니기에 영속성을 가지지 못함.
  • 거의 사용하지 않고 추상 클래스로 만들어서 사용하는것이 일반적.

출처
자바 ORM 표준 JPA 프로그래밍 강의
게시글 속 자료는 모두 위 강의 속 자료를 사용했습니다.

profile
스프링 백엔드를 공부중인 대학생입니다!

0개의 댓글