ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 고급 매핑
    JPA 2022. 7. 30. 08:03

     

    해당 내용은

    자바 ORM 표준 JPA 프로그래밍 - 기본편 (https://www.inflearn.com/course/ORM-JPA-Basic/dashboard)

    강의를 듣고 정리한 포스트입니다.

     

     

    목차

    • 상속관계 매핑

    • @MappedSuperclass

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

     

     

     

     

    상속관계 매핑

     

    상속관계 매핑

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

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

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

    관계를 매핑

    논리모델 물리모델

    공통적인 속성이 있다.

    물품은 , 특징적 값은 따로 내린다.

     

     

    상속관계 매핑

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

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

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

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

     

    주요 어노테이션

    • @Inheritance(strategy=InheritanceType.XXX)

    • JOINED: 조인 전략

    • SINGLE_TABLE: 단일 테이블 전략

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

    • @DiscriminatorColumn(name=“DTYPE”)

    • @DiscriminatorValue(“XXX”)

     

    조인 전략

     

    RDB에서 어떻게 가져가냐

    슈퍼 타입인 ITEM_ID 둔다

    서브 타입 테이블은 슈퍼 타입의 PK ITEM_ID , PK,FK 둔다.

    INSERT할때 2 INSERT 한다.

    대신에 PK만으로는 이게 어떤 물품인지 알수 없으므로

    구분자인 DTYPE 둔다.

     DTYPE 통해 DTYPE 엘범이면 ALBUM 테이블에 조인하여 가져온다.

     

    조인 전략

    • 장점

    • 테이블 정규화

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

    • 저장공간 효율화

    • 단점

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

    • 조회 쿼리가 복잡함

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

     

     

    단일 테이블 전략

     

    • 장점

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

    • 조회 쿼리가 단순함

    • 단점

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

    • 단일 테이블에 모든 것을 저장하므로 테이블이 커질 수 있다. 상

    황에 따라서 조회 성능이 오히려 느려질 수 있다.

     

     

     

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

     

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

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

    • 장점

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

    • not null 제약조건 사용 가능

    • 단점

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

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

     

    예제

    @Entity
    public class Item {
        @Id @GeneratedValue
        private Long id;


        private String name;
        private int price;


    }

     

    @Entity
    public class Book extends Item{
        private String author;
        private String isbn;
    }

     

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

     

    이런식으로 했을

     JPA 단일 테이블 전략을 가져간다. 이런식으로 명시적으로 해도됨

    SINGLE_TABLE: 단일 테이블 전략

    단일 테이블 전략일경우 @DiscriminatorColumn 애너테이션이 없어도

    DTYPE 컬럼이 자동생성된다.

    @Entity
    @Inheritance(strategy = InheritanceType.SINGLE_TABLE)
    @DiscriminatorColumn
    public class Item {
        @Id @GeneratedValue
        private Long id;

    테이블생성문

        create table Item (
           DTYPE varchar(31) not null,
            id bigint not null,
            name varchar(255),
            price integer not null,
            atrist varchar(255),
            author varchar(255),
            isbn varchar(255),
            actor varchar(255),
            director varchar(255),
            primary key (id)
        )
    Hibernate

     

     

    조인전략을 선택할 있다.

     

     

    @Entity
    @Inheritance(strategy = InheritanceType.JOINED)
    public class Item {
        @Id @GeneratedValue
        private Long id;


        private String name;
        private int price;
    }

    요런식으로 경우  조인 전략을 가져간다

     

        create table Album (
           atrist varchar(255),
            id bigint not null,
            primary key (id)
        )
    Hibernate:
       
        create table Book (
           author varchar(255),
            isbn varchar(255),
            id bigint not null,
            primary key (id)
        )
    Hibernate:
       
        create table Item (
           id bigint not null,
            name varchar(255),
            price integer not null,
            primary key (id)
        )
     Hibernate:
       
        create table Movie (
           actor varchar(255),
            director varchar(255),
            id bigint not null,
            primary key (id)
        )

     

             Movie movie = new Movie();
                movie.setDirector("aaaa");
                movie.setActor("bbbb");
                movie.setName("아이언맨");
                movie.setPrice(10000);
                em.persist(movie);

    insert 2 나간다.

    item,Movie

     

    select 할경우 조인해서 가져온다.

     

           Movie movie = new Movie();
                movie.setDirector("aaaa");
                movie.setActor("bbbb");
                movie.setName("아이언맨");
                movie.setPrice(10000);
                em.persist(movie);


                em.flush();
                em.clear();
                Movie findMovie = em.find(Movie.class, movie.getId());
                System.out.println("findMovie.getName() = " + findMovie.getName());
                tx.commit();

     

       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=?

    근데 구분하는 DType 컬럼이 없음

    Item 클래스에

    @DiscriminatorColumn 넣음

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

     

     

    기본 DTYPE 들어가는 값은 Default Entity 명이다.

     

    만약에 해당 들어가는 값을 바꾸고 싶으면

    서브 클래스에 아래와같은 어노테이션을 붙이면된다. (개인적으루 추천은 안함 , 회사 규정일경우)

    @DiscriminatorValue("M")

    그러면 이런식으로 들어감

        /* insert hello.jpa.Movie
            */ insert
            into
                Item
                (name, price, DTYPE, id)
            values
                (?, ?, 'M', ?)

     

    DTYPE 운영상 있는게 좋다.

     

     

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

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

     

      
        create table Album (
           id bigint not null,
            name varchar(255),
            price integer not null,
            atrist varchar(255),
            primary key (id)
        )
    Hibernate:
       
        create table Book (
           id bigint not null,
            name varchar(255),
            price integer not null,
            author varchar(255),
            isbn varchar(255),
            primary key (id)
        )


    Hibernate:
       
        create table Movie (
           id bigint not null,
            name varchar(255),
            price integer not null,
            actor varchar(255),
            director varchar(255),
            primary key (id)
        )

     

    요런식으로 생긴다.

    abstract 클래스로 상위클래스로 변환해야한다.

    단일 클래스로 만들면 해당 Item 테이블 자체를 있기 때문에

    @Discriminate 전략이 필요가 없다 .

     

    단순하게 값을 넣고 뺄때는 좋다

    언제 망하냐

    조회를 하는데

    부모클래스 타입으로 해당 객체를 가져올때

    select union All 전체 테이블을 뒤져야한다.

     

    장단점 설명

    조인전략

    • 데이터가 정규화 되어있음
    • 조금 성능이 안나온다.

    조인전략이 정석이라고 생각해야한다.

    단일테이블전략

    • 조회성능이 빠름
    • 자식 entity 컬럼은 모두 null 허용

     

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

    • 쓰면 안되는 전략
    • 둘다 싫어함 ,DB 설계자, ORM  전문가 추천안함
    • 정산이 만약에 추가될 마다 코드를 고쳐야됨
    • 변경이라는 관점에서 굉장히 좋지 않음

     

     

     

    @MappedSuperclass

     

    @MappedSuperclass

    • 공통 매핑 정보가 필요할 사용 (id, name)

     

    객체 입장에서 귀찮아서 공통 속성을 상속해서 사용하고 싶을 사용

    DB 따로 나눠져 있다.

     

    만약에 DB에서 테이블에 누가 수정했는지 언제 수정했는지에 관한 데이터가 있어야한다.

    (DBA 정했다 치고)

     

    그러면 객체에 관련 멤버정보를 넣어줘야한다.

     

    그럴때 MappedSuperClass 이용한다.

    @MappedSuperclass
    public class BaseEntity {
        private String createdBy;
        private LocalDateTime createdDate;
        private String lastModifiedBy;
        private LocalDateTime lastModifiedDate;

     

    @Entity
    public class Member extends  BaseEntity{


        @Id @GeneratedValue
        @Column(name="MEMBER_ID")
        private Long id;
        @Column(name = "USER_NAME")
        private String name;

     

    @Entity
    public class Team extends BaseEntity{
        @Id @GeneratedValue
        @Column(name = "TEAM_ID")
        private Long id;
        private String name;

     

    테스트를 해본다.

        public static void main(String[] args) {
            EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");


            EntityManager em = emf.createEntityManager();


            EntityTransaction tx = em.getTransaction();
            tx.begin();
            try {
                Member member = new Member();
                member.setName("kim");
                member.setCreatedBy("kim");
                member.setCreatedDate(LocalDateTime.now());
                em.persist(member);
                em.flush();
                em.clear();
                tx.commit();

     

    실행시 create 되는 테이블

        create table Team (
           TEAM_ID bigint not null,
            createdBy varchar(255),
            createdDate timestamp,
            lastModifiedBy varchar(255),
            lastModifiedDate timestamp,
            name varchar(255),
            primary key (TEAM_ID)
        )
       
        create table Member (
           MEMBER_ID bigint not null,
            createdBy varchar(255),
            createdDate timestamp,
            lastModifiedBy varchar(255),
            lastModifiedDate timestamp,
            USER_NAME varchar(255),
            LOCKER_ID bigint,
            TEAM_ID bigint,
            primary key (MEMBER_ID)
        )

     

    @MappedSuperclass

    • 상속관계 매핑X

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

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

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

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

     

    @MappedSuperclass
    public abstract class BaseEntity {

    누군가 실수 있으므로 추상클래스로 생성권장  (캡슐화와도 같다.)

     

    @MappedSuperclass

    • 테이블과 관계 없고, 단순히 엔티티가 공통으로 사용하는 매핑

    정보를 모으는 역할

    • 주로 등록일, 수정일, 등록자, 수정자 같은 전체 엔티티에서 공통

    으로 적용하는 정보를 모을 때 사용

    • 참고: @Entity 클래스는 엔티티나 @MappedSuperclass로 지

    정한 클래스만 상속 가능

     

     

     

     

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

     

    요구사항 추가

    • 상품의 종류는 음반, 도서,영화가 있고 이후 확장 있다.
    • 모든 데이터는 등록일과 수정일이 필수다.

     

     

    도메인 모델

    도메인 상세 모델

    테이블 설계

     

    구조에 맞춰 객체를 생성해준다.

     

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

     

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

     

    @Entity
    public class Book extends Item{
        private String author;
        private String isbn;

     

    후에  Item 테이블 자체를 단독으로 사용할 있는지 고려해야한다

    예제에서는 단독으로 사용할 일이 없다고 가정한다.

     

    @Entity
    @Inheritance(strategy = InheritanceType.SINGLE_TABLE)
    @DiscriminatorColumn
    public abstract class Item {
        @Id
        @GeneratedValue
        @Column(name = "ITEM_ID")
        private Long id;
        private String name;
        private int price;
        private int stockQuantity;

     

    기억이 애매해서 복습 개념

     

    슈퍼타입 서브타입 논리 모델 -> 물리모델 구현 방법

    • 객체는 상속을 지원하므로 모델링과 구현이 똑같지만, DB는 상속을 지원하지 않으므로 논리 모델을 물리 모델로 구현할 방법이 필요하다.
    • @Inheritance(strategy=InheritanceType.XXX)의 stategy를 설정해주면 된다.
    • default 전략은 SINGLE_TABLE(단일 테이블 전략)이다.
    • @DiscriminatorColumn(name="DTYPE")
    • @DiscriminatorValue("XXX")

    ------------------------------------------------------------------------------------------------------------------------------

     

    SingleTable 전략이므로 Item 테이블에 해당 값이 전부 들어감

     

    create table Item (
           DTYPE varchar(31) not null,
            ITEM_ID bigint not null,
            name varchar(255),
            price integer not null,
            stockQuantity integer not null,
            actor varchar(255),
            director varchar(255),
            author varchar(255),
            isbn varchar(255),
            artist varchar(255),
            etc varchar(255),
            primary key (ITEM_ID)
        )

     

    @MappedSuperclass

    적용 


    @MappedSuperclass
    public abstract class BaseEntity {
        private String createBy;
        private LocalDateTime createDate;
        private String lastModifiedBy;
        private LocalDateTime lastModifiedDate;

     

    해당 사용하는 클래스를 extends 해준다.

    public abstract class Item extends BaseEntity{
     

     

    실전에서 상속 관계를 사용하는 하느냐

    그냥 싱글테이블로하고 해당 데이터를 json으로 말아넣느냐.

    정답은 없다 .

    객체지향적으로 가다가

    장점과 단점이 트레이드 오프하는 시점에 바꿔야한다.

     

     

     

    -------------------------------------------------------------------------------------------

     

     

     

    'JPA' 카테고리의 다른 글

    프록시와 연관관계 관리  (0) 2022.08.06
    다양한 연관관계 매핑  (0) 2022.07.29
    연관관계 매핑 기초  (0) 2022.07.27
    엔티티 매핑  (0) 2022.07.26
    영속성 관리 - 내부 동작 방식  (0) 2022.07.26
Designed by Tistory.