"인프런 - 자바 ORM 표준 JPA 프로그래밍 강의를 듣고 작성한 글 입니다."
www.inflearn.com/course/ORM-JPA-Basic#
자바 ORM 표준 JPA 프로그래밍 - 기본편 - 인프런
JPA를 처음 접하거나, 실무에서 JPA를 사용하지만 기본 이론이 부족하신 분들이 JPA의 기본 이론을 탄탄하게 학습해서 초보자도 실무에서 자신있게 JPA를 사용할 수 있습니다. 초급 웹 개발 프로그
www.inflearn.com
지금까지 객체와 객체 간의 다대일, 일대다 와 같은 연관관계를 알아보았다.
그렇다면 객체 간의 상속관계의 경우 테이블에 어떻게 매핑할 수 있을까?
테이블의 상속관계 매핑??
기본적으로 관계형 데이터베이스는 상속관계가 존재하지 않는다.
그나마 테이블에서 객체의 상속관계와 비슷한 개념이 있다고 한다면, 슈퍼 타입과 서브 타입 관계라는 모델링 기법이 객체 상속과 유사하다고 할 수 있다.
-> 객체의 경우 명확하게 상속 관계를 나타낼 수 있으며, 데이터베이스의 슈퍼 타입과 서브 타입으로 해당 객체의 상속관계를 매핑해주는 것이 가능하다.
- 관계형 데이터베이스는 논리 모델과 물리 모델 이라는 개념이 있다.
: 이 글 썸네일에서 왼쪽의 논리 모델을 보면 음반, 영화, 책 이라는 객체가 존재하는데, 해당 객체들에서 오른쪽 논리 모델과 같이 이름, 가격과 같이 객체간에 공통적으로 묶일 수 있는 부분은 물품(슈퍼 타입) 에 두고 그외 각각에 맞는 데이터들은 각 객체에 맞게 밑으로 내려 줄 수 있다.
그렇다면 슈퍼, 서브 타입으로 구성된 테이블의 논리 모델을 실제 물리 모델로 어떻게 구현할 수 있을까?
- DB 입장에서는 데이터를 구성한 논리 모델을 물리 모델로 구체화 할 때 아래와 같은 3가지 방법중 하나를 선택할 수 있다.
1. 각각을 테이블로 변환한다. -> 테이블 조인 전략
: 슈퍼 타입의 테이블과 서브 타입의 테이블을 서로 조인으로 구성하는 전략이다.
데이터를 삽입할 경우 슈퍼 타입과 서브 타입 테이블에 각각 한번씩, 총 두번 삽입하게 되며(각 테이블에 맞는 데이터들을 삽입해 줘야 하기 때문), 데이터 조회의 경우 기본 키가 같은 것을 이용해서 기본 키 - 외래 키 매핑을 통해 조인을 해서 데이터를 조회해 온다.
* 이 전략의 경우 각각 어떤 객체의 데이터를 삽입하고 조회하는지 구분 할 수 있어야 하기 때문에 슈퍼 타입 테이블에 DTYPE 과 같이 구분하는 컬럼을 둬야 한다.
2. 논리 모델을 통합 테이블 형태로 변환환다. -> 단일 테이블 전략
: 논리 모델을 한 테이블로 모두 합쳐버리는 전략이다.
* 이 전략의 경우에도 DTYPE 컬럼을 이용하여 어떤 객체의 데이터가 들어왔는지 구분해 줄 수 있다.
(단일 테이블 전략의 경우 필수적으로 DTYPE 컬럼을 이용해야 한다. -> 도메인들이 각각 떨어져 있지 않고 한 테이블로 묶이기 때문에 어떤 데이터가 들어오는지 명확하게 파악할 수 있어야 하기 때문)
3. 서브 타입들을 테이블로 변환한다. -> 구현 클래스마다 테이블 생성 전략
: 객체에서는 상속 관계를 가질 수 있으나, 테이블의 경우 슈퍼 타입을 만들지 않고 그냥 각자 따로따로 공통된 컬럼을 가진 서브 타입 테이블들을 만들어주는 전략이다.
* 테이블 입장에서는 상속 관계인 논리 모델을 물리 모델로 구현하는데 3가지 방법이 있으나, 객체의 경우는 애초에 상속관계를 지원하기 때문에 그냥 다 똑같다.(테이블에서 어떤 방법을 활용하든 JPA 에서는 다 매핑이 가능하다.)
-> 결론적으로 상속 관계 매핑의 핵심은 DB 에서 슈퍼 타입, 서브 타입 관계라는 논리 모델링 기법을 3가지 전략 중 어떤 방법으로 구현하든 JPA 에서 다 매핑을 할 수 있도록 지원을 해준 다는 것이다.
첫 번째, 테이블 조인 전략
테이블에서 조인 전략을 이용하여 가장 정규화된 방식으로 문제를 해결 할 때 데이터 또한 정규화 되어 들어가게 된다.
실습을 위해 Item 클래스를 생성하여 기본 키와 각종 필드를 선언해준 다음
해당 클래스를 상속하는 Album, Book, Movie 클래스를 만든 후에 실행해보자.
- Item.java
@Entity
public class Item {
@Id @GeneratedValue
private Long id;
private String name;
private int price;
// Getter, Setter
}
- Album.java
@Entity
public class Album extends Item { // Item 클래스 상속
private String artist
// Getter, Setter
}
- Book.java
@Entity
public class Book extends Item {
private String author;
private String isbn;
// Getter, Setter
}
- Movie.java
@Entity
public class Movie extends Item {
private String director;
private String actor;
// Getter, Setter
}
위와 같이 코드를 작성한 후 애플리케이션을 실행해보면 아래와 같은 hibernate SQL 을 출력하는 것을 볼 수 있다.
- hibernate SQL
Hibernate:
create table Item (
DTYPE varchar(31) not null,
id bigint not null,
name varchar(255),
price integer not null,
artist varchar(255),
author varchar(255),
isbn varchar(255),
actor varchar(255),
director varchar(255),
primary key (id)
)
- 위의 SQL 을 보면 단일 테이블 전략으로 상속관계가 매핑되는 것을 볼 수 있는데, 이는 JPA 가 상속관계를 매핑하는데 있어 기본 전략으로 단일 테이블에 모든 필드를 집어 넣는 방식이 적용되어 있기 때문에 한 테이블에 모든 필드들이 들어간다.
여기서 테이블 조인 전략을 선택하려면 @Inheritance 어노테이션을 활용할 수 있다.
* @Inheritance 어노테이션
상속관계를 가지는 객체를 테이블로 매핑하기 위해 사용하는 어노테이션이다.
strategy 속성에서 테이블 매핑 전략 3가지 중 하나를 선택할 수 있다.
- 키워드
1. InheritanceType.JOINED : 테이블 조인 전략
2. InheritanceType.SINGLE_TABLE : 단일 테이블 전략
3. InheritanceType.TABLE_PER_CLASS : 구현 클래스마다 테이블 생성 전략
위에 따라 테이블 조인 전략을 사용하기 위해 @Inheritance 어노테이션을 선언해준 뒤에, strategy 속성에서 값을 InheritanceType.JOINED 로 지정해준 후 애플리케이션을 실행해주자.
- Item.java
@Entity
@Inheritance(strategy = InheritanceType.JOINED) // 객체 상속관계를 테이블 조인 전략으로 지정한다.
public class Book extends Item {
private String author;
private String isbn;
// Getter, Setter
}
코드를 위와 같이 작성해 준 후 애플리케이션을 실행해보면 아래와 같은 hibernate SQL 을 출력하는 것을 볼 수 있다.
- hibernate SQL
hibernate SQL
Hibernate:
create table Album (
artist varchar(255),
id bigint not null,
primary key (id)
)
Hibernate:
create table Book (
author varchar(255),
isbn varchar(255),
id bigint not null,
primary key (id)
)
Hibernate:
create table Item (
id bigint not null,
name varchar(255),
price integer not null,
primary key (id)
)
Hibernate:
create table Movie (
actor varchar(255),
director varchar(255),
id bigint not null,
primary key (id)
)
위의 SQL 을 보면 테이블 조인 전략에 따라 부모 클래스인 Item 클래스의 테이블과 해당 클래스를 상속 받는 자식 클래스인 Album, Movie, Book 클래스의 테이블을 모두 생성해주는 것을 알 수 있다.
-> Album, Movie, Book 클래스에는 id 필드를 선언하지 않았는데, Item 클래스의 기본 키를 외래 키로 가져오면서 hibernate SQL 상에 해당 id 값이 각 클래스들의 기본 키로 함께 매핑되었다.
그럼 아래와 같은 코드를 통해 데이터를 테이블에 삽입하면 어떻게 될까?
- JpaMain.java
Movie movie = new Movie();
movie.setDirector("aaaa");
movie.setActor("bbbb");
movie.setName("인터스텔라");
movie.setPrice(10000);
em.persist(movie);
tx.commit();
위와 같이 데이터를 삽입하였을 경우 아래와 같이 insert SQL 이 2개 나오는 것을 확인할 수 있다.
Hibernate:
/* insert hellojpa.Movie
*/ insert
into
Item // Item 테이블 insert
(name, price, id)
values
(?, ?, ?)
Hibernate:
/* insert hellojpa.Movie
*/ insert
into
Movie // Movie 테이블 insert
(actor, director, id)
values
(?, ?, ?)
이번엔 데이터를 조회할 경우 SQL 이 어떻게 나오는지 확인해보자.
- JpaMain.java
em.flush(); // 데이터 삽입 쿼리를 미리 데이터베이스에 전달한다.
em.clear(); // 쌓여있는 쿼리들을 모두 날린다.
Movie findMovie = em.find(Movie.class, movie.getId());
- hibernate SQL
Hibernate:
select
movie0_.id as id1_2_0_,
movie0_1_.name as name2_2_0_,
movie0_1_.price as price3_2_0_,
movie0_.actor as actor1_6_0_,
movie0_.director as director2_6_0_
from
Movie movie0_
inner join
Item movie0_1_
on movie0_.id=movie0_1_.id
where
movie0_.id=?
-> 데이터 조회의 경우 inner join 을 통해 Item 테이블에서 찾고자 하는 movie entity 의 기본 키 값을 가져온 후, Movie 테이블에서 해당 키를 통해 movie entity 의 데이터를 가져온다.
다음글에선 삽입 또는 조회되는 데이터를 구분하기 위한 컬럼인 DTYPE 과 단일 테이블 전략에 대해서 알아보자.
'JPA' 카테고리의 다른 글
상속관계 매핑 - 3 (0) | 2020.10.10 |
---|---|
상속관계 매핑 - 2 (0) | 2020.10.10 |
실전 예제 3 - 다양한 연관관계 매핑 (0) | 2020.10.06 |
실전 예제 2 - 연관관계 매핑 기초 (0) | 2020.10.06 |
다양한 연관관계 매핑 - 3 (0) | 2020.10.05 |