Kotlin

Kotlin JPA - 상속 관계 맵핑

dev_roach 2021. 11. 17. 21:00
728x90

RDB 에서는 데이터들간의 연관관계는 있지만, 상속관계는 존재하지 않는다.

하지만 우리는 객체지향적으로 프로그래밍을 해야할때 상속이 필요할때가 있는데 JPA 에서는 이를 상속관계맵핑을 통해 지원한다.

이는 슈퍼타입 서브타입 논리 모델을 통해 실제 모델을 상속관계를 구성할 수 있는데 말이 어려우니 그림을 보자.

우리가 이런식으로 슈퍼타입 서브타입 모델을 실제 물리 모델인 테이블로 구현하기 위한 전략이 필요하다.

즉 쉽게 말해서 Album, Movie, Book 이 Item 을 상속받고 있는데 이것들을 어떻게 실제 물리 모델로 구현하냐이다.

조인전략

첫번째로는 조인 전략이 있다.

뭐 기존 RDB 를 엔티티로 가져오듯, 전부 Entity 각각으로 만들고 Entity 당 Table 을 만든다.

Album, Movie, Book 을 전부 Entity 로 만드는 것이다.

이렇게 한뒤 Item Table 에 Join 하여 상속구조 처럼 이용한다.

여기서 중요한건 DB 에는 이런 Album, Movie 등등.. 커스텀 타입을 우리가 임의로 넣을 수 없다.

따라서 하나의 Column 을 추가하여 Type 을 구분해줘야 한다. 아래와 같이 말이다.

외래키를 통해서 Join 을 통해 상속구조인 것 처럼 이용할 수 있게 된다.

하나 특이한 점은 DTYPE 이라는 컬럼을 통해 어떤 타입인지를 구분한다는 점이다.

어떻게 JPA 에서 구현할 수 있는지 알아보자.

일단 특이한점은 @Inheritance 어노테이션에 위에서 설명한 JOIN 전략을 기술한 것이다.

아까 말했던 것처럼 슈퍼타입 서브타입 모델 -> 실제 물리 데이터 모델로 구현할 전략 중 하나를 택한 것 이다.

@DiscriminatorColumn 의 경우에는 우리가 위에 정의한 테이블 모델에서 우리는 타입 구분을 DTYPE 으로 정한대로 적어준 것이다.

여기서 대략적으로 감이 오는 사람도 있겠지만 자식에도 어떤 타입인지 적어줘야 할 것이다.

우리의 예상대로 @DiscriminatorValue 를 통해서 자신은 "ALBUM" 타입으로 분리되겠다고 명시한 것을 볼 수 있다.

그렇다면 다른 서브타입들도 빠르게 만들어보자.

자 이제 클래스는 짜진것 같고 어떻게 TABLE 이 만들어지는지를 데이터베이스에서 확인해보자.

잘 보면 모든 Entity 가 Table 로 생성된 것을 알 수 있다.

Movie Table 이 이렇게 되어 있는 걸 보아서 id 속성만 상속된 것을 확인할 수 있다.

 

이렇게 봤을때 장점이 하나있다.

원래는 Item 이라는 테이블에 딱 크게 들어가 있을 수도 있지만 이런 구조를 가져갔을 때

정규화가 잘 되어있다는 장점을 얻을 수 있을 것이다.

 

하지만 단점은 무엇이 있을까? 

데이터 베이스를 사용하다보면 알테지만 JOIN 을 계속해서 만들게 되면 여러 테이블을 조인하게 되는 경우가 생긴다.

따라서 SELECT 시 큰 비용으로 다가올 수도 있게 된다.

 

간단하게 조회쿼리를 살펴보자.

이렇게 작성했다. 

확실히 객체 상속구조로 짜니깐 좀더 깔끔한것 같다.

 

뭐 여하튼 쿼리는 어떻게 나갈까? 아마도 우리가 Join Startegy 를 사용했으니 Join Query 가 나갈 것이다.

