[개발] - Spring/JPA 공부

상속관계 매핑

완벽한 장면 2023. 8. 23. 15:13

개념 설명

상속관계 매핑

 

 

정리하자면, DB의 슈퍼타입-서브타입 모델링 기법을 3가지 방법 중 어떤 걸 선택하여 구현해도 다 매핑할 수 있도록 JPA가 지원해줌.

 

 

주요 어노테이션


도입

단일 테이블 전략  (1)

부모 클래스 Item

@Entity
public abstract class Item {

  @Id
  @GeneratedValue
  private Long id;

  private String name;
  private int price;
}

 

Album

@Entity
public class Album extends Item {

  private String artist;

}

 

Book

@Entity
public class Book extends Item {

  private String author;
  private String isbn;
}

 

Movie

@Entity
public class Movie extends Item {

  private String director;
  private String actor;

}

 

이렇게 하고 실행시켜보면

 

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)
    )

이렇게 한 테이블에 다 떼려박혀 나옴.

 


조인 전략

Item 클래스에

@Inheritance(strategy = InheritanceType.JOINED)

이걸 추가하고 

getter, setter 다 만들어준 후

@Entity
@Inheritance(strategy = InheritanceType.JOINED)
public abstract class Item {

  @Id
  @GeneratedValue
  private Long id;

  private String name;
  private int price;

  public Long getId() {
    return id;
  }

  public void setId(Long id) {
    this.id = id;
  }

  public String getName() {
    return name;
  }

  public void setName(String name) {
    this.name = name;
  }

  public int getPrice() {
    return price;
  }

  public void setPrice(int price) {
    this.price = price;
  }
}

 

JPAMain을 통해서 

Movie에 값을 넣어준다.

그 전에 Movie에도 getter, setter 추가.

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 {
      Movie movie = new Movie();
      movie.setDirector("김AB");
      movie.setActor("한BC");
      movie.setName("무빙");
      movie.setPrice(10000);

      em.persist(movie);


      tx.commit();
    } catch (Exception e) {
      tx.rollback();
    } finally {
      em.close();
    }
    emf.close();
  }
}

 

콘솔 출력 결과는

 

 

이제 조회를 해보면.

일단 movie를 가져오려면 item을 조인해 가져와야 한다는 걸 인지하고 있자.

 

//조회해보기 - 먼저 1차캐시 날리기
em.flush();
em.clear();

Movie findMovie = em.find(Movie.class, movie.getId());
System.out.println("findMovie = "+ findMovie);

 

콘솔을 보면

Hibernate: 
    select
        movie0_.id as id1_2_0_,
        movie0_1_.name as name2_2_0_,
        movie0_1_.price as price3_2_0_,
        movie0_.actor as actor1_5_0_,
        movie0_.director as director2_5_0_ 
    from
        Movie movie0_ 
    inner join
        Item movie0_1_ 
            on movie0_.id=movie0_1_.id 
    where
        movie0_.id=?

 

 

findMovie = inflearn.exjpa.jpaExample.item.Movie@127705e4

 

 

JPA가 이런 것 까지 다 해준다.

조회할 때 Join이 필요하면 Join 해주고,

insert할 때 2번 insert해야 하면 그거까지 jpa가 챙겨서 다 해준다.

 

 

 

이제 Item 클래스에

@Entity
@Inheritance(strategy = InheritanceType.JOINED)
@DiscriminatorColumn
public abstract class Item { }

@DiscriminatorColumn 추가하면

 

Hibernate: 
    
    create table Item (
       DTYPE varchar(31) not null,
        id bigint not null,
        name varchar(255),
        price integer not null,
        primary key (id)
    )

DTYPE 들어와 있고,

 

H2 가보면

추가되어있음.

 

@DiscriminatorColumn 추가하면

DTYPE 이 생기고

그 안에 엔티티 명이 들어가게 된다.

