본문 바로가기
  • 개발공부 및 일상적인 내용을 작성하는 블로그 입니다.
JPA

상속관계 매핑 - 1

by 방구석 대학생 2020. 10. 10.

"인프런 - 자바 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