먹었으면 뇌를 쓰자

JPA 학습 내용을 정리한 로드맵 - 3. 상속/3.1 상속관계 본문

Framework/JPA

JPA 학습 내용을 정리한 로드맵 - 3. 상속/3.1 상속관계

뇌이비 2022. 12. 20. 14:20

 

1. JPA란 무엇인가
     1.1 기본 CRUD
     1.2 기초 지식

2. 연관관계
     2.1 다대일 연관관계 
     2.2 일대다 연관관계 

3. 상속
     3.1 상속관계
     3.2 CASCADE

4. SQL
     4.1 JPQL
     4.2 Querydsl

 

 

 

3. 상속

 

상속은 자바에서 중요한 개념이다.

엔티티 차원에서도 상속할 수 있을까? 

 

RDB에는 상속 개념이 없지만, JPA에서 상속을 구현할 수 있는 전략이 있다. 

 

Mapped SuperClass, Table per Class, Single Table, Joined가 그것이다.

 

 

 

이 중에서 실제 유용한 것은 Single Table과 Joined이다. 

 

 

 

 

3.1 상속관계

 

Publication : 원고, 부모 클래스

Book : 단행본, 자식 클래스1

BlogPost : 포스트, 자식 클래스2

 

부모 클래스를 추상 클래스(abstract class)로 선언하고,

자식 클래스가 이를 상속한다.

 

 

 

Mapped SuperClass

 

부모 클래스를 @Entity로 생성하지 않는다. DB에 테이블이 없다.

부모 클래스를 상속하는 자식 클래스만 테이블을 만들게 된다.

 

따라서 공통 필드를 테이블에 끼워넣을 때 쓴다. (작성자 정보createdBy, 작성 시간 createdOn 등)

 

그러나 부모 클래스가 엔티티가 아니기 때문에

다형성을 적용할 수 없다. (Book/BlogPost 종류에 관계없이 Publication에 대한 쿼리 날릴 수 없다)

 

 

<Publication.class>


@MappedSuperclass // 엔티티를 만들지 않는다
public abstract class Publication { // abstract class

@Id
@GeneratedValue(strategy = GenerationType.Identity)
@Column(name = “PUBLICATION_ID”, updatable = false, nullable = false)
protected Long id;
@Column
protected String title;

@Version
@Column(name = “version”)
private int version;
…
}
<Book.class>

@Entity(name = “Book”)
public class Book extends Publication { // 상속
@Column
private int pages;
…
}



<BlogPost.class>

@Entity(name = “BlogPost”)
public class BlogPost extends Publication { // 상속
@Column
private String url;
…
}

 

 

BOOK 테이블

PUBLICATION_ID title version pages
       

 

 

BLOGPOST 테이블

PUBLICATION_ID title version url
       

 

 

 

 

 

 

Single Table

 

자식 클래스를 하나의 부모 테이블에 저장하는 방식이다.

싱글 테이블만 조회하면 되므로 성능 측면에서 가장 효율적이다.

 

싱글 테이블에서 자식 클래스들을 구분하기 위해

부모 클래스는 @DiscriminatorColumn으로 추가 필드를 생성한다.

자식 클래스는 @DiscriminatorValue으로 추가 필드에서 쓰일 자신의 이름표를 만든다.

 

자식 클래스가 많아서 개별사항이 늘어나면

Null허용 필드가 많아지는 단점이 있다.

 

예를 들어, Book은 자신이 사용하지 않는 url 필드를 Null허용해야 한다.

마찬가지로 BlogPost는 pages 필드를 Null허용해야 한다.

Null허용이 많아질수록 해당 테이블의 데이터 무결성이 감소된다.

 

 

 

<Publication.class>


@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE) // Single Table 
@DiscriminatorColumn(name = “Publication_Type”) // Book과 BlogPost 구분을 위한 추가 필드
public abstract class Publication { // abstract class

@Id
@GeneratedValue(strategy = GenerationType.Identity)
@Column(name = “Publication_id”, updatable = false, nullable = false)
protected Long id;
@Column
protected String title;
@Version
@Column(name = “version”)
private int version;
}

 

<Book.class>


@Entity
@DiscriminatorValue(“Book”) // 추가 필드에 표시되는 이름 
public class Book extends Publication { // 상속
@Column
private int pages;
…
}


<BlogPost.class>

@Entity
@DiscriminatorValue(“Blog”) // 추가 필드에 표시되는 이름 
public class BlogPost extends Publication { // 상속
@Column
private String url;
…
}

 

 

PUBLICATION 테이블

Publication_Type Publication_Id title version pages url
Book / Blog       Null허용 Null허용

 

 

 

 

 

Joined

 

자식 테이블을 각자의 테이블에 저장하는 방식이다.

 

Table per Class와 비슷하다.

차이점은 부모 테이블이 엔티티가 될 뿐만 아니라 DB 테이블이 된다는 것이다.

 

Single Table의 Null허용 단점이 보완된다.

공통사항은 부모 엔티티에 있고, 개별사항은 자식 엔티티에 있기 때문이다.

 

그러나 부모 엔티티도 테이블을 갖고 연관관계를 맺기 때문에

자식 엔티티를 조회할 때 부모 엔티티와 join하며 쿼리 복잡도가 증가한다.

 

 

 

<Publication.class>


@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE) // Joined
@DiscriminatorColumn(name = “Publication_Type”) // Book과 BlogPost 구분을 위한 추가 필드
public abstract class Publication { // abstract class

@Id
@GeneratedValue(strategy = GenerationType.Identity)
@Column(name = “Publication_id”, updatable = false, nullable = false)
protected Long id;
@Column
protected String title;
@Version
@Column(name = “version”)
private int version;
}

 

PUBLICATION 테이블

Publication_Type Publication_Id title version
       

 

 

BOOK 테이블

pages Publication_Id
   

 

 

BLOGPOST 테이블

url Publication_Id
   

 

 

 

 

 

Table per Class

 

Mapped Superclass에서는 부모 클래스 엔티티가 존재하지 않았지만,

Table per Class에서는 부모 클래스 엔티티도 존재한다.

 

상속될 공통사항이 부모 엔티티, 자식 엔티티들에 중복으로 존재하게 된다.

 

 

-> 잘 이해가 안 가는데, 거의 안 쓰인다고 한다.

아마 아래와 같이 중복이 발생해서 비효율적일듯?

 

 

PUBLICATION 테이블

Publication_Id title version
     

 

 

BOOK 테이블

Publication_Id title version pages
       

 

 

BLOGPOST 테이블

Publication_Id title version url
       

 

 

 

 

 

 

Mapped Superclass
: 자식 클래스만 엔티티가 되어 각자의 테이블을 만든다. 
자식 테이블마다 공통사항과 개별사항이 존재한다.

Single Table
: 부모, 자식 클래스 모두 엔티티가 되고 자식 엔티티는 하나의 부모 테이블 밑으로 들어간다. 
공통사항과 개별사항 모두 하나의 부모 테이블에 존재한다.

Joined
: 부모, 자식 클래스 모두 엔티티가 되고 부모, 자식 엔티티 모두 각자의 테이블을 만든다. 
공통사항은 부모 테이블에, 개별사항은 자식 테이블에 존재한다.

 

 

 

 

그렇다면 상속관계를 어떻게 맺어야 하는 것일까?

 

고성능, 쿼리 다형성이 필요한 경우 -> Single Table

무결성이 중요한 경우 -> Joined

 

 

 

 

Comments