select 
album0_.id as id2_2_, 
album0_1_.name as name3_2_, 
album0_1_.price as price4_2_, 
album0_.artist as artist1_0_ 
from album album0_ 
inner join item album0_1_ on album0_.id=album0_1_.id 
where album0_.artist=?

위와 같이 item <-> album 테이블간의 join 이 발생한 것을 알 수 있다.

item table 의 DTYPE 에는 ALBUM 이 들어간 것을 볼 수 있다.

JPA 정말 편하다..

 

하지만 SELECT 말고도 문제가 하나 더 있다면 더 있었다.

모두 TABLE 로 구성되고 JOIN 으로 관계를 이루다 보니, 데이터도 두번 추가되야만 했다.

일단 테이블로 나누고 정규화를 했기에 어쩔 수 없이 따라오는 단점이라고 생각한다.

그렇담 이 단점을 상쇄하기 위해서는 어떻게 해야될까?

 

바로 이 테이블을 하나로 합쳐 단일 테이블로 구성하면 된다.

그게 바로 단일 테이블 전략이다.

단일 테이블 전략

그럼 단일 테이블 전략으로 하려면 우리는 코드를 어떻게 바꿔야 할까?

다행히도 JPA 는 전략을 바꾸는 것만으로도 가능하다.

이렇게 한 뒤에 바로 DDL 로 만들어진 테이블을 확인해보자.

일단 TABLE 이 하나로 줄었다.

모두 합쳐서 단일 테이블로 구성 시킨 것 이다.

실제로 데이터 베이스의 테이블 구성을 확인 해 보자.

위와 같이 모든 것들을 하나의 테이블에 넣어서 구성했다.

하지만 정말 단점은 이게 원래 하나의 테이블에 있어야 할 데이터가 아니다.

즉, 정규화가 안된 테이블이라는 것이다.

 

하지만 그럼에도 불구하고 이런 전략을 사용하는 이유가 있을 것 이다.

무엇일까? 바로 성능상의 이점이다.

 

테이블이 1개로 합쳐져 있다보니 JOIN 도 하지 않고, INSERT 도 단 한번 이루어진다.

아래 쿼리를 한번 보자.

INSERT 쿼리가 JOIN 전략에서는 두번 나갔는데 여기서는 한번 밖에 나가지 않는다.

그럼 SELECT 쿼리는 어떨까?

select 
album0_.id as id2_0_, 
album0_.name as name3_0_, 
album0_.price as price4_0_, 
album0_.artist as artist5_0_ 
from item album0_ 
where album0_.dtype='ALBUM' and album0_.artist=?

JOIN Query 또한 나가지 않는다.

우리는 이로서 단일 테이블로 구성하면 성능적인 이점을 얻을 수 있다는 사실을 알 수 있었다.

구현 클래스마다 테이블 전략

마지막으로 구현 클래스마다 테이블 전략이 있는데..

이건 굳이 추천하지 않는 패턴이라 적지는 않겠다. 궁금하다면 직접 찾아보길 바란다.

느낀점

JPA 를 사용하면 우리가 객체지향을 조금 더 쉽게 할 수 있도록 도와준다.

그리고 우리가 Native Query 로 관리해야 할 부분들을 객체들간의 관계 혹은 Java Code 로 유지보수 할 수 있도록 도와준다. 

여기서 단순히 넘기는 것이 아니라 전략 패턴을 사용하여 전략만 바꾸면 쉽게 코드 변경없이도 잘 작동하는 모습도 볼 수 있었다.

 

728x90

'Kotlin' 카테고리의 다른 글

[Mac] Kotlin 설치 및 CLI 에서 사용하기  (0) 2021.12.06
Kotlin Null 처리  (0) 2021.11.18
Kotlin JPA  (0) 2021.11.16
Kotlin object  (0) 2021.11.15
Kotlin 변수 선언  (0) 2021.11.13