테이블에서의 상속관계와 객체에서의 상속관계
- 관계형 데이터베이스는 상속관계가 없다.
- 슈퍼타입 서브타입 관계라는 모델링 기법이 객체에서의 상속이랑 유사
디비 입장에서 객체에서의 상속관계처럼 만들 수 있는 여러가지 전략이 있다.
- 조인전략(JOINED)
-
@Inheritance(strategy = InheritanceType.JOINED)
- @DiscriminatorColumn 어노테이션을 통해 DTYPE을 넣어줄 수 있고 위의 그림에서 ALBUM, MOVIE, BOOK의 엔티티 명이 Default로 들어간다.
- 위의 컬럼은 안넣어줘도 상관없지만, 넣어주는 것이 좋다. (DTYPE이 ALBUM때매 들어온건지 MOVIE때문인지 등을 분별할 수 있기때문에)
- 자식들에는 @DiscriminatorValue를 써줘야 한다. (회사마다 다르겠지만, DBA의 특성에 따라 Default인 엔티티명을 그대로 쓰는것이 아니라 별칭을 쓸수도 있기 때문 ex) ALBUM이 아니라 DTYPE을 A로 표시할 경우 @DiscriminatorValue("A")
- 기본적으로는 JOINED 전략을 쓰겠다 라고 생각을 가지고 가야 한다.
- 장점
- 테이블 정규화
- 외래키 참조 무결성 제약조건 활용가능
- 저장공간 효율화
- 단점
- 조회시 조인을 많이 사용, 성능 저하
- 조회 쿼리가 복잡함
- 데이터 저장시 INSERT 쿼리 2번 호출
- 장점
- 실행 결과
Hibernate: /* insert hellojpa.Movie */ insert into Item (name, price, DTYPE, id) values (?, ?, 'M', ?) Hibernate: /* insert hellojpa.Movie */ insert into Movie (actor, director, id) values (?, ?, ?) // JPA가 JOINED 전략이면 알아서 판단해서 // INNER JOIN 하여 값을 가져온다. Hibernate: select movie0_.id as id2_2_0_, movie0_1_.name as name3_2_0_, movie0_1_.price as price4_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=?
-
- 단일 테이블 전략(SINGLE_TABLE)
-
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
- 단일 테이블 전략에서는 @DiscriminatorColumn 이 들어가있지 않아도 알아서 DTYPE이 생긴다.
- 단일 테이블 전략에서는 한테이블에 들어가있다 보니까 이게 ALBUM인지 MOVIE인지 등의 엔티티를 판별할 수 없기때문에 DTYPE이 필수적으로 있어야 한다.
- 장점
- 조인이 필요 없으므로 일반적으로 조회 성능이 빠름
- 조회 쿼리가 단순함
- 단점
- 자식 엔티티가 매핑한 컬럼은 모두 null 허용
- 내가 선택한 자식 테이블의 데이터를 제외하고 다른 테이블에도 null 허용이 되어야 한다.
- ex) BOOK 테이블에 있는 작가(Author) 값만 넣어줘야 하는데, 한테이블에 다른 자식인 ALBUM이나 MOVIE도 있기 때문에 얘네들이 들고있는 컬럼을 null 허용을 해줘야 한다는 뜻
- 단일 테이블에 모든 것을 저장하므로 테이블이 커질 수 있음 상황에 따라서 조회 성능이 느려질 수 있다.
- 자식 엔티티가 매핑한 컬럼은 모두 null 허용
- 장점
- 단일 테이블 전략에서는 한테이블에 들어가있다 보니까 이게 ALBUM인지 MOVIE인지 등의 엔티티를 판별할 수 없기때문에 DTYPE이 필수적으로 있어야 한다.
- 실행결과
// SINGLE_TABLE 전략으로 설정하게되면 // JPA가 아래와 같이 하나의 테이블에다가 쫙 다 넣어준다. 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) ) // INSERT 할 경우에도 한번에 쫙 INSERT 한다. Hibernate: /* insert hellojpa.Movie */ insert into Item (name, price, actor, director, DTYPE, id) values (?, ?, ?, ?, 'M', ?) Hibernate: select movie0_.id as id2_0_0_, movie0_.name as name3_0_0_, movie0_.price as price4_0_0_, movie0_.actor as actor8_0_0_, movie0_.director as director9_0_0_ from Item movie0_ where movie0_.id=? and movie0_.DTYPE='M'
-
- 구현 클래스마다 테이블 전략(TABLE_PER_CLASS)
-
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
- 테이블 구현할때마다 각각 데이터를 다 들고있는 전략
- 결과적으로 봤을 때 세번째 전략인 구현 클래스마다 테이블 전략은 사용하지 말자!
- 명확하게 특정 테이블에서 데이터를 찾는 것은 쉽지만, 단점이 특정 아이디를 가지고 조회할 경우에 각 테이블을 다 뒤져봐야한다는 단점이 있다.
- 장점
- 서브 타입을 명확하게 구분해서 처리할 때 효과적
- not null 제약조건 사용 가능
- 단점
- 여러 자식 테이블을 함께 조회할 때 성능이 느림 (UNION 쿼리를 쓰기 때문)
- 자식 테이블을 통합해서 쿼리하기 어려움
- 변경이라는 관점으로 봤을 떄 정말 비효율적이다 새로운 타입이 추가가 될 경우 많은 부분을 뜯어야한다.
- 장점
- 실행결과
// 각 테이블별로만 만들어지고 ITEM 테이블은 만들어지지 않는다. // 부모테이블이 만들어지지 않는다. Hibernate: create table Movie ( id bigint not null, name varchar(255), price integer not null, actor varchar(255), director varchar(255), primary key (id) ) Hibernate: create table Book ( id bigint not null, name varchar(255), price integer not null, author varchar(255), isbn varchar(255), primary key (id) ) Hibernate: create table Album ( id bigint not null, name varchar(255), price integer not null, artist varchar(255), primary key (id) )
-
결론!
- 기본적으로 JOINED 전략을 가지고 가자라는 생각을 하고, 조인 테이블의 장단점과 단일 테이블 전략의 장단점을 고려하여 DBA와 상의하여 정하자!
- 단, 구현 클래스마다 테이블 전략은 생각을 하지도 말자!
- 정말 단순한 구조에 데이터도 얼마 안되고 확장성도 낮다 할 경우에는 단일 테이블 전략을 사용하는 것이 효율적이다.
- 비즈니스 적으로 복잡해지고 중요한 테이블이다 싶으면, JOINED 전략을 사용하자!
'Dev > JPA' 카테고리의 다른 글
RS) 프록시와 연관관계 (0) | 2022.03.02 |
---|---|
RS) MappedSuperclass 매핑 정보 상속(상속관계X) (0) | 2022.02.22 |
RS) 다대다 관계 (0) | 2022.02.17 |
RS) 연관관계 매핑시 주의할 점 (0) | 2022.02.15 |
RS) 양방향 연관관계와 연관관계의 주인 (0) | 2022.02.06 |