본문 바로가기

Spring/JPA

JPA- 상속관계 매핑

 기본적으로 테이블에는 상속관계라는 개념이 존재하지 않는다. 따라서 우리가 객체적으로는 상속관계라고 하더라도 테이블 상에서는 그렇지 않을 수 있는데 이런 것을 가능하게 해주는 것이 @Inheritance 어노테이션이다. 이 또한 상속은 아니고 슈퍼타입, 서브타입을 통해서 상속과 비슷한 역할을 한다.

@Inheritance

 위의 어노테이션을 사용해서 상속관계에 있는 엔티티들을 테이블상에서 매핑할 수 있다.

예를들어, Item을 상속하는 Album, Movie, Book 클래스가 있다고하자.

  - @Inheritance 어노테이션을 사용하지 않은 경우

Item.class

@Entity
@Getter @Setter
public abstract class Item {

    @Id @GeneratedValue
    private Long id;

    private String name;
    private int price;
}

Album.class

@Entity
@Getter
@Setter
public class Album extends Item {

    private String artist;
}

Book.class

@Entity
@Getter 
@Setter
public class Book extends Item{

    private String author;
    private String isbn;
}

Movie.class

@Entity
@Getter
@Setter
public class Movie extends Item{

    private String director;
    private String actor;
}

이 상태로 Item 객체를 persist 한다면 무슨일이 일어날까? 참고로 코드의 길이를 위해 클래스들의 초기화 메소드는 표기를 생략했다.

싱글 테이블 전략 (성능 훌륭)

public class JpaMain {
    public static void main(String[] args) {
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
        EntityManager em = emf.createEntityManager();
        EntityTransaction tx = em.getTransaction();
        tx.begin();

        try {
            Album album = new Album();
            album.initAlbum("albumA",30000,"beatles");
            Book book = new Book();
            book.initBook("bookA", 10000, "philip dick","fa23d5354sz45");
            Movie movie = new Movie();
            movie.initMovie("movieA", 12000, "Quentin Jerome Tarantino", "Leonardo Wilhelm Dicaprio");

            em.persist(album);
            em.persist(book);
            em.persist(movie);

            tx.commit();
        } catch (Exception e) {
            tx.rollback(); //오류 발생 시 롤백
        } finally {
            //종료
            em.close();
        }
        emf.close();
    }
}

ITEM 테이블만 생성되고 자식 클래스 테이블들은 생성되지 않은 모습

 재밌게도 지금보면 Item의 하위 클래스들은 테이블이 생성되지 않은 반면에 상위클래스인 ITEM은 테이블이 생성되었다. 또한 Item안에 Album(artist), Book(author, isbn), Movie(actor, director)의 필드가 모두 들어가 있는 것을 확인가능하다. 이렇게 슈퍼타입에 여럿 서브타입의 모든 정보를 담아두는 것을 싱글테이블 전략이라고 한다. 

 

 참고로 Item 테이블의 sql 문은 다음과 같다.

ITEM에 서브타입들의 필드가 주입

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)
    )
Hibernate: 
    /* insert hellojpa.Album
        */ insert 
        into
            Item
            (name, price, artist, DTYPE, id) 
        values
            (?, ?, ?, 'Album', ?)
Hibernate: 
    /* insert hellojpa.Book
        */ insert 
        into
            Item
            (name, price, author, isbn, DTYPE, id) 
        values
            (?, ?, ?, ?, 'Book', ?)
Hibernate: 
    /* insert hellojpa.Movie
        */ insert 
        into
            Item
            (name, price, actor, director, DTYPE, id) 
        values
            (?, ?, ?, ?, 'Movie', ?)

  - @Inheritance 어노테이션 사용

 이번엔 모든 내용은 동일하고 Item에 @Inheritance 어노테이션만 추가해보자.

어노테이션이 없을때랑 똑같음

 위에서 봤다시피 @Inheritance 어노테이션을 추가하더라도 바뀐게 아예 없다. sql문도 완전히 동일하다. 이는 

@Inheritance의 기본전략이 싱글테이블 전략이기 때문이다. 

조인 전략 (가장 추천)

 사실 싱글테이블 전략으로는 우리가 기본적으로 보는 것과 같은 상속관계를 표현할 수 없다. 그래서 우리는 조인 전략을 사용한다.

@Entity
// 조인 전략: 자식 클래스(엔티티)들 마다 별도의 테이블 제공, 안하면 싱글테이블 전략이 디폴트
@Inheritance(strategy = InheritanceType.JOINED)
@Getter @Setter
public abstract class Item {

    @Id @GeneratedValue
    private Long id;

    private String name;
    private int price;
}

JOIN 전략으로 상속관계를 표현

@Inheritance의 strategy를 InheritanceType.JOINED로 설정함으로써 JOIN 전략의 사용이 가능하다. 그렇게 되는 경우 위에서와 같이 상속관계를 표현할 수 있다.

조인이 베스트!!

@DiscriminatorColumn

DTYPE을 확인하기 위해선 상위 클래스(Item)에 위 어노테이션을 붙여주면 된다.

@Entity
// 조인 전략: 자식 클래스(엔티티)들 마다 별도의 테이블 제공, 안하면 싱글테이블 전략이 디폴트
@Inheritance(strategy = InheritanceType.JOINED)
@Getter
@DiscriminatorColumn  // DTYPE 명시
public abstract class Item {

    @Id @GeneratedValue
    @Column(name = "ITEM_ID")
    private Long id;

    private String name;
    private int price;

    public void initItem(String name, int price) {
        this.name = name;
        this.price = price;
    }
}

실행 결과는 다음과 같다.

DTYPE 칼럼이 새로 생김

 DTYPE 칼럼을 통해서 조인된 자식 엔티티를 확인 가능하다. DTYPE 칼럼명도 name = "원하는 이름" 속성을 통해서 변경이 가능하다.

@DiscriminatorColumn(name = "ITEM_TYPE")

 싱글테이블의 경우 DTYPE이 자동으로 들어간다.

@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@Getter
// @DiscriminatorColumn 없음
public class Item {

    @Id @GeneratedValue
    @Column(name = "ITEM_ID")
    private Long id;

    private String name;
    private int price;
}

자동으로 DTYPE이 명시

 

'Spring > JPA' 카테고리의 다른 글

JPA- 프록시  (0) 2023.03.16
JPA- @MappedSuperClass  (0) 2023.03.12
JPA- 다대일, 일대일  (0) 2023.03.08
JPA- 연관관계 매핑 주의점  (0) 2023.03.05
JPA- 연관관계 매핑  (0) 2023.03.05