JPA(Java Persistence API) 프로그래밍 - 고급 매핑

u-nij·2022년 8월 16일
0

JPA 프로그래밍

목록 보기
6/10
post-thumbnail

이 글은 김영한님의 자바 ORM 표준 JPA 프로그래밍 - 기본편 강의를 듣고 정리한 글입니다.

상속관계 매핑

  • 관계형 데이터베이스는 상속 관계가 없다.
  • 슈퍼타입 서브타입 관계라는 모델링 기법이 객체 상속과 유사
  • 상속관계 매핑 : 객체의 상속 구조와 DB의 슈퍼타입 서브타입 관계를 매핑

  • 슈퍼타입 서브타입 논리 모델을 실제 물리 모델로 구현하는 방법
    • 각각 테이블로 변환 → 조인 전략
    • 통합 테이블로 변환 → 단일 테이블 전략
    • 서브타입 테이블로 변환 → 구현 클래스마다 테이블 전략

주요 어노테이션

  • @Inheritance(strategy=InheritanceType.XXX)
    • JOINED : 조인 전략
    • SINGLE_TABLE : 단일 테이블 전략
    • TABLE_PER_CLASS : 구현 클래스마다 테이블 전략

      @Inheritance의 기본 전략은 단일 테이블 전략이다.

  • @DiscriminatorColumn(name=”DTYPE”): 부모 클래스에 작성. 어떤 엔티티의 것인지 알 수 있는 칼럼.
    • 단일 테이블 전략에서 필수이다.
  • @DiscriminatorValue(”XXX”): 자식 클래스에 작성

    @DiscriminatorValue(”XXX”): 기본적으로 엔티티명이 값으로 들어간다.

    Movie.java에서 값 변경 & 적용 후

    @Entity
    @DiscriminatorValue("M") // DTYPE 값이 변경된다.
    public class Movie extends Item {
        private String director;
        private String actor;
    }

사전 코드 작성

Item.java

@Entity
public abstract class Item { // 추상화 클래스
    @Id
    @GeneratedValue
    private Long id;

    private String name;
    private int price;
}

추상화 클래스 (Abstract Class)
Item 클래스를 바로 생성하게 되면 자식이 없이 부모만 존재하게 된다.
다형성을 위해 추상화 클래스로 만들어주는 것이 적절하다.

Album.java

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

Movie.java

@Entity
public class Movie extends Item { // 상속
    private String director;
    private String actor;
}

Book.java

@Entity
public class Book extends Item { // 상속

    private String author;
    private String isbn;
}

실행 결과

조인 전략

장점

  • 테이블 정규화
  • 외래 키 참조 무결성 제약조건 활용 가능
    (ITEM_ID 사용 가능, ITEM_ID만을 이용해 ITEM만을 볼 수 있음 - 설계 깔끔)
  • 저장공간 효율화

단점

  • 조회시 조인을 많이 사용, 성능 저하
  • 조회 쿼리가 복잡함
  • 데이터 저장시 INSERT SQL 2번 호출
    (단일 테이블 전략에 비해서일 뿐 큰 단점들은 아님)

Item.java

@Entity
@Inheritance(strategy = InheritanceType.JOINED)
@DiscriminatorColumn // 생략 가능하지만, 운영상 있는 것이 좋다.
public abstract class Item {
    @Id
    @GeneratedValue
    private Long id;

    private String name;
    private int price;
}

실행 결과

객체 삽입

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("감독");
            movie.setActor("배우");
            movie.setName("바람과 함께 사라지다");
            movie.setPrice(10000);

            em.persist(movie);

            em.flush();
            em.clear();

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

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

        emf.close();
    }
}

실행 결과

(오류) MOVIE 엔티티에 값이 들어가지 않을 경우
링크를 참고해 pom.xml을 수정한다.

→ 하이버네이트 버전을 5.4.13.Final로 수정했다.

단일 테이블 전략

장점

  • 조인이 필요 없으므로 일반적으로 조회 성능이 빠름
  • 조회 쿼리가 단순함