(@DiscriminatorColumn(name = "~~~") 이렇게 이름을 직접 지정해줄 수도 있다.

 

사실 안 넣어도 무방하긴 하나, 운영하거나 DB만 딱 쿼리를 확인했을 때는 얘가 뭐 때문에 들어왔는지를 모른다.

그리고 세부사항을 각각 이름 지정해서 넣고 싶다면 사용하는 어노테이션이 

@DIscriminatorValue("이름")

 

@Entity
@DiscriminatorValue("A")
public class Album extends Item { }
@Entity
@DiscriminatorValue("B")
public class Book extends Item { }
@Entity
@DiscriminatorValue("M")
public class Movie extends Item { }

 

이렇게 들어감을 확인할 수 있음.


조인 전략 개념 정리

조인 전략

 

 

장점과 단점

 

 


단일 테이블 전략

 

장점과 단점

 


단일 테이블 전략 실습

@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn
public abstract class Item { }

@Inheritance(strategy = InheritanceType.SINGLE_TABLE)

이렇게 바꿔놓고 돌리면 된다.

 

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 쿼리도 한 번만 나간다.

 

Hibernate: 
    /* insert inflearn.exjpa.jpaExample.item.Movie
        */ insert 
        into
            Item
            (name, price, actor, director, DTYPE, id) 
        values
            (?, ?, ?, ?, 'M', ?)

 

select 쿼리도 아주 심플하게 나간다.

 

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'

Join 필요없다.

 

장점 : 성능

- 인서트 한 번만 들어가고, 조인 필요 없고, select도 깔끔함.

 

@DiscriminatorColumn 이게 없어도 DTYPE은 생성된다.

다른 전략은 어떻게든 어떤 테이블에 들어가는지를 확인할 수 있는데, 

얘는 한 테이블에 다 때려박기 때문에  JPA가 구분을 위해 DTYPE을 넣어준다 필수 로.

 


구현 케이스마다 테이블 전략

설계도

 


구현 케이스마다 테이블 전략 실습

@Entity
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
@DiscriminatorColumn
public abstract class Item { //원래 abstract class로 만들었어야 한다.

}

어노테이션을

@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)

이걸로 바꿔준다.

 

그러면 Item클래스 따로 생성이 안 된다.

그러면 @DiscriminatorColumn 도 있어도 쓸모가 없다.

 

값을 뺄 때고 넣을 때는 좋은데,

조회할 때가 문제

 

JPAMAIN

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 {
      Movie movie = new Movie();
      movie.setDirector("김AB");
      movie.setActor("한BC");
      movie.setName("무빙");
      movie.setPrice(10000);

      em.persist(movie);

      //조회해보기 - 먼저 1차캐시 날리기
      em.flush();
      em.clear();
      
      Item findItem = em.find(Item.class, movie.getId());
      System.out.println("item = " + findItem);

      tx.commit();
    } catch (Exception e) {
      tx.rollback();
    } finally {
      em.close();
    }
    emf.close();
  }
}

      Item findItem = em.find(Item.class, movie.getId());
      System.out.println("item = " + findItem); 이렇게 찾으려고 하면

 

Hibernate: 
    select
        item0_.id as id1_2_0_,
        item0_.name as name2_2_0_,
        item0_.price as price3_2_0_,
        item0_.artist as artist1_0_0_,
        item0_.author as author1_1_0_,
        item0_.isbn as isbn2_1_0_,
        item0_.actor as actor1_5_0_,
        item0_.director as director2_5_0_,
        item0_.clazz_ as clazz_0_ 
    from
        ( select
            id,
            name,
            price,
            artist,
            null as author,
            null as isbn,
            null as actor,
            null as director,
            1 as clazz_ 
        from
            Album 
        union
        all select
            id,
            name,
            price,
            null as artist,
            author,
            isbn,
            null as actor,
            null as director,
            2 as clazz_ 
        from
            Book 
        union
        all select
            id,
            name,
            price,
            null as artist,
            null as author,
            null as isbn,
            actor,
            director,
            3 as clazz_ 
        from
            Movie 
    ) item0_ 
where
    item0_.id=?

 

이렇게 일일이 union 해서 전체에서 다 뒤져야 한다...

 


강의를 마치며 다시 정리

 

728x90
반응형