7. 상속관계 매핑
2025. 6. 16. 18:05ㆍSpring/JPA
1. 상속관계 매핑
1) 개념
2) 구현 전략 3가지
3) 주요 어노테이션
2. 상속 전략별 특징
1) 조인 전략 (JOINED)
2) 단일 테이블 전략 (SINGLE_TABLE)
3) 구현 클래스마다 테이블 전략 (TABLE_PER_CLASS)
3. @MappedSuperclass
1) 개념 및 역할
2) 사용 목적
3) 참고 사항
1. 상속관계 매핑
1) 개념
- 관계형 데이터베이스는 객체의 상속 개념이 없음.
- 대신 슈퍼타입-서브타입 관계라는 모델링 기법을 통해 객체 상속과 유사하게 표현.
- JPA에서는 객체의 상속 구조를 DB의 슈퍼타입-서브타입 관계로 매핑하는 것을 상속관계 매핑이라 함
- 설정해놓은 상속관계 매핑 구현 전략에 따라 JPA가 알아서 테이블 생성 및 데이터 추가, 수정, 삭제 쿼리를 작성해준다.
2) 구현 전략 3가지
- 1. 각각 테이블로 변환 → 조인 전략 (JOINED)
- 2. 단일 테이블 전략 (SINGLE_TABLE)
- 3. 구현 클래스마다 테이블 생성 전략 (TABLE_PER_CLASS)
3) 주요 어노테이션
- @Inheritance(strategy = InheritanceType.XXX)
- JOINED: 조인 전략
- SINGLE_TABLE: 단일 테이블 전략
- TABLE_PER_CLASS: 구현 클래스마다 테이블 전략
- @DiscriminatorColumn(name = "DTYPE"): 부모 테이블에 자식 타입을 구분하는 컬럼 지정 (기본 컬럼명 DTYPE)
- @DiscriminatorValue("XXX"): 자식 클래스가 어떤 구분 값으로 들어갈지 지정 (클래스명이 디폴트)
2. 상속 전략별 특징
@Entity
@Inheritance(strategy = InheritanceType.XXX) // 전략 지정
@DiscriminatorColumn(name = "DTYPE") // 구분 컬럼
public abstract class Item {
@Id
@GeneratedValue
private Long id;
private String name;
private int price;
}
@Entity
@DiscriminatorValue("ALBUM")
public class Album extends Item {
private String artist;
}
@Entity
@DiscriminatorValue("MOVIE")
public class Movie extends Item {
private String director;
private String actor;
}
@Entity
@DiscriminatorValue("BOOK")
public class Book extends Item {
private String author;
private String isbn;
}
1) 조인 전략 (JOINED)
@Entity
public class AppRunner implements CommandLineRunner {
@PersistenceContext
EntityManager em;
@Override
@Transactional
public void run(String... args) {
Album album = new Album();
album.setName("Greatest Hits");
album.setPrice(15000);
album.setArtist("Queen");
em.persist(album); // ITEM과 ALBUM 테이블에 INSERT 발생
//insert into ITEM (NAME, PRICE, DTYPE) values ('Greatest Hits', 15000, 'ALBUM');
//insert into ALBUM (ITEM_ID, ARTIST) values (1, 'Queen');
Movie movie = new Movie();
movie.setName("Inception");
movie.setPrice(12000);
movie.setDirector("Christopher Nolan");
movie.setActor("Leonardo DiCaprio");
em.persist(movie); // ITEM과 MOVIE 테이블에 INSERT 발생
}
}
- @Inheritance(strategy = InheritanceType.JOINED)
- 부모 테이블과 자식 테이블을 조인해서 하나의 엔티티로 조회함.
- 조회할 때는 SELECT + JOIN, 저장할 때는 INSERT가 부모, 자식 각각 발생
- 장점: 테이블 정규화, 외래 키 참조 무결성 제약조건 활용 가능, 저장 공간 효율적 사용 가능
- 단점: 조회 시 조인 많이 발생 → 성능 저하, 데이터 저장 시 INSERT SQL 2번 호출 필요 (부모, 자식 각각)
- 정리하면, 자식이 많고 조회가 빈번하다면 성능 고려 필요. 하지만 정규화 구조라 DB 설계가 깔끔하고 확장성에 좋음.
2) 단일 테이블 전략 (SINGLE_TABLE)
Album album = new Album();
album.setName("Greatest Hits");
album.setPrice(15000);
album.setArtist("Queen");
em.persist(album); //실행쿼리는 한 줄
//insert into ITEM (NAME, PRICE, ARTIST, DTYPE)
//values ('Greatest Hits', 15000, 'Queen', 'ALBUM');
- @Inheritance(strategy = InheritanceType.SINGLE_TABLE)
- 테이블 1개에 모든 데이터 저장하는 전략으로 실무에서 가장 많이 쓰인다.
- 장점: 조인 없어서 조회 성능이 빠르고, 쿼리가 단순
- 단점: 자식 엔티티의 컬럼은 모두 null을 허용해서 null 컬럼 많아짐, 테이블 커져서 오히려 조회 느려지기도 함
- 가장 큰 단점은 데이터 정규화가 깨지는 것이다
- 데이터 정규화는 DB 설계 시, 중복을 최소화하고 데이터의 일관성과 무결성을 유지하기 위해 각 데이터는 자신에게 필요한 컬럼만 가지게끔 테이블을 나누는 원칙. (데이터를 쪼개서 구조화하는 원칙)
- 그런데 단일 테이블 전략은 모든 자식 클래스의 필드를 하나의 테이블에 몰아넣으면서 null이 많고 불필요한 컬럼이 생김
- 따라서 조인은 없어서 성능은 좋을 수 있지만 데이터 구조가 깔끔하지 않고 유지보수가 어려워진다.
3) 구현 클래스마다 테이블 전략 (TABLE_PER_CLASS)
- 추천하지 않음 (DB 설계자와 ORM 전문가 모두 추천X)
- 장점: 서브 타입을 명확히 구분해서 처리할 때 효과적, not null 제약조건 각각 사용 가능
- 단점: 여러 자식 테이블 조회 시 성능 저하 (UNION SQL 필요), 자식 테이블 통합 조회 어려움
3. @MappedSuperclass
1) 개념 및 역할
- 공통 필드만 정의해서 자식 엔티티들에게 매핑 정보를 물려주는 클래스
- 상속관계 매핑이 아님!
- @Entity처럼 엔티티로 인식되지 않음. JPA가 관리하는 대상 X
- 테이블도 생성되지 않음
- 단순히 필드와 매핑 정보만 자식에게 상속하는 역할
- 조회, 검색 불가능 (em.find(BaseEntity) 사용 불가)
- 직접 사용하지 않으므로 추상 클래스를 권장한다
2) 사용 목적
- 등록일, 수정일, 등록자, 수정자 등 전체 엔티티에 공통으로 필요한 필드 정의 시 사용
- 테이블과는 관계없이 공통 속성만 상속시키는 용도
@MappedSuperclass //엔티티에 필요한 공통 필드 정의
public abstract class BaseEntity {
private LocalDateTime createdDate;
private LocalDateTime lastModifiedDate;
private String createdBy;
private String lastModifiedBy;
}
@Entity
public class Member extends BaseEntity {
//Member 클래스는 BaseEntity에 있던 필드들도 매핑되어서
//MEMBER 테이블에 해당 컬럼들이 생성됨!
@Id
@GeneratedValue
private Long id;
private String name;
}
3) 참고 사항
- @Entity 클래스는 @Entity 또는 @MappedSuperclass로 선언된 클래스만 상속 가능
- @Entity랑 같이 쓰면 안 됨! @MappedSuperclass는 테이블이 없어야 하니까 @Entity는 빼야 한다.
- 상속은 되지만 연관관계 매핑은 안 됨
- @OneToMany, @ManyToOne 같은 연관관계 어노테이션은 상속해도 적용 안 되고
- 단순히 @Column, @Id 같은 필드 매핑 정보만 상속됨
- 조회 대상이 아님
- em.find(BaseEntity.class, id) 이런 건 절대 불가
- 왜냐하면 테이블이 없기 때문에 쿼리를 날릴 수 없음!