단점

  • 자식 엔티티가 매핑한 칼럼은 모두 NULL 허용
    (데이터 무결성 입장에서 애매함)
  • 단일 테이블에 모든 것을 저장하므로 테이블이 커질 수 있다. 상황에 따라서 조회 성능이 오히려 느려질 수 있다.

Item.java

@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn // 필수이기 때문에, 생략해도 자동으로 DTYPE이 생성된다.
public abstract class Item {
    @Id
    @GeneratedValue
    private Long id;

    private String name;
    private int price;
}

실행 결과

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

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

장점

  • 서브 타입을 명확하게 구분해서 처리할 때 효과적
  • NOT NULL 제약조건 사용 가능

단점

  • 여러 자식 테이블을 함께 조회할 때 성능이 느림(UNION SQL 필요)
  • 자식 테이블을 통합해서 쿼리하기 어려움

Item.java

@Entity
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
// @DiscriminatorColumn // 의미 X
public abstract class Item {
    @Id
    @GeneratedValue
    private Long id;

    private String name;
    private int price;
}

실행 결과

문제점

            Item findItem = em.find(Item.class, movie.getId());
            System.out.println("findItem = " + findItem);
  • 조회할 때, DB에서 union을 이용해 모든 테이블을 다 찾아야 한다.
    → 비효율적인 동작 방법

정리

  • 전략을 바꿔도 @Inheritance(strategy=InheritanceType.XXX) 어노테이션 이외의 소스코드를 변경하지 않아도 되기 때문에 편리하다.
  • 기본적으로 ‘조인 전략’, 단순하고 확장 가능성이 없을 경우 ‘단일 테이블 전략’
    • ‘조인 전략’과 ‘단일 테이블 전략’ 중 trade off
  • 구현 클래스마다 테이블 전략’은 데이터베이스 설계자와 ORM 전문가 둘 다 추천X

@MappedSuperclass

  • 매핑 정보 상속: 공통 매핑 정보가 필요할 때 사용(id, name)
  • 상속 관계 매핑 아님!
  • 엔티티 아니다. (CREATE 되지 않음.) → 테이블과 매핑이 안 된다.
  • 부모 클래스를 상속 받는 자식 클래스에 매핑 정보만 제공
  • 조회, 검색 불가 (em.find(BaseEntity) 불가)
  • 직접 생성해서 사용할 일이 없으므로 추상 클래스 권장
  • 테이블과 관계가 없고, 단순히 엔티티가 공통으로 사용하는 매핑 정보를 모으는 역할
  • 주로 등록일, 수정일, 등록자, 수정자 같은 전체 엔티티에서 공통으로 적용하는 정보를 모을 때 사용
  • 참고: @Entity 클래스는 '엔티티'나 '@MappedSuperclass로 지정한 클래스'만 상속(extends) 가능

BaseEntity.java

@MappedSuperclass // 매핑 정보만 받는 슈퍼클래스
public abstract class BaseEntity {
    private String createdBy;
    private LocalDateTime createDate;
    private String lastModifiedBy;
    private LocalDateTime lastModifiedDate;
}
  • 나중에 자동화할 수 있다.
  • 중간테이블에는 들어가지 않는다.
  • 상속 관계에서 이미 부모 클래스에서 상속 중이라면 자식 클래스에 넣어줄 필요 없다.

Member.java

@Entity
public class Member extends BaseEntity { // 상속
		// code ...
}

JpaMain.java

        try {
            Member member = new Member();
            member.setCreatedBy("kim");
            member.setCreateDate(LocalDateTime.now());

            em.persist(member);

            em.flush();
            em.clear();

            tx.commit();
        }

실행 결과

상속관계 매핑을 실제로 사용하는가?

  • 상속 관계를 사용해 얻을 수 있는 장&단점이 존재한다.
  • 노다가를 하더라도, 복잡도를 관리한다는 측면에서 더 나을 수 있다.
  • 데이터가 억단위로 넘어가거나, 파티셔닝이 필요하게 된다면 복잡해진다.
  • 우선은 객체지향적으로 진행하고, 장단점을 생각해 trade off
profile
삶은 달걀이다

0개의 댓글