빡코

[JPA][개념] 상속관계 본문

Java/JPA

[JPA][개념] 상속관계

chris.djang 2023. 2. 22. 10:54
상속관계매핑
관계형 데이터베이스는 상속관계X
슈퍼타입 서브타입 관계라는 모델링기법 이객체 상속과 유사
상속 관계 매핑: 객체의 상속과 구조와  DB슈퍼타입 서브타입 관계를 매핑
 
 
슈퍼타입 서브타입 논리모델을 실제 물리 모델로 구현하는 방법 
•각각 테이블로 변환 -> 조인전략 
•통합 테이블로 변환 -> 단일테이블전략 
•서브타입 테이블로 변환 -> 구현 클래스마다 테이블 전략
 
 
주요 어노테이션
@Inheritance(strategy=InheritanceType.XXX)
  •JOINED: 조인전략
  •SINGLE_TABLE: 단일 테이블 전략
  •TABLE_PER_CLASS: 구현 클래스 마다 테이블 전략
@DiscriminatorColumn(name=“DTYPE”)
@DiscriminatorValue(“XXX”)
 
 
조인 전략 
 
 
 
조인 전략
 
장점
테이블 정규화
외래참조 무결성 제약조건 활용가능
저장 공간 효율화
단점
조회시 조인을 많이 사용, 성능저하
조회 쿼리가 복잡함
데이터 저장시 INSERT SQL 2호출
 
@Entity
@Inheritance(strategy = InheritanceType.JOINED)
@DiscriminatorColumn //엔티티명이 들어가게 된다.
public class Item {

    @Id @GeneratedValue
    private Long id ;

    private String name;
    private int 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;
    }
}​
 
 
  Movie movie = new Movie();
            movie.setDirector("aaa");
            movie.setActor("bbb");
            movie.setName("바람과 함께 사라지다");
            movie.setPrice(1000);

            em.persist(movie);

            em.flush();//db저장
            em.clear();//1차캐시 날리기 

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

          
//로그 
Hibernate: 
    /* insert hellojpa.Movie
        */ insert 
        into
            Item
            (name, price, id) 
        values
            (?, ?, ?)
Hibernate: 
    /* insert hellojpa.Movie
        */ insert 
        into
            Movie
            (actor, director, id) 
        values
            (?, ?, ?)
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@3a8d467e
            (?, ?, ?)
findMovie = hellojpa.Movie@3a8d467e
 
 
 
 
 
*Item 의 Id와  Movie의 Id는 동일하다. 부모 Item의 Id는 PK, 자식 테이블 Movie의 Id는 PK이면서 동시에 FK이다.  
 
 
 
 
@DiscriminatorColumn부모 테이블에서 적용 
@DiscriminatorValue : 자식테이블 명 
@Entity
@Inheritance(strategy = InheritanceType.JOINED)
@DiscriminatorColumn //엔티티명이 들어가게 된다.
public class Item { 
}

@Entity
@DiscriminatorValue("M")
public class Movie extends Item {
}
   
   
   create table Item (
       DTYPE varchar(31) not null,
        id bigint not null,
        name varchar(255),
        price integer not null,
        primary key (id)
    )
 
 
 
 
 
단일 테이블 전략


단일 테이블 전략
•장점
•조인이 필요 없으므로 일반적으로 조회성능이 빠름
•조회쿼리가 단순함
•단점 
•자식엔티티가 매핑한 칼럼은 모두 null 허용 
•단일테이블에 모든 것을 저장하므로 테이블이 커질 수 있다. 
상황에 따라서 조회성능이 오히려 느려질 수 있다

 

*한 테이블에 다 올리고 DTYPE 컬럼으로 구분한다.

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

 

Hibernate: 
    /* insert hellojpa.Movie
        */ insert 
        into
            Item
            (name, price, actor, director, DTYPE, id) 
        values
            (?, ?, ?, ?, 'M', ?)
Hibernate: 
    select
        movie0_.id as id2_0_0_,
        movie0_.name as name3_0_0_,
        movie0_.price as price4_0_0_,
        movie0_.actor as actor8_0_0_,
        movie0_.director as director9_0_0_ 
    from
        Item movie0_ 
    where
        movie0_.id=? 
        and movie0_.DTYPE='M'
findMovie = hellojpa.Movie@63d5874f

* insert 와 select 쿼리가 심플하게 나간다.

 

 

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

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

구현 클래스마다테이블전략 
•이 전략은 데이터베이스설계자와 ORM 전문가 둘 다 추천 X 
•장점 
•서브타입을 명확하게 구분해서 처리할 때효과적 
•not null 제약조건사용가능 
•단점 
•여러 자식테이블을 함께 조회할 때성능이 느림(UNION SQL 필요) 
•자식테이블을 통합해서 쿼리 하기 어려움

 

 

부모 클래스로 조회시 전체 테이브을 조회한다. 비추천 

Movie movie = new Movie();
movie.setDirector("aaa");
movie.setActor("bbb");
movie.setName("바람과 함께 사라지다");
movie.setPrice(1000);

em.persist(movie);

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

Item findItem  = em.find(Item.class, movie.getId());
System.out.println("findMovie = " + findItem);
 
  
//실행 로그   
  select
        item0_.id as id1_2_0_,
        item0_.name as name2_2_0_,
        item0_.price as price3_2_0_,
        item0_.artist as artist1_0_0_,
        item0_.author as author1_1_0_,
        item0_.isbn as isbn2_1_0_,
        item0_.actor as actor1_6_0_,
        item0_.director as director2_6_0_,
        item0_.clazz_ as clazz_0_ 
    from
        ( select
            id,
            name,
            price,
            artist,
            null as author,
            null as isbn,
            null as actor,
            null as director,
            1 as clazz_ 
        from
            Album 
        union
        all select
            id,
            name,
            price,
            null as artist,
            author,
            isbn,
            null as actor,
            null as director,
            2 as clazz_ 
        from
            Book 
        union
        all select
            id,
            name,
            price,
            null as artist,
            null as author,
            null as isbn,
            actor,
            director,
            3 as clazz_ 
        from
            Movie 
    ) item0_ 
where
    item0_.id=?
findMovie = hellojpa.Movie@141d3d43

 

 

@ MappedSuperClass

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

ex) Team 과 Member 클래스에 공통으로 들어가는 속성들을 BaseEntity 클래스로 별도 생성하여 각 클래스에서 상속하여 사용한다. 

@MappedSuperclass
public class BaseEntity {

    private String createBy;
    private LocalDateTime createDate;
    private String lastModifiedBy;
    private LocalDateTime lastModifiedDate;

    public String getCreateBy() {
        return createBy;
    }

    public void setCreateBy(String createBy) {
        this.createBy = createBy;
    }

    public LocalDateTime getCreateDate() {
        return createDate;
    }

    public void setCreateDate(LocalDateTime createDate) {
        this.createDate = createDate;
    }

    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;
    }
}

 

//실행로그 
//각 테이블에 해당 필드가 생성된 것을 확인할 수 있다. 
create table Member (
    MEMBER_ID bigint not null,
    createBy varchar(255),
    createDate timestamp,
    lastModifiedBy varchar(255),
    lastModifiedDate timestamp,
    USERNAME varchar(255),
    LOCKER_ID bigint,
    TEAM_ID bigint,
    primary key (MEMBER_ID)
)

create table Team (
    TEAM_ID bigint not null,
    createBy varchar(255),
    createDate timestamp,
    lastModifiedBy varchar(255),
    lastModifiedDate timestamp,
    name varchar(255),
    primary key (TEAM_ID)
)
상속관계매핑X
엔티티X, 테이블과매핑X
부모 클래스를 상속받는 자식 클래스에 매핑 정보만 제공
조회, 검색불가(em.find(BaseEntity) 불가)
직접 생성해서 사용할 일이 없으므로 추상 클래스 권장
 
•테이블과 관계없고, 단순히 엔티티가 공통으로 사용하는 매핑정보를 모으는 역할 
•주로 등록일, 수정일, 등록자, 수정자 같은 전체엔티티에서 공통으로 적용하는 정보를 모을 때사용 
•참고: @Entity 클래스는 엔티티나 @MappedSuperclass로지 정한 클래스만상 속가능
 
예제

 

 

 

 

 

@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn

public abstract class Item  extends  BaseEntity{

    @Id
    @GeneratedValue
    @Column(name = "ITEM_ID")
    private Long id;

    private String name;
    private int price;
    private int stockQuantity;

    @ManyToMany(mappedBy = "items")
    private List<Category> categories = new ArrayList<>();
    
}
@MappedSuperclass
public abstract class BaseEntity {

        private String createBy;
        private LocalDateTime createDate;
        private String lastModifiedBy;
        private LocalDateTime lastModifiedDate;

}

book, Movie, Album은 Item 클래스를 상속한다. 

'Java > JPA' 카테고리의 다른 글

[JPA][개념] 값 타입  (0) 2023.03.23
[JPA][개념] 프록시와 연관관계 정리  (0) 2023.03.23
[JPA][개념]다양한 연관관계 매핑  (0) 2023.02.17
[JPA][개념] 엔티티 매핑  (0) 2023.02.14
[JPA][개념] 영속성 관리  (0) 2023.02.10