JPA & Querydsl/JPA

5. 고급 매핑

상속관계 매핑

관계형 데이터베이스는 상속 관계X

슈퍼타입 서브타입 관계라는 모델링 기법이 객체 상속과 유사

상속관계 매핑: 객체의 상속과 구조와 DB의 슈퍼타입 서브타입 관계를 매핑

 

1) 슈퍼타입 서브타입 논리 모델을 실제 물리 모델로 구현하는 방법

- 각각 테이블로 변환 -> 조인 전략

- 통합 테이블로 변환 -> 단일 테이블 전략

- 서브타입 테이블로 변환 -> 구현 클래스마다 테이블 전략

 

3가지 전략에서 서브타입의 코드는 동일하여 우선적으로 작성

@Entity
public class Album4 extends Item4 {

    private String artist;

    public String getArtist() {
        return artist;
    }

    public void setArtist(String artist) {
        this.artist = artist;
    }
}
@Entity
public class Movie4 extends Item4 {

    private String director;
    private String actor;

    public String getDirector() {
        return director;
    }

    public void setDirector(String director) {
        this.director = director;
    }

    public String getActor() {
        return actor;
    }

    public void setActor(String actor) {
        this.actor = actor;
    }
}
@Entity
public class Book4 extends Item4 {

    private String AUTHOR;
    private String ISBN;

    public String getAUTHOR() {
        return AUTHOR;
    }

    public void setAUTHOR(String AUTHOR) {
        this.AUTHOR = AUTHOR;
    }

    public String getISBN() {
        return ISBN;
    }

    public void setISBN(String ISBN) {
        this.ISBN = ISBN;
    }
}

2) 주요 어노테이션

@Inheritance(strategy=InheritanceType.XXX) - 전략을 설정

 

- JOINED: 조인 전략

  장점

  1) 테이블 정규화

  2) 외래 키 참조 무결성 제약조건 활용가능

  3) 저장공간 효율화

 

  단점

  1) 조회시 조인을 많이 사용, 성능 저하

  2) 조회 쿼리가 복잡함

  3) 데이터 저장시 INSERT SQL 2번 호출

 

@Entity
@Inheritance(strategy = InheritanceType.JOINED) // 조인 전략
public abstract class Item4 {

    @Id @GeneratedValue
    private Long id;

    private String name;
    private int price;

	...
    }
}

- SINGLE_TABLE: 단일 테이블 전략

  장점

  1) 조인이 필요 없으므로 일반적으로 조회 성능이 빠름

  2) 조회 쿼리가 단순함

 

  단점

  1) 자식 엔티티가 매핑한 컬럼은 모두 null 허용

  2) 단일 테이블에 모든 것을 저장하므로 테이블이 커질 수 있다. 상 황에 따라서 조회 성능이 오히려 느려질 수 있다.

@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE) // 단일 테이블 전략
public abstract class Item4 {

    @Id @GeneratedValue
    private Long id;

    private String name;
    private int price;

	...
    }
}

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

이 전략은 데이터베이스 설계자와 ORM 전문가 둘 다 추천X

 

장점

  1) 서브 타입을 명확하게 구분해서 처리할 때 효과적

  2) not null 제약조건 사용 가능

 

  단점

  1) 여러 자식 테이블을 함께 조회할 때 성능이 느림(UNION SQL 필요)

  2) 자식 테이블을 통합해서 쿼리하기 어려움

@Entity
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS) // 구현 클래스 마다 테이블 전략
public abstract class Item4 {

    @Id @GeneratedValue
    private Long id;

    private String name;
    private int price;

	...
    }
}

 

@DiscriminatorColumn(name=“DTYPE”) - 서브 타입을 구분하는데 사용하는 칼럼의 이름을 결정

@Entity
@Inheritance(strategy = InheritanceType.JOINED)
@DiscriminatorColumn // DTYPE(기본값) 설정
// @DiscriminatorColumn(name=“DT”)
public abstract class Item4 {

    @Id @GeneratedValue
    private Long id;

    private String name;
    private int price;

	...
}

 

@DiscriminatorValue(“XXX”) - 자신을 구분하는 서브타입 칼럼의 값을 설정

@Entity
@DiscriminatorValue("A") // DT값을 A로 설정
public class Album4 extends Item4 {

    private String artist;

    public String getArtist() {
        return artist;
    }

    public void setArtist(String artist) {
        this.artist = artist;
    }
}

 

2. @MappedSuperclass

공통 매핑 정보가 필요할 때 사용

상속관계 매핑X

엔티티X, 테이블과 매핑X

부모 클래스를 상속 받는 자식 클래스에 매핑 정보만 제공

조회, 검색 불가(em.find(BaseEntity) 불가)

직접 생성해서 사용할 일이 없으므로 추상 클래스 권장

테이블과 관계 없고, 단순히 엔티티가 공통으로 사용하는 매핑 정보를 모으는 역할

주로 등록일, 수정일, 등록자, 수정자 같은 전체 엔티티에서 공통 으로 적용하는 정보를 모을 때 사용

@MappedSuperclass
public class BaseEntity4 {

    private String createdBy;
    private LocalDateTime createDate;
    private String lastModifiedBy;
    private LocalDateTime lastModifiedDate;

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

    @Id @GeneratedValue
    private Long id;

    private String name;
    private int price;

    ...
}

 

 

3. 실습 테스트 코드

public class SuperclassTest {
    public static void main(String[] args) {
        // persistence.xml 의 persistence-unit 을 받아서 EntityManagerFactory 생성 - 하나만 생성해서 전체 어플리케이션 공유
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("test04");

        //EntityManagerFactory 에서 EntityManager 생성 - 쓰레드간에 공유 X (사용하고 버려야함)
        EntityManager em = emf.createEntityManager();

        // EntityManager 의 트랜잭션 받아옴
        EntityTransaction tx = em.getTransaction();

        // 트랜잭션 시작 - 모든 데이터 변경은 이 안에서 처리해야함
        tx.begin();

        try {
            /*
            // 상속 관계 매핑
            Movie movie = new Movie();
            movie.setDirector("aaaaa");
            movie.setActor("bbb");
            movie.setName("제목");
            movie.setPrice(1234);
            em.persist(movie);
            em.flush();
            em.clear();
            Movie findMovie = em.find(Movie.class, movie.getId());
            System.out.println(findMovie.getName());
            */

            /*
            // Mapped Superclass
            Movie movie = new Movie();
            movie.setDirector("1111");
            movie.setActor("2222");
            movie.setName("33333");
            movie.setPrice(44444);
            movie.setCreatedBy("관리자");
            movie.setCreateDate(LocalDateTime.now());
            em.persist(movie);
            */

            // 트랜잭션 커밋
            tx.commit();
        } catch (Exception e){
            // 롤백
            tx.rollback();
        } finally {
            // EntityManager 닫음
            em.close();
        }

        // EntityManagerFactory 닫음
        emf.close();
    }
}

 

Reference

자바 ORM 표준 JPA 프로그래밍 - 김영한님

 

'JPA & Querydsl > JPA' 카테고리의 다른 글

7. 값 타입  (0) 2021.02.28
6. 프록시와 연관관계 관리  (0) 2021.02.25
4. 다양한 연관관계 매핑  (0) 2021.02.24
3. 연관관계 매핑 기초  (0) 2021.02.22
2. 엔티티 맵핑  (0) 2021.02.21