Spring JPA 상속 관계 매핑

seongcheollee·2023년 12월 9일
0
post-thumbnail

상속 관계 매핑 사용 계기

기존에 프로젝트의 게시글을 담당하는 Feed Entity를 아래와 같이 작성하였다.

@Entity
@Getter
@Setter
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class Feed extends BaseTime{

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @ManyToOne
    @JoinColumn(name = "user_id")
    private Users user;

    @Column(length = 1000, nullable = false)
    private String content;

    private int feedLike;

    @Column(nullable = true)
    private String feedImageUrl;

    private String musicFileName;
    private String musicFileUrl;


    @OneToMany(mappedBy = "feed", fetch = FetchType.LAZY,  cascade = CascadeType.REMOVE)
    @OrderBy("id asc") // 댓글 정렬
    private List<Comment> comments;
}

그러나 회의를 거치면서, 기존에 mp3 파일을 저장하는 방식이 아닌, youtube url의 vedioId를 사용하여 음원을 불러오기로 결정하면서, Feed 엔터티의 변화가 일어났고, 그에 따라 플레이리스트 엔터티를 정의하면서 상당 수의 컬럼이 겹치는 현상이 발생했다.

기존의 Feed는

    private String musicFileUrl; -> private String videoId;

로 컬럼 이름만 변경하였으나, Playlist 엔터티의 경우에는 여러 개의 videoId가 필요하여 아래와 같이 정의했다.

    @ElementCollection
    private List<String> videoIds;

따라서 위의 두 컬럼 외의 나머지 컬럼은 두 엔터티 간의 공통 필드이기 때문에 따로 entity를 생성해서 상속을 해주고자 했다.


문제

RDB에는 객체에서의 상속관계와 같은 상속관계가 없다. DB가 지원하는 상속이 있으나, 객체의 상속과는 거리가 멀다. 바로 슈퍼타입 / 서브타입 관계라는 모델링 기법이 존재하긴 한다.

해결

그렇다면 spring 의 상속관계를 RDB에 적용하려면 어떻게 해야할까?

Spring에서 상속 관계를 RDB(관계형 데이터베이스)에 매핑하는 방법은 일반적으로 객체 관계 매핑(Object-Relational Mapping, ORM) 기술을 사용하여 처리됩니다. Spring에서는 주로 Hibernate, MyBatis, 또는 Spring Data JPA와 같은 ORM 프레임워크를 사용하여 상속 관계를 처리합니다.

JPA에서 상속관계 매핑에 대한 전략을 제공한다.

• JOINED: 조인 전략
• SINGLE_TABLE: 단일 테이블 전략
• TABLE_PER_CLASS: 구현 클래스마다 테이블 전략

각각의 전략에 대해서는 현재 페이지에서는 따로 설명하지는 않겠다.

가장 먼저 배제한 전략은 구현 클래스마다 테이블 전략이다. 이 전략은 데이터베이스 설계자와 ORM 전문가 둘 다 추천하지는 않는 방법이기도 하거니와, 여러 자식 테이블을 함께 조회할 때 성능이 느린 단점이 있다.

다음으로는 단일 테이블 전략이다. 해당 프로젝트의 경우 어떠한 전략을 사용하더라도 유의미한 속도 차이가 발생하지는 않겠지만, sns라는 특성상 DB에 상당한량의 데이터가 쌓일 것을 감안하면, 그다지 유리한 전략은 아니라고 생각했다.

이러한 이유로 최종적으로 선택한 전략은 조인 전략이다.

@Entity
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
@Inheritance(strategy = InheritanceType.JOINED)
@DiscriminatorColumn(name = "ARTICLE_TYPE")

public class Article extends BaseTime {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @ManyToOne
    @JoinColumn(name = "user_id")
    private Users user;

    @Column(length = 1000, nullable = false)
    private String content;

    private int feedLike;

    @Column(nullable = true)
    private String imageUrl;

    @OneToMany(mappedBy = "article", fetch = FetchType.LAZY, cascade = CascadeType.REMOVE)
    @OrderBy("id asc") // 댓글 정렬
    private List<Comment> comments;
}

@Inheritance(strategy = InheritanceType.JOINED)
@DiscriminatorColumn(name = "ARTICLE_TYPE")

를 사용해서 join 전략임을 알려주었고,Dtype을 지정해주었다.

@Entity
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
@DiscriminatorValue("FEED_TYPE")
public class Feed extends Article{

    private String videoId;

}

@Entity
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
@DiscriminatorValue("PLAYLIST_TYPE")

public class Playlist extends Article {

    @ElementCollection
    private List<String> videoIds;

}

Article 엔터티를 Feed, Playlist에 상속해주었다.

이후 Swagger를 사용하여 데이터를 삽입한 결과

mysql에 잘 들어간 것을 확인할 수 있다.

0개의 댓글