1. 개요
김영한 님의 '자바 ORM 표준 JPA 프로그래밍 - 기본편' 을 들으면서 정리하는 포스팅입니다.
https://www.inflearn.com/course/ORM-JPA-Basic
자바 ORM 표준 JPA 프로그래밍 - 기본편 강의 | 김영한 - 인프런
김영한 | JPA를 처음 접하거나, 실무에서 JPA를 사용하지만 기본 이론이 부족하신 분들이 JPA의 기본 이론을 탄탄하게 학습해서 초보자도 실무에서 자신있게 JPA를 사용할 수 있습니다., 실무에서도
www.inflearn.com
2. DB 슈퍼-서브 타입 관계 모델링
객체지향에서 상속 관계를 사용하면 공통된 필드를 슈퍼 타입에 정의하여 코드의 중복을 줄일 수 있다는 장점이 있다.
하지만, 관계형 데이터베이스는 상속 관계를 직접적으로 지원하지 않는다.(지원하는 DB도 있음.)
관계형 데이터베이스에서는 상속 구조와 유사한 슈퍼-서브 타입 관계의 모델링 기법이 있다.
관계형 데이터베이스에서 슈퍼-서브 타입 관계는 공통된 속성을 가진 엔티티를 슈퍼타입으로 정의하고, 이를 상속 받는 서브 타입을 별도로 정의하는 방식으로 모델링 할 수 있다. 이를 통해 데이터 구조의 논리적 표현이 명확해지고, 중복 데이터를 줄일 수 있다.
예를 들어 앨범, 영화, 책을 표현하기 위해 각각의 테이블을 생성해서 모델링을 할 수 있다.
하지만, 이들 간에는 공통된 속성(name, price)이 존재한다.
위 상황에서는 어떤식으로 풀어나갈지는 상황에 따라 유연하게 대처할 필요가 있다.
이를 해결하기 위한 슈퍼-서브 타입 관계의 3가지 전략에 대해서 알아보자.
3. 슈퍼-서브 타입 전략 설정 @Inheritance
JPA에서는 @Inheritance 어노테이션을 사용하여 슈퍼-서브 타입 관계를 정의할 수 있다.
이를 통해 데이터베이스 슈퍼-서브 타입 모델링 구조를 구현할 수 잇다.
a. joined 전략(조인 전략)
a-1. Joined 전략 특징
- 슈퍼타입과 서브타입 각각에 대해 별도의 테이블을 생성한다.
- 데이터 중복을 최소화 한 정규화된 방식이다.
- 슈퍼타입 관점과 서브타입 관점에서 각각 유연하게 조회할 수 있다.
- 조회시 조인을 많이 사용하므로, 성능이 저하될 수 있다.
- 데이터 저장시 Insert가 두번 호출된다.(Item, Alume 각각 Insert)
a-2. Joined 전략 예제
@DiscriminatorColumn // DTYPE(자식 타입을 구분하는 컬럼 생성)
@Inheritance(strategy = InheritanceType.JOINED) // DB 슈퍼타입, 서브타입 전략 설정
@Entity
public class Item {
@Id
@GeneratedValue
private Long id;
private String name;
private int price;
// Getter, Setter
}
@DiscriminatorValue("Album") // 슈퍼 타입을 DTYPE 컬럼의 Value를 지정(defaut: Entity 클래스 이름)
@Entity
public class Album extends Item {
private String artist;
// Getter, Setter
}
@DiscriminatorValue("Book") // 슈퍼 타입을 DTYPE 컬럼의 Value를 지정(defaut: Entity 클래스 이름)
@Entity
public class Book extends Item {
private String author;
private String isbn;
// Getter, Setter
}
DDL 결과
- Item(슈퍼타입) 테이블과 Album, Book(서브타입) 테이블을 생성한다.
- 슈퍼타입과 서브타입을 FK(외래키)로 맺어준다.
a-3. Joined 전략에서 Insert - Select 예제
Movie movie = new Movie();
movie.setDirector("홍길동");
movie.setActor("강동원");
movie.setName("바람과함께사라지다");
movie.setPrice(10000);
em.persist(movie);
em.flush();
em.clear();
Movie findMovie = em.find(Movie.class, movie.getId());
System.out.println("findMovie = " + findMovie);
Insert-Select 결과
- Movie(서브타입)를 Insert 할 때 Item(슈퍼타입)의 Insert 쿼리도 발생한다.
- 조회시 Join을 사용한다.
b. single table전략 (단일 테이블 전략)
b-1. single table 전략 특징
- 모든 데이터를 하나의 테이블에 저장하고, 타입 컬럼을 추가하여 레코드의 유형을 구분한다.
- 하나의 테이블로 관리가 간단함.
- Join이 필요하지 않아 성능적으로 유리할 수 있음.
- NULL 데이터가 많아짐.(서브 타입의 엔티티 컬럼은 모두 NULL 허용)
- 서브타입이 늘어나면 테이블이 너무 커질 수 있다.(오히려 성능 저하가 발생)
b-2. single table 전략 예시
@DiscriminatorColumn // DTYPE(자식 타입을 구분하는 컬럼 생성) 싱글 테이블 전략에서 생략 가능
@Inheritance(strategy = InheritanceType.SINGLE_TABLE) // DB 슈퍼타입, 서브타입 전략 설정
@Entity
public class Item {
@Id
@GeneratedValue
private Long id;
private String name;
private int price;
// Getter, Setter
}
@DiscriminatorValue("Album") // 슈퍼 타입을 DTYPE 컬럼의 Value를 지정(defaut: Entity 클래스 이름)
@Entity
public class Album extends Item {
private String artist;
// Getter, Setter
}
@DiscriminatorValue("Book") // 슈퍼 타입을 DTYPE 컬럼의 Value를 지정(defaut: Entity 클래스 이름)
@Entity
public class Book extends Item {
private String author;
private String isbn;
// Getter, Setter
}
DDL 결과
- 서브타입의 모들 속성들을 Item(슈퍼타입)에 저장한다.
Insert - Select 결과
- Joined 전략과 다르게 한번만 Insert 한다는 점과 조회할 때 join을 사용하지 않는다.
c. table per class 전략(구현 클래스마다 테이블)
c-1. table per class 전략 특징
- 슈퍼 타입 테이블 없이 서브 타입마다 개별 테이블을 생성함.
- 각 테이블은 독립적
- 공통된 속성에 대한 중복 정의가 발생함.
- 슈퍼 타입 관점에서 조회하면 UNION 쿼리가 발생함.
- 자식 테이블을 통합해서 쿼리하기 어려움.
c-2. table per class 전략 예시
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS) // DB 슈퍼타입, 서브타입 관계
@Entity
public abstract class Item { // 추상클래스
@Id
@GeneratedValue
private Long id;
private String name;
private int price;
// Getter, Setter
}
DDL 결과
- Item(슈퍼타입) 테이블을 생성하지 않고, 각각의 서브타입 테이블을 생성한다.
c-3. table per class 전략 조회쿼리 예시
Movie movie = new Movie();
movie.setDirector("aaaa");
movie.setActor("bbbbb");
movie.setName("바람과함께사라지다");
movie.setPrice(10000);
em.persist(movie);
em.flush();
em.clear();
Item findItem = em.find(Item.class, movie.getId());
System.out.println("findItem = " + findItem);
조회 결과
- UNION 쿼리를 사용하면서, 모든 서브 타입을 탐색한다.(비효율적)
'Programming > Java' 카테고리의 다른 글
[JPA] 값 타입 정리(기본값, 임베디드, 컬렉션) (0) | 2025.01.02 |
---|---|
[JPA] CASECADE(영속성 전이) 가 무엇인가? (0) | 2024.12.23 |
[JPA] Entity 연관 관계 매핑(단방향/양방향, 연관관계주인, 다중성) (0) | 2024.12.13 |
[JPA] JPA에서 PK를 다루는 방법(@Id, @GeneratedValue) (0) | 2024.12.12 |
[JPA] Entity 매핑과 DDL 자동 생성 옵션 정리 (0) | 2024.12.12 |