도입
지난 시간에 JPA를 활용해 객체와 연관관계 매핑을 하는 방법을 알아보았다. 이번 시간에는 JPA를 활용하여 객체의 상속관계를 테이블로 표현하는 방법에 대하여 알아보고, 상속관계와는 다르지만 비슷해서 혼동하기 쉬운 개념인 @MappedSuperclass를 알아보도록 하겠다.
상속관계 매핑
객체지향언어에서는 중복되는 정보를 상속관계를 사용해 묶어줄 수있다. 예를들어 사람과 강아지 고양이는 모두 이름과 나이 그리고 구분지어줄 ID를 가지고 있다. 이러한 특징을 '동물'로 묶어서 상속해줄 수 있다.
객체에서는 이렇게 상속을 허용해주지만, 테이블에서는 이렇게 상속을 할 수 없다. 따라서 상속을 위해서는 전략을 사용해야한다.
상속의 전략을 사용하기 위해서는 @Inheritance annotation을 이용하여 strategy값을 넣어주면 된다. 추가적으로 자주 사용되는 Annotation으로 @DiscriminatorColumn(name="xx")이 있으며 이것은 어느 테이블과 매핑이 되어있는지에 대한 정보를 상위 테이블에 저장해두는 것이다. 만약 동물 테이블이 사람과 연관되어있는 것이라면 동물에 DTYPE으로 사람이 들어가게 된다.(Default) name의 값을 변경하면 그 값이 DTYPE이 된다. 그리고 @DiscriminatorValue("xx")를 사용해주면 DTYPE으로 입력되는 엔티티의 이름을 변경해줄 수 있다.(Default는 엔티티 테이블 이름이다.)JSP에서는 3가지의 전략을 제공하며 3가지에 대하여 자세히 알아보도록 하겠다.
- Join을 활용하여 상속관계 매핑(JOINED)
- 단일 테이블로 매핑(SINGLE_TABLE)
- 각 테이블로 매핑(TABLE_PER_CLASS)
Join을 활용하여 상속관계 매핑하기
Join을 활용하여 매핑을 하는 것은 말그대로 엔티티의 조인을 활용해주는 것이다. 사람과 강아지 그리고 고양이에 동물과 연관된 ID를 넣어주어 매핑을 할 수 있게 만들어 주는 전략이다. 그렇게 되면 사람 강아지 고양이에는 ID라는 PK이자 FK가 생성된다. 이 전략을 사용하면 객체와 굉장히 유사하게 매핑을 할 수 있다. 그리고 테이블이 정규화가 되어있다.(같은 것 끼리 묶여있음) 그리고 외래키 참조 무결성 제약조건을 사용할 수 있다.(참조할 수 없는 값을 가질 수 없음) 그리고 뒤에서 소개할 Single Table에 비해 저장공간을 효율적으로 사용한다.
하지만 단점으로 조회를 할 때마다 조인을 사용하기 때문에 성능이 저하될 수 있다. 그리고 데이터를 저장하게 되면 항상 동물과 아래 엔티티 두개의 Insert sql이 날라가게 된다.
@DiscriminatorColumn을 붙여주어야 동물 Entity에 DTYPE으로 어느 엔티티와 매핑이 되었는지 정보를 제공해준다.
단일 테이블로 매핑하기
단일 테이블로 매핑하는 것은 테이블 한곳에 위 정보를 모두 넣어주는것을 말한다. 즉 동물 테이블 안에 사람과 고양이 그리고 강아지의 특징을 모두 넣어주고 만약 사람을 사용한다면 강아지와 고양이에 대한 정보는 null로 남겨주는 것이다. 단일 테이블로 매핑하면 매우 간단하고, 따로 조회를 하거나 할 때 조인도 일어나지 않기 때문에 성능도 빠르다. 하지만 단점으로 모든 자식 엔티티의 매핑 컬럼에 null값이 올 수 있도록 세팅을 해주어야하며, 모든 것을 한 테이블에 때려박으므로 조인 방식에 비해 비교적 공간을 많이 잡아먹는다.
그리고 @DiscriminatorColumn(name="xx")를 사용하지 않아도 DTYPE값이 들어간다. 왜냐하면 어느 값이 매핑되어있는지 모르기 때문이다.
실무에서는 Join과 단일 테이블 매핑 둘중에 하나만 사용하므로 두개를 비교해서 알아보겠다.
Join으로 매핑 | 단일 테이블로 매핑 | |
조회 성능 | 느림 | 빠름 |
메모리 사용 | 적절함 | 많음 |
단점 | 조회시 조인을 너무 많이 함 | 자식 엔티티 칼럼 모두 null을 허용함 |
기본적으로 Join을 사용하는 것이 테이블도 정규화되어있고 저장공간도 효율적으로 사용할 수 있으므로 괜찮은 것 같다. 그리고 단일 테이블 매핑의 경우 만약 칼럼이 엄청 많다면, 오히려 Join을 하는 것보다 메모리 낭비로 인해 더 느려질 수 있을 것 같다. Join을 사용하지만 매우 간단하고 미래를 봤을 때 크게 변화할 가능성이 적어보이는 간단한 상속관계는 단일테이블전략을 사용해주는 방식으로 선택해야할 것 같다.
각 테이블로 매핑하는 전략
실무에서는 사용하지 않지만 간단히 알아보겠다.
동물 테이블 즉 상위 클래스의 엔티티 테이블의 정보를 사람, 고양이, 그리고 강아지에 넣어 각각을 구현하는 방법이다. 이렇게 구현을 하면
서브타입을 명확히 구분하여 처리가 가능하며 싱글테이블전략에서 사용하지 못하였던 not null 제약조건을 사용할 수 있다.
하지만, 만약 자식에 대하여 찾는게 아닌 부모(동물클래스)에 대하여 찾을 때는 모든 자식 클래스를 Union하여 찾아주어야하므로 성능이 굉장히 느려진다.
상속관계에서 보여지는 JPA의 위대함
상속관계를 어떤 전략을 사용하느냐에 따라 테이블이 달라지는 것을 알 수 있다. 테이블이 달라지면, 그 테이블을 생성하거나 하는 모든 코드를 변경해 주어야 하므로 매우 번거로울 것이다. 하지만 JPA로 이러한 일들을 처리하면 @Inheritance의 strategy 값만 변경하여주면 손쉽게 해결해 줄 수 있다.
@MappedSuperclass
MappedSuperclass는 상속관계 매핑이 아니다!! 하지만 너무 비슷하여 혼동할 수도 있기 때문에 이번시간에 다루어보려고 한다.
엔티티에 중복적으로 들어가는 데이터로 ID가 있을 수 있다. 그리고, 객체를 언제 생성하였는지? 누가 생성하였는지, 누가 변경하였는지, 언제 마지막으로 변경하였는지 등에 대한 정보를 넣어주고 싶을 때가 있을 것이다. 이 때 일일이 모든 클래스를 들어가서 Entity를 수정해주면 된다. 하지만 이것은 매우 비효율적이다. 이렇게 중복적으로 대부분의 엔티티에서 사용해주는 정보를 상속으로 해결해줄 수 있다. @MappedSuperclass를 사용해주면 된다. 이것은 엔티티가 아니며 테이블과 매핑도 되지 않는다. 단순히 상속받은 클래스에게 매핑되는 정보를 제공해주는 역할을 수행해준다.
매핑정보만 제공해주기 때문에 @Entity나 @MappedSuperclass만 상속받을 수 있다.
직접 생성해서 사용하는 일이 없으므로 되도록 abstract 클래스로 생성하도록 하자.
import javax.persistence.MappedSuperclass;
@MappedSuperclass
public abstract class BaseEntity {
private String createdBy;
private LocalDateTime createdDate;
private String lastModifiedBy;
private LocalDateTime lastModifiedDate;
}
다른 Entity클래스에서 extends해주면 위에 대한 매핑정보를 받아와 사용하게 된다.
이 글은 김영한님의 강의 "자바 ORM 표준 JPA 프로그래밍"를 공부한 후 작성한 글 입니다.
'Back-end > JPA' 카테고리의 다른 글
JPA 프록시 객체와 지연로딩 (0) | 2022.02.02 |
---|---|
JPA 연관관계 매핑하기 (0) | 2022.01.15 |
JPA 기본 키 매핑하기 (0) | 2022.01.12 |
JPA로 필드와 칼럼 매핑하는 annotation 알아보기 (0) | 2022.01.10 |
JPA로 객체와 테이블 매핑하기 (0) | 2022.01.07 |