객체에는 상속관계가 있지만 관계형 데이터베이스는 상속 관계가 없다 .
슈퍼타입 서브타입 관계라는 모델링 기법이 객체 상속과 유사한 편이다.
상속관계 매핑 : 객체의 상속과 구조와 DB의 슈퍼타입 서브타입 관계를 매핑하는 것이다 .
DB의 super type sub type 논리 모델을 물리모델로 구현하는 방법은 3가지이다.
1. JOIN 전략
아이템 테이블과 ALBUM , MOVIE , BOOK 테이블을 각각 조인 하여 가져오는 전략이다 . 전략들 중 가장 정규화 되어 있고 JPA와 가장 유사한 모델이다.
ALBUM 을 생성한다고 하면 공통 정보는 ITEM에 저장하고 ALBUM 정보는 ALBUM에 저장한다 .
album , item 이 따로 2번의 insert 가 일어나고 조회시에는 ALBUM과 ITEM의 item_id 를 조인하여 데이터를 가져온다.
ALBUM , MOVIE , BOOK 구분을 위해 아이템 테이블에 DTYPE 컬럼을 넣어준다.
구현방법
부모 엔티티에 @Inheritance로 전략을 JOINED로 설정해준다.
@Entity
@Inheritance(strategy = InheritanceType.JOINED)
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class Item {
@Id @GeneratedValue
private Long id;
private String name;
private int price;
}
@Entity
@Getter
@Setter
@ToString // 사용하지 않는 것이 좋다.
@NoArgsConstructor
@AllArgsConstructor
public class Movie extends Item{
private String director;
private String actor;
}
자식 엔티티에서 아이디 값을 따로 정하지 않아도. 부모의 아이디 값이 들어간다 . DDL 은 아래와 같이 나온다.
데이터를 추가해 봤다 .
//movie 저장
Movie movie = new Movie();
movie.setName("movie1");
movie.setPrice(10000);
movie.setDirector("directorA");
movie.setActor("actorA");
em.persist(movie);
em.flush() // 지금까지 생성된 쿼리를 DB로날리고
em.clear() // 영속성 컨텍스트 초기화
//movie 조회
Movie movie = em.find(Movie.class , movie.getId());
//movie에는 lombock으로 ToString을 만들어 놨다 (테스트용)
System.out.println(movie)
실행시켜 쿼리와 실제 DB에 들어가는 값을 보자
아이템과 무비의 insert가 각각 한번씩 나갔고
조회 쿼리는 둘을 join 해서 가져왔다.
item에는 toString 이없어서 movie1에는 item의 필드가 안찍혔다 .
H2 DB에 들어간 값을 확인 해보자
item_id 가 같다 item_id는 item에서는 PK movie 에서 PK,FK 역할을 한다.
부모테이블인 Item에 @DiscriminatorColumn을 추가해 주면 DTYPE을 줄 수 있다 . DTYPE 이 없어도 조인은 되지만
DB에서 봤을떄는 DTYPE이 있어야 구분이 가능하니 넣어주는게 좋다 .
자식클래스에서 @DiscriminatorValue(value = "M") 으로 value를 지정해주면 DTYPE에 어떤 이름으로 들어갈지 이름을 정해 줄 수 있다.
2.싱글 테이블 전략
그냥 한 테이블에 다 때려 넣고 DYPE으로 구분한다. JPA 기본전략이다.
부모타입에 @Inheritance(strategy = InheritanceType.SINGLE_TABLE) 지정해준다
쿼리를 보자
한방에 다들어갔다 .
조인할 필요가 없고 인서트도 한번에 되기 때문에 성능상의 이점이 있다 .
싱글 테이블 전략에서는 @DiscriminatorColumn 을 지정하지 않아도 자동으로 들어간다 DTYPE이 없으면 데이터를 구분할 수가 없기때문에 강제 되어있다.
3. 공통 적인 것을 구현클래스마다 넣어두는 전략
부모클래스를 @Inheritance(strategy = InheritanceType.TABLE_PER_CLASS) 로 지정해 주면 된다 각각 테이블에 저장된 것이기 떄문에. @DiscriminatorColumn 이 의미가 없다 . .
부모타입인 item 타입으로 조회한다 했을때 id 값만 알고 있다고 한다면, 아래 자식타입의 모든 테이블을 다뒤져 봐야할 것이다 .
명확하게 뭐가 뭔지 모를 경우가 아니면 사용하지 않는 것이 좋다.
각 전략의 장단점 정리
장점 | 단점 | |
JOIN 전략 | - 테이블 정규화 - 외래 키 참조로 무결성 제약조건 활용 가능 - 외부 테이블에서 item을 조회해야 할 때 item만 조회하면 된다 . - 저장공간 효율화 - 정석이라 생각하면 된다. |
-조회시 조인을 많이 사용 (성능 저하) -조회 쿼리가 복잡함 -데이터 저장시 insert 2번 호출 -복잡 |
싱글 테이블 전략 | -조인이 필요없다 조회 성능 빠르다. - 조회 쿼리 단순 |
-자식 엔티티가 전달한 컬럼 뺴고는 다 널을 허용해줘야 한다. 데이터 무결성 입장에서 취약 -테이블이 커질 수 있고 상황에 따라서 조회 성능이 오히려 느려질 수 있다. |
각각 저장 | -서브 타입을 명확하게 구분해서 처리할 때 효과적이다 . -각각 구분되기 때문에 notNull제약조건 사용 가능 |
-쓰면 안되는 것 -자식 테이블을 통합해서 쿼리하기 어렵다. -여러 자식 테이블을 함꼐 조회할 때 성능 느림 |
3가지 방법을 알아봤다 . 사실 Item 테이블은 그 자체로 의미가 있는 테이블이 아니기 떄문에 클래스를 abstract로 구현하는 것이 좋다고 한다.
위에 3가지 전략으로 테이블이 구현됬지만 객체입장에서는 ?
상속을 지원하기 떄문에 다 똑같다 때문에 코드를 따로 변경할 필요 없이 @Inheritance 만 각각 지정해도 각 전략에 맞춰서 맵핑이된다. JPA 의 큰 장점이다. Join전략으로 설계를 했다가 성능이 안나와서 바꾼다고 했을때 @Inheritance 만 바꿔주면 된다.
'JPA' 카테고리의 다른 글
JPA 공부 9 - 즉시로딩과 지연로딩 (0) | 2021.06.05 |
---|---|
JPA 공부 8 - Proxy (0) | 2021.06.05 |
JPA 공부 6 - 다대다 연관관계(N:N) (0) | 2021.06.04 |
JPA 공부 5 - 일대일 연관관계(1:1) (0) | 2021.06.04 |
JPA 공부 4 - 일대다 단방향,양방향(1:N) (비추) (0) | 2021.06.04 |