[JPA] 상속관계 매핑 :: 잡다한 프로그래밍
반응형

JPA의 상속 매핑 전략

JPA에서는 객체지향 언어에서 제공하는 상속(Inheritance) 개념을 관계형 데이터베이스 위에서도 그대로 표현할 수 있도록 상속 매핑 전략을 제공합니다.

 

하지만 DB는 상속 개념이 없기 때문에, 이를 다음의 세 가지 전략으로 풀어냅니다.

 

1. JOINED 전략 (@Inheritance(strategy = JOINED))

가장 정규화된 방식

 

  • 공통 속성은 Item 테이블에 저장하고
  • 개별 속성은 Album, Book, Movie 테이블에 저장
  • 각 자식 테이블은 ITEM_ID를 외래키로 가지며
  • 조회 시 JOIN으로 연결
  • ITEM 입장에서 어떤 타입의 데이터인지 알 수있게 DTYPE으로 구분할 수 있음 (넣는것을 권장)

 

@Entity
@Ingeritance(strategy = IngeritanceType.JOINED) // 해당 값이 없으면 기본 한테이블에 다 넣는 2번방식
@DiscriminatorColumn // 해당 값이 있으면 DTYPE이 생김
public abstract class Item { // 추상클래스로 안하면 > ITEM을 독단적으로 쓰는 경우가 있다고보고 생성하는것 추상클래스로 만들자
	@Id @GeneratedValue
    private Long id;
    
    private String name;
    private int price;
}
@Entity
// @DiscriminatorValue("A") 값으로 DTYPE값을 변경 가능
public class Album extends Item {
	private String artist;
}
@Entity
pulbic class Moview extends Item {
	private String director;
    private String actor;
}

장점

  • 테이블이 정규화되어 있어 무결성 보장
  • NOT NULL, FK, 유니크 제약조건 사용 가능
  • 저장공간 효율화

단점

  • JOIN이 많아져 조회 성능 저하 가능 (조회 쿼리가 복잡)
  • INSERT 시 부모 테이블과 자식 테이블에 2번 INSERT

 

2. 단일 테이블 전략 (@Inheritance(strategy = SINGLE_TABLE))

모든 데이터를 한 테이블에 넣는 방식 (주로 프로젝트가 단순하거나, 복잡한 테이블 구조가 필요없을 때 사용)

 

  • DTYPE으로 구분
  • 공통 컬럼 + 각 타입별 컬럼 모두 포함됨
  • 관련 없는 데이터는 NULL 처리됨

 

@Entity
@Ingeritance(strategy = IngeritanceType.SINGLE_TABLE) //혹은 생략
@DiscriminatorColumn // 해당 값이 없어도 무조건 생김 = DTYPE이 없으면 누가 누군지 몰라서 필수임
public abstract class Item {
	@Id @GeneratedValue
    private Long id;
    
    private String name;
    private int price;
}

장점

  • 조인이 없기 때문에 조회 성능 우수
  • 쿼리 단순함

단점

  • null 칼럼 다수 발생
  • 테이블이 점점 넓어짐 (column 수 증가) → 인덱싱 비효율
  • 구조상 제약 조건 사용 제한

3. 구현 클래스마다 테이블 전략 (@Inheritance(strategy = TABLE_PER_CLASS))

자식 클래스마다 완전한 테이블을 따로 생성 (비추천)

@Entity
@Ingeritance(strategy = IngeritanceType.TABLE_PER_CLASS)
public abstract class Item {
	@Id @GeneratedValue
    private Long id;
    
    private String name;
    private int price;
}

※ 추상클래스를 사용하지 않으면, ITEM 테이블이 단독으로 생길수 있으니 주의!

 

이 전략은 절대 실무에서 쓰지 말자

  • 부모 타입으로 조회하면 UNION ALL 쿼리 발생
    → SELECT * FROM album UNION ALL SELECT * FROM movie ...
Item item = em.find(Item.class, move.getId());
System.out.println("Item = " + item);
  • DTYPE 사용하지 않음

장점

  • 서브타입 명확히 분리
  • 각 테이블 독립적 → NOT NULL, 유니크 키 사용 가능

단점

  • 통합 조회 어렵고 성능 저하
  • 변경 시 확장성, 유지보수 최악 (테이블 변경시 비즈니스 로직 변경되어야함)
반응형

+ Recent posts