WEB/JPA

김영한 (ORM 표준 JPA 프로그래밍 7) 고급 매핑

Tony Lim 2021. 3. 13. 15:58
728x90

 

상속관계 매핑

관계형 데이터베이스는 상속관계가 없다. 대신 슈퍼타입 서브타입 관계라는 모델링 기법이 유사하다.

 

주요 어노테이션

@Inheritance(strategy = InheritanceType.XXX)

JOINED = 조인 전략

package hellojpa;


import javax.persistence.*;

@Entity
@Inheritance(strategy = InheritanceType.JOINED)
@DiscriminatorColumn
public 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;
    }
}
package hellojpa;

import javax.persistence.Entity;

@Entity
public class Movie extends Item
{
    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;
    }
}
        try
        {
            Movie movie = new Movie();
            movie.setDirector("aaaa");
            movie.setActor("bbb");
            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();
        }
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_6_0_,
        movie0_.director as director2_6_0_ 
    from
        Movie movie0_ 
    inner join
        Item movie0_1_ 
            on movie0_.id=movie0_1_.id 
    where
        movie0_.id=?
findMovie = hellojpa.Movie@61bcbcce

insert 할시에도 2번 하게 된다. Item, Movie에게 각각 한번씩 한다.

1차 캐시를 비운후에 em.find()를 하게 되면 JPA가 알아서 Join query를 날려서 가져온다. 

@DiscriminatorColumn 을 통해서 DType 현재 어떤 자식 클래스의 데이터를 담고있는 지 알려준다.

장점 = 테이블의 정규화, 외래키 참조 무결성 제약 조건활용 가능, 저장 공간의 효율화

단점 = 조회시 조인을 많이사용, 성능이저하 , 조회 쿼리가 복잡합, 데이터 저장시 Insert 2번호출

 

SINGLE_TABLE = 단일 테이블 전략

단일테이블의 경우 @DiscriminatorColumn 이 없어도 DTYPE 이 자동으로 생성된다. 이게 없으면 내가 Ablum인지 Movie인지 뭔지 구분을 할 수 가 없기 때문이다.

쿼리를 join 해서 날리지 않고 그냥 심플한 select 쿼리를 날림으로 성능이 좀 더 좋다.

하지만 단일테이블에 모든것을 저장하므로 테이블이커지면 상황에따라 느려질수도있다.

또한 자식 엔티티들이 모두 null을 허용해야한다. 관련이 없는것들은 null로 들어가기 때문이다.

 

TABLE_PER_CLASS = 구현 클래스마다 테이블 전략 // 쓰면안됨

item class 를 abstract으로 만들고 시작한다. item 테이블은 따로 생성되지 않는다.

@DiscriminatorColumn 이 동작하지않음 -> 테이블이 이미 다다르니 의미가 없다.

아이템 테이블을 없애버리고 거기있는 속성들을 다 밑으로 내리는 것이다. 

하지만 Item으로 조회를 할때에는 union으로 모든 테이블을 가져오게 된다. 각 테이블(movie, book ,album) 명으로 조회할때는 문제 없긴하다.

 

Mapped Superclass - 매핑 정보 상속

서로 전혀다른 table이지만 속성만 상속 받고 싶을 때 쓰이는 것이다.

상속관계 매핑이 아니고 엔티티도 아니고 테이블과 매핑이 되는것도 아니다.

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

조회 , 검 색 불가 em.find(BaseEntity) 불가 , 직접 사용할일이 없으므로 추상 클래스 권장

 

package hellojpa;

import javax.persistence.MappedSuperclass;
import java.time.LocalDateTime;

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

    public String getCreatedBy()
    {
        return createdBy;
    }

    public void setCreatedBy(String createdBy)
    {
        this.createdBy = createdBy;
    }

    public LocalDateTime getCreatedDate()
    {
        return createdDate;
    }

    public void setCreatedDate(LocalDateTime createdDate)
    {
        this.createdDate = createdDate;
    }

    public String getLastModifiedBy()
    {
        return lastModifiedBy;
    }

    public void setLastModifiedBy(String lastModifiedBy)
    {
        this.lastModifiedBy = lastModifiedBy;
    }

    public LocalDateTime getLastModifiedDate()
    {
        return lastModifiedDate;
    }

    public void setLastModifiedDate(LocalDateTime lastModifiedDate)
    {
        this.lastModifiedDate = lastModifiedDate;
    }
}
@Entity
public class Member extends BaseEntity
@Entity
public class Team extends BaseEntity
        try
        {
            Member member = new Member();
            member.setUsername("user1");
            member.setCreatedBy("kim");
            member.setCreatedDate(LocalDateTime.now());

            em.persist(member);
            em.flush();
            em.clear();
            

            tx.commit();
        }
Hibernate: 
    
    create table Member (
       MEMBER_ID bigint not null,
        createdBy varchar(255),
        createdDate timestamp,
        lastModifiedBy varchar(255),
        lastModifiedDate timestamp,
        USERNAME varchar(255),
        LOCKER_ID bigint,
        TEAM_ID bigint,
        primary key (MEMBER_ID)
    )

extend를 해준 Member 를 살펴보면 도메인에는 적혀있지 않던 createdBy ~ lastModifiedDate 까지 생겼다.

한마디로 같이 쓰고 싶은 속성을 중복없이 쓸 수 있게 해준다. 

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

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

참고: @Entity 클래스는 엔티티나 @MappedSuperclass 로 지정한 클래스만 상속가능

java - JPA: Implementing Model Hierarchy - @MappedSuperclass vs. @Inheritance - Stack Overflow

 

JPA: Implementing Model Hierarchy - @MappedSuperclass vs. @Inheritance

I am using Play Framework 1.2.4 with PostgreSQL and JPA. I would like to have a Model hierarchy and see that there are some alternatives to doing this. I have a base class (which is abstract) and ...

stackoverflow.com

 

 

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

요구 사항 추가

상품의 종류는 음반, 도서, 영화가 있고 이후 더 확장될 수 있다.

모든 데이터는 등록일과 수정일이 필수다.

기존의 것에 Album, Book ,Movie 가 생성되었음

테이블은 single table 로 설계가 되었음

@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn
public abstract class Item
public class Movie extends Item
public class Book extends Item
public class Album extends Item

자식 클래스를 구분하기위한 DTYPE 은 필수다. 이떄 전략을 바꾸고 싶다면 바로 annotation 만 JOINED 로 바꾸면된다.

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

자식 테이블도 간단하게 생성되었다. @TABLE_PER_CLASS 도 마찬가지이다.

728x90