"인프런 - 자바 ORM 표준 JPA 프로그래밍 강의를 듣고 작성한 글 입니다."
www.inflearn.com/course/ORM-JPA-Basic#
자바 ORM 표준 JPA 프로그래밍 - 기본편 - 인프런
JPA를 처음 접하거나, 실무에서 JPA를 사용하지만 기본 이론이 부족하신 분들이 JPA의 기본 이론을 탄탄하게 학습해서 초보자도 실무에서 자신있게 JPA를 사용할 수 있습니다. 초급 웹 개발 프로그
www.inflearn.com
구현 클래스마다 테이블을 생성하는 전략
Item 과 같은 슈퍼 타입의 테이블 없이 각 도메인 별로 각각 테이블을 만들어서 데이터를 관리하는 전략이다.
- name, price 와 같이 중복된 이름의 필드명을 허용한다.
- @Inheritance(strategy = InheritanceType.TABLE_PER_CLASS) 와 같이 속성값에서 TABLE_PER_CLASS 전략을 지정해준다.
- 이후 Item 과 같은 슈퍼 타입의 클래스를 abstract 키워드를 통해 추상 클래스로 만들면 애플리케이션 실행 때 Item 테이블이 생성되지 않고, 서브 타입 도메인 클래스들 각각의 테이블들 만 생성되는 것을 확인할 수 있다.
- 구현 클래스마다 테이블 을 생성하는 전략의 논리 모델과 테이블
* 이렇게 전략을 생성할 경우 데이터는 어떻게 삽입될까?
- 삽입된 데이터의 타입을 JPA 에서 자체적으로 분류해준다.
- 이후 분류된 테이블에 맞게 데이터를 삽입해준다.(해당되는 테이블에만 삽입하면 되므로 insert 문이 한번만 실행된다.)
* 조회
- 데이터를 조회할 경우 다른 테이블에 조인을 하거나 where 조건에서 DTYPE 과 같은 분류 필드를 판별할 필요 없이 곧바로 select 문을 실행해 줄 수 있다.
- 따라서 이 전략의 경우 @DiscriminatorColumn 어노테이션은 의미가 없기 때문에 사용되지 않는다.(굳이 테이블마다 직접 구분해줄 필요가 없기 때문)
- Item.java
@Entity
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class Item {
@Id @GeneratedValue
private Long id;
private String name;
private int price;
// Getter, Setter
}
- JpaMain.java
Movie movie = new Movie();
movie.setDirector("aaaa");
movie.setActor("bbbb");
movie.setName("인터스텔라");
movie.setPrice(10000);
em.persist(movie);
em.flush();
em.clear();
Movie findMovie = em.find(Movie.class, movie.getId());
- hibernate SQL(insert, select)
Hibernate:
/* insert hellojpa.Movie
*/ insert
into
Movie
(name, price, actor, director, id)
values
(?, ?, ?, ?, ?)
Hibernate:
select
movie0_.id as id1_2_0_,
movie0_.name as name2_2_0_,
movie0_.price as price3_2_0_,
movie0_.actor as actor1_6_0_,
movie0_.director as director2_6_0_
from
Movie movie0_
where
movie0_.id=?
그런데 이 전략은 정말 좋은 전략일까?
단순히 테이블에 데이터를 넣고 빼는 대는 훌륭한 전략이나, 조회의 경우 문제가 발생하게 된다.
객체 지향 스럽게 코딩을 하고 있기 때문에 데이터를 조회할 때 find 메소드에서 movie 데이터를 찾기 위해 꼭 Movie.class 를 지정해주는 것이 아니라, 슈퍼 타입 클래스인 Item.class 를 지정해줘도 데이터를 찾을 수 있어야 한다.
- JpaMain.java
Item item = em.find(Item.class, movie.getId());
위와 같은 코드를 통해 데이터를 조회할 경우 어떤 문제점이 발생하게 될까?
애플리케이션을 실행해보면 아래와 같은 hibernate SQL 을 출력하는 것을 확인할 수 있다.
Hibernate:
select
item0_.id as id1_2_0_,
item0_.name as name2_2_0_,
item0_.price as price3_2_0_,
item0_.artist as artist1_0_0_,
item0_.author as author1_1_0_,
item0_.isbn as isbn2_1_0_,
item0_.actor as actor1_6_0_,
item0_.director as director2_6_0_,
item0_.clazz_ as clazz_0_
from
( select
id,
name,
price,
artist,
null as author,
null as isbn,
null as actor,
null as director,
1 as clazz_
from
Album
union
all select
id,
name,
price,
null as artist,
author,
isbn,
null as actor,
null as director,
2 as clazz_
from
Book
union
all select
id,
name,
price,
null as artist,
null as author,
null as isbn,
actor,
director,
3 as clazz_
from
Movie
) item0_
where
item0_.id=?
// union 을 통해 모든 테이블을 전부 뒤져야 한다.
- 슈퍼 타입 클래스를 통해 데이터를 조회하려면 모든 테이블을 전부 다 뒤지게 되므로 검색 성능에 있어서 굉장히 비효율적이게 된다.
- 그런데 여기서 만약 데이터를 찾아야 하는데, item id 만 알고 있고 어떤 테이블에 있는 데이터인지 모를 경우 모든 테이블을 다 뒤져보게 되기 때문에 정말 비효율적으로 동작하게 된다.
* 단일 테이블 전략의 경우 item id 값만 명확하게 알고 있어도 어차피 한 테이블에 데이터들이 전부 다 들어있기 때문에 검색 성능에 문제가 없다.
* 테이블 조인 전략도 마찬가지로 item id 만 알아도 Item 테이블에서 해당하는 키의 DTYPE 컬럼을 참조하여 데이터를 가져올 수 있기 때문에 문제가 없다.
각 상속관계 매핑 전략의 장단점에 대해 알아보자.
1. 테이블 조인 전략
장점 :
- 정규화가 되어 있기 때문에 제약조건과 같은 부분은 Item 테이블측에 걸어서 맞춰줄 수 있다.
-> 예시 : 가격 데이터를 통해 정산을 해야 할 경우 Item 테이블만 보면 된다.
- 외래 키 참조 무결성 제약 조건을 활용할 수 있다.
-> 예시 : Order 테이블에서 Item id 를 봐야 할 경우 굳이 그 아래에 있는 테이블 까지 볼 필요 없이 Item 테이블 하나만 보면 된다.(설계가 굉장히 깔끔하게 들어간다.)
- 테이블 자체가 정규화가 잘 되어 있기 때문에 저장공간 활용에 있어 효율적이다.
단점 :
- 조회할 시 조인이 많이 사용되는 편으로 성능이 저하되고 조회 쿼리가 그만큼 복잡해진다.
- 데이터 삽입 시 insert 쿼리가 두 번 호출된다.
- 생각보다 단점들이 크다고 할 수는 없으나 단일 테이블 전략과 비교하면 좀 복잡한게 단점이 될 수 있을 것이다.
(테이블 보인 전략이 객체와도 잘 맞고 정규화도 되고, 설계에서도 깔끔하게 딱 떨어지게 나오는 점에서 기본적으로 정석이라고 보고 들어가면 된다고 한다.)
2. 단일 테이블 전략
장점 :
- 조인이 필요 없으므로 일반적으로 조회 성능이 빠르다.
- 조회 쿼리가 단순하다.
단점 :
- 자식 entity 가 매핑한 컬럼은 모두 null 을 허용하게 된다.(데이터 무결성 입장에서 봤을 때 좀 애매한게 있고, 꽤나 치명적인 단점이다.)
- 단일 테이블에 모든 것을 저장하므로 테이블이 커지면서, 상황에 따라 오히려 조인 테이블 전략보다 조회 성능이 느려질 수 있다.
(그런데 일반적으로는 더 빠르다, 이 정도까지 되려면 임계점을 넘어야 하는데 보통의 경우 임계점을 넘는 경우는 잘 없다.)
3. 구현 클래스마다 테이블 생성 전략
: 결론부터 얘기하자면 쓰면 안되는 전략이다.
왜? -> 객체 지향스럽게 개발하는 것을 좋아하는 쪽과 DB에 맞게 개발하는 것을 좋아하는 쪽 둘다 이 전략을 싫어한다.
(데이터베이스 설계자와 ORM 전문가 둘 다 추천하지 않는 전략임)
데이터들이 묶여 있지 않으므로 만약 새로운 테이블이 추가될 경우 가격 정산을 해야한다고 할 때, 정산을 위한 코드까지 모두 바꿔주어야 한다.
(새로운 테이블이 들어왔으므로 해당 테이블까지 정산 범위에 같이 포함시켜 주어야 한다.)
장점 :
- 서브 타입을 명확하게 구분해서 처리할 때 효과적이다.(insert 및 테이블에 대한 직접적인 select 시 효과적일 수 있다.)
- not null 제약조건을 사용하는 것이 가능하다.
(각 도메인들이 따로 떨어져 있으므로 단일 테이블 전략과 같이 null 을 허용해줄 필요가 없다.)
단점 :
- 여러 테이블을 함께 조회해야 할 때 성능이 느려진다.(union 쿼리를 날려야 함)
- 자식 테이블들을 통합해서 쿼리하기 어렵다.
- 변경이라는 관점에서 볼 때 굉장히 안 좋다.(새로운 타입이 추가 될 때 굉장히 많은 것들을 고쳐야 한다.)
'JPA' 카테고리의 다른 글
실전 예제 4 - 상속관계 매핑 (0) | 2020.10.11 |
---|---|
@MappedSuperclass 어노테이션 (0) | 2020.10.10 |
상속관계 매핑 - 2 (0) | 2020.10.10 |
상속관계 매핑 - 1 (0) | 2020.10.10 |
실전 예제 3 - 다양한 연관관계 매핑 (0) | 2020.10.06 |