고급 매핑

born_a·2022년 9월 6일
0

상속관계 매핑

관계형 db는 슈퍼타입, 서브타입 관계라는 모델링 기법이 있는데 이 기법이 객체 상속과 유사하다.
상속 관계 매핑이란 객체의 상속, 구조와 DB의 슈퍼타입 관계를 매핑하는 것이다.

공통적인 속성은 물품에 두고 각각에 맞는 데이터들은 아래로 내린다.

조인 전략

ITEM, ALBUM, MOVIE, BOOK 테이블을 만들고 데이터를 나누고 조인으로 구성한다. 앨범의 이름, 가격 등과 같은 정보는 아이템 테이블로 들어가고, 아티스트 정보는 앨범 테이블에 들어간다. 따라서 두번의 insert를 하고, 조회는 pk,fk로 조인을 해서 가져옴. DTYPE이란 걸 넣어서 구분.

단일 테이블 전략

논리 모델을 한 테이블로 합친다.
pk그대로 두고 컬럼으로 다 때려넣어.
DTYPE이라는 컬럼명을 만들고 이걸로 구분한다.

구현 클래스마다 테이블 전략

ALBUM,MOVIE,BOOK 테이블만 만든다. NAME, PRICE정보를 각각 다 가진다.

DB에서 세가지 중 어느 방식으로 구현하더라도 JPA에서는 다 매핑이 가능하다.
->상속관계 매핑

Item.java

@Entity
public class Item {
    @Id @GeneratedValue
    @Column(name = "ITEM_ID")
    private Long Id;
    private String name;
    private int Price;

Album.java

@Entity
public class Album extends Item{
    private String artist;
}

Book.java

@Entity
public class Book extends Item{

    private String author;
    private String isbn;
}

Movie.java

@Entity
public class Movie extends Item{
    private String director;
    private String actor;

}


기본적으로 JPA가 단일테이블 전략을 사용해서 Item에 컬럼으로 때려넣은걸 볼 수 있다.

이제 전략을 바꿔보자

조인 전략 적용

Item.java

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

@Inheritance를 넣어주자.

테이블이 각각 생성된것을 확인할 수 있다.

데이터를 넣어서 확인해보자.

JpaMain.java
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("a");
            movie.setActor("bbb");
            movie.setName("바람과 함께 사라지다");
            movie.setPrice(10000);

            em.persist(movie);

            tx.commit();
        } catch (Exception e) {
            tx.rollback();

        }finally {
            em.close();
        }

        emf.close();
    }
}


movie를 가져올려면 item을 조인해서 가져와야한다.

try {
            Movie movie = new Movie();
            movie.setDirector("a");
            movie.setActor("bbb");
            movie.setName("바람과 함께 사라지다");
            movie.setPrice(10000);

            em.persist(movie);

            em.flush();
            em.clear();//영속성 컨텍스트에 있는걸 db로 날리고,영속성 컨텍스트에 있는걸 깔끔하게 제거하니까 1차캐시에 아무것도 안남음.

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

            tx.commit();
        }


select쿼리가 제대로 나간다.

에러발생시 참조 : https://www.inflearn.com/questions/52796

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

@DiscriminatorColumnd을 추가하면

DTYPE이 추가된것을 확인 가능.
업로드중..
DTYPE에 ALBUM, MOVIE, BOOK 엔티티가 들어옴.
DTYPE은 웬만하면 넣어주는게 좋다.
이름 바꿀 수 있다.(name = "")넣어서!

만약 ALBUM 을 a, MOVIE 를 m 이런식으로 회사 규약상 바꿔야 해! 이러면
자식클래스에 @DiscriminatorValue를 추가한다.

@Entity
@DiscriminatorValue("M")
public class Movie extends Item{

이런식으로!
업로드중..
DTYPE에 M으로 잘 들어가는 것을 확인 가능.

만약 한테이블로 가자! 할 경우 단일 테이블 전략으로 한다.

단일 테이블 전략 적용

Item.java

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

업로드중..
업로드중..

단일 테이블 전략은 @DiscriminationColumn없어도 DTYPE이 생성됨.
DTYPE이 없으면 얘가 뭔지 모르기때문. book인지, movie인지..

구현 클래스마다 테이블 전략 적용

Item.java

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

Item이 단독적으로 쓰일수도 있다는 것이기 때문에 테이블 생성이 된다.
원래 처음부터 추상 클래스로 만들었어야 함!!
따라서 abstract class로 바꿔줘야한다.

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

업로드중..

이 전략은 단순하게 값을 넣고 빼기엔 좋다.
하지만 조회할때가 문제다.
부모 타입으로 조회하면 데이터를 다 뒤져봐야 하므로 복잡한 쿼리가 나간다.

JpaMain.java

try {
            Movie movie = new Movie();
            movie.setDirector("a");
            movie.setActor("bbb");
            movie.setName("바람과 함께 사라지다");
            movie.setPrice(10000);

            em.persist(movie);

            em.flush();
            em.clear();//영속성 컨텍스트에 있는걸 db로 날리고,영속성 컨텍스트에 있는걸 깔끔하게 제거하니까 1차캐시에 아무것도 안남음.

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

            tx.commit();
        }

업로드중..
jpa는 union all로 다 뒤짐.

각 전략의 장단점

조인 전략

외래키 참조 무결성 제약조건 활용가능 : 주문에서 외래키 참조로 id를 봐야하면, ITEM 테이블의 ITEM_ID만 보면 된다.

단점 -> 별로 큰 단점이 아님.

단일 테이블 전략

구현 클래스마다 테이블 전략

얘는 쓰면 안되는 전략이다.
변경이라는 관점에서 볼 때, 되게 안좋다. 시스템에 새로운 타입이 추가될때 많은 부분을 고쳐야한다.

결론

조인 전략을 기본으로 하되, 너무 단순한 경우 단일 테이블로 가자

Mapped Superclass - 매핑 정보 상속

모든 테이블에 누가 생성했고, 수정했는지 이런 정보를 넣어야한다고 가정해보자.
MappedSuperclass는 단순히 여기에 있는 속성 같이 쓰고싶어~ 해서 사용한다! 상속 관계 매핑 아니다! Entity가 아니라서 테이블 생성이 안된다!

BaseEntity.java

@MappedSuperclass //매핑 정보만 받는 부모클래스
public class BaseEntity {
    private String createdBy;
    private LocalDateTime createdDate;
    private String lastModifiedBy;
    private LocalDateTime lastModifiedDate;

    public String getCreatedBy() {
        return createdBy;
    }

Member.java

@Entity
public class Member extends BaseEntity{

Team.java

@Entity
public class Team extends BaseEntity{

JpaMain.java

try {
            Member member = new Member();
            member.setUsername("user1");
            member.setCreatedBy("kim");
            member.setCreatedDate(LocalDateTime.now());

            em.persist(member);

            em.flush();
            em.clear();//영속성 컨텍스트에 있는걸 db로 날리고,영속성 컨텍스트에 있는걸 깔끔하게 제거하니까 1차캐시에 아무것도 안남음.

            tx.commit();
        } 

업로드중..
업로드중..

공통적으로 들어가 있는 것을 확인 가능

em.find에서 BaseEntity.class로 조회 불가!

실전 예제 - 4.상속관계 매핑

요구사항 추가

  • 상품의 종류는 음반, 도서, 영화가 있고 이후 더 확장될 수 있다. -> 상속관계 매핑 쓰자
  • 모든 데이터는 등록일과 수정일이 필수다. ->MappedSuperclass 쓰자

0개의 댓글

관련 채용 정보