ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [JPA] 상속관계 매핑 전략();
    JPA 2020. 3. 27. 16:38
    반응형
    JPA는 테이터베이스와 객체를 매핑해주는 자바 진영의 ORM 기술 표준이다. 그럼 객체지향언어의 특징 중 하나인 '상속'의 개념이 포함된 객체는 데이터베이스에서 어떠한 방식으로 매핑되는 것일까? 

    객체지향의 상속관계

    Item 객체를 부모클래스로 지정하고 Album, Movie, Book 객체는 부모클래스를 자원을 상속받은 자식클래스로서 역할을 수행하고있다. 데이터베이스는 상속을 어떤식으로 표현할까?

     

    결론부터 말하자면 객체의 상속관계와 정확하게 일치하는 데이터베이스 모델링은 존재하지 않는다고 한다. 차선책으로 상속관계와 비교적 유사한 슈퍼타입-서브타입 모델링 기법으로 데이터베이스를 상속 객체에 매핑해야 한다.

     

    슈퍼타입-서브타입 논리모델을 실제 데이터베이스 물리모델로 구현하는 것은 3가지 전략이 있다.

     

    조인 전략


    1. 모델링

    공통적인 칼럼을 가진 상위 테이블을 만들고 개별적인 칼럼은 하위 테이블로 만든다. 상위 테이블과 하위 테이블은 pk를 통해 Join으로 연결시킨다.

    만약 ALBUM 데이터를 추가하면 ALBUM의 name과 price는 ITEM 테이블에 추가되고 artist는 ALBUM 테이블에 추가가 되면서 INSERT 쿼리는 두 번 발생한다. ALBUM 데이터를 조회한다면ITEM 테이블과 ALBUM 테이블을 ITEM_ID 칼럼의 값으로 join 하여 데이터를 가져오는 방식이다. 상위 테이블에서는 하위 테이블에 대한 구분자의 역할로 DTYPE 같은 필드가 존재한다. 정규화 규칙을 잘 따른 전략이다.

     

    2. JPA 매핑 구현 방식

    @Entity
    @Inheritance(strategy = InheritanceType.JOINED)
    @DiscriminatorColumn
    public abstract class Item {
    
    	@Id @GeneratedValue
    	private Long id;
    	private String name;
    	private int price;
    }
    @Entity
    @DiscriminatorValue("앨범")
    public class Album extends Item{
    	private String artist;
    }
    @Entity
    @DiscriminatorValue("영화")
    public class Movie extends Item{
    	private String director;
    	private String actor;
    }
    @Entity
    @DiscriminatorValue("책")
    public class Book extends Item{
    	private String author;
    	private String isbn;
    }
    

     

    상위 클래스가 되는 Item 추상클래스를 살펴보자. 

    @Inheritance(strategy = InheritanceType.JOINED) 애노테이션은 데이터베이스의 조인 전략으로 매핑할 것이라는 의미를 나타낸다.

    @DiscriminatorColumn 애노테이션은 상위 클래스에서 명시된다. 데이터베이스의 상위 테이블(ITEM 테이블)에게 구분자 역할을 DTYPE 칼럼을 넣어준다. 애노테이션의 name 속성으로 칼럼명을 바꿔준 수 있다.

    @DiscriminatorValue 애노테이션은 하위 클래스에서 사용되고 있다. 상위 테이블(ITEM 테이블)을 조회할때 DTYPE 칼럼에 사용되는 value를 설정해주는 기능이다. 애노테이션을 사용하지 않을 경우 기본 Default 값인 Entity 이름으로 value값이 채워지게 된다.

    DTYPE이 추가된 ITEM 테이블

    3. 성능 및 테스트

     

    간단하게 Item 추상클래스를 상속받은 Movie 클래스를 생성해서 INSERT, SELECT 해보았다.

    테이블이 조인 전략으로 나누어져 있기 때문에 INSERT 할때, 상위 테이블에 1번, 하위 테이블에 1번 총 2번 나가는 것을 확인할 수 있다. 또한 SELECT 할땐 id값을 통해 JOIN을 하는 쿼리를 확인할 수 있다.

      장점 단점
    조인 전략

    정규화가 잘 적용되어 있는 형태(저장공간 효율)

    객체지향적인 설계와 가장 부합됨.

    데이터 조회할때 조인을 사용함.

    데이터 등록할때 INSERT 2번 사용함.

     

    단일테이블 전략


    1. 모델링

    상위 테이블, 하위 테이블 구분없이 모든 칼럼을 하나의 테이블로 합쳐 사용하는 전략이다. DTYPE 칼럼은 앨범, 영화, 책을 구분할 수 있는 구분자로 사용한다. 가장 직관적이고 단순한 방식이다. Join할 필요가 없기 때문에 성능이 가장 좋다고 한다.

     

    2. JPA 매핑 구현 방식

    @Entity
    @Inheritance(strategy = InheritanceType.SINGLE_TABLE)
    @DiscriminatorColumn
    public abstract class Item {
    	@Id @GeneratedValue
    	private Long id;
    	private String name;
    	private int price;
    }
    @Entity
    @DiscriminatorValue("앨범")
    public class Album extends Item{
    	private String artist;
    }
    @Entity
    @DiscriminatorValue("영화")
    public class Movie extends Item{
    	private String director;
    	private String actor;
    }
    @Entity
    @DiscriminatorValue("책")
    public class Book extends Item{
    	private String author;
    	private String isbn;
    }
    

    JPA 매핑방법은 간단하다. @Inheritance(strategy = InheritanceType.SINGLE_TABLE) 애노테이션을 통해 설정이 가능하다. 사실 JPA 상속 기본전략이 단일테이블을 default로 가지고 있어서 애노테이션으로 명시하지 않아도 단일테이블 전략을 따른다.

    참고로 단일테이블 전략은 하나의 테이블에 모든 칼럼을 가지고 있기때문에 구분자를 위해 @DiscriminatorColumn 애노테이션을 사용하지 않아도 DTYPE 칼럼이 ITEM 테이블에 생성이 된다.

     

    3. 성능 및 테스트

    테이블을 초기화 한 뒤 다시 한번 Item 추상클래스를 상속받은 Movie 클래스를 생성해서 INSERT, SELECT 해보았다.

    테이블이 단일테이블 전략으로 단 하나의 테이블만 존재한다. 칼럼에 null 값을 가지고 있는 것을 확인할 수 있다.

     

      장점 단점
    단일테이블 전략

    조인이 필요 없으므로 조회 성능 우수함.

    하위 엔티티의 필드값은 모두 Null을 허용.

    하나의 테이블에 칼럼이 많아져 복잡함.

     

    구현 클래스 각각의 테이블 전략


    1. 모델링

    상위 테이블과 하위 테이블의 개념을 사용하지 않고 각각의 테이블에 공통된 칼럼(name, price)을 지니고 있는 전략이다.

    DBA와 ORM전문가 모두 기피하는 방식이라눙...

     

    2. JPA 구현방식

    @Entity
    @Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
    public abstract class Item {
    	@Id @GeneratedValue
    	private Long id;
    	private String name;
    	private int price;
    }
    @Entity
    public class Album extends Item{
    	private String artist;
    }
    @Entity
    public class Movie extends Item{
    	private String director;
    	private String actor;
    }
    @Entity
    public class Book extends Item{
    	private String author;
    	private String isbn;
    }
    

     

    상속 전략을 @Inheritance(strategy = InheritanceType.TABLE_PER_CLASS) 로 설정한다.

    공통 칼럼을 가진 상위 테이블 개념이 존재하지 않으므로 @DiscriminatorColumn, @DiscriminatorValue애노테이션을 사용할 필요가 없다.

     

    3. 성능 및 테스트

      장점 단점
    구현클래스 각각의 테이블 전략

    not null 제약조건 사용가능.

    Item findItem = em.find(Item.class, m.getId());

    위와 같이, 상위 클래스 ITEM 형식으로 데이터를 받고자 하면 모든 테이블을 다 select하기 위해 UNION 사용하면서 비효율적임. 

     

    결론 : 맺음말..


    상속 객체를 데이터베이스로 표현하고자 할땐 3가지 방법이 있으나, 조인 전략 or 단일테이블 전략 중에 고민해보고 결정해야 한다. 기본적으로 가장 객체지향적인 조인 전략을 선택하고 테이블 칼럼 수가 굉장히 작고 가볍다면 단일테이블 전략을 선택해보는 것도 나쁘지 않은 선택이다.

     

    반응형

    댓글

Designed by Tistory.