티스토리 뷰

JPA

고급매핑

✨✨✨✨✨✨✨ 2022. 2. 12. 18:09
반응형

고급매핑 - 상속관계 매핑

상속관계매핑이란, 객체의 상속 구조관계형DB의 슈퍼타입과 서브타입의 관계를 매핑하는것이다.

 

상속관계의 매핑에서 슈퍼타입과 서브타입의 논리모델을 실제 물리 모델로 구현하는방법은 아래와 같다.

가. 각각 테이블로 변환하는 조인전략

나. 통합테이블로 변환하는 단일 테이블 전략

다. 서브타입 테이블로 변환하는 구현 클래스마다 테이블 전략

 

 

위 전략을 사용하면서 함께 쓰이는 어노테이션은 아래와 같다.

// 부모클래스에 작성한다.
@Inheritance(strategy=InheritanceType.XXX)
    // JOINED
    // SINGLE_TABLE
    // TABLE_PER_CLASS

// 부모클래스에 구분자 컬럼을 지정한다.
@DiscriminatorColumn(name="DTYPE")

// 자식클래스에 구분자 값을 지정해준다.
@DiscriminatorValue("A")
구분 JOINED SINGLE_TABLE TABLE_PER_CLASS
DTYPE 사용안해도되지만
사용권장
하나의 테이블로
생성되기에
안넣어도 자동으로 들어감
각 테이블이 생성되기에
필요없음

그럼 각 전략에 대해 자세히 알아보자


가. 조인전략(JOINED)

조인전략이란, 공통된 컬럼은 ITEM TABLE에 넣고
그 외 특성있는 정보들은 각 테이블인 ALBUM, MOVIE, BOOK에 넣는다.

@Inheritance(strategy=InheritanceType.JOINED)

  • 장점
    • 테이블 정규화가 가능하다
    • 외래 키 참조 무결성 제약조건 활용이 가능해진다
    • 저장공간을 효율화 한다.
  • 단점
    • 조회 시 조인을 많이 사용하면 성능이 저하된다.
    • 조회 쿼리가 복잡하다.
    • 데이터 저장 시 INSERT SQL을 2번 호출한다.

Item을 부모추상클래스, Album을 자식클래스로 지정한다.
Inheritance를 사용하여 조인전략으로 설정한다. 여기서 조인전략이기에 Album클래스의 id는 PK이면서 FK가 된다.
@DiscriminatorColumn 컬럼을 사용하면 DTYPE이라는 컬럼으로 값을 자식테이블의 Entity명이 들어간다
name속성을 통해 컬럼명 지정이 가능하다.

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

    private String name;
    private int price;
}

@Entity
@DiscriminatorValue("A") //DTYPE에 들어갈 컬럼 값을 설정 가능하다.
public class Album extends Item{
    private String artist;
}

Album Class는 Item Class를 상속받음으로써 album객체를 생성하여 데이터를 넣어준다.

flush후, clear() 한다면 1차캐시에 사라지게되고
em.find()를 통해 데이터를 가져오면 Item 테이블과 album테이블을 join을 통해 가지고온다.

Album album = new Album();
album.setName("라일락");
album.setPrice(10000);
album.setArtist("아이유");

em.persist(album);

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

Album albumFind = em.find(Album.class, album.getId());
System.out.println(albumFind);
create table Item (
  DTYPE varchar(31) not null,
  id bigint not null,
  name varchar(255),
  price integer not null,
  primary key (id)
)

create table Album (
  artist varchar(255),
  id bigint not null, // 자식입장에선 PK, FK∑
  primary key (id)
)

alter table Album 
  add constraint FKcve1ph6vw9ihye8rbk26h5jm9 
  foreign key (id) 
  references Item

// INSERT가 2번날라간다.
    /* insert hellojpa.Album
        */ insert 
        into
            Item
            (name, price, DTYPE, id) 
        values
            (?, ?, 'A', ?)
Hibernate: 
    /* insert hellojpa.Album
        */ insert 
        into
            Album
            (artist, id) 
        values
            (?, ?)

// SELECT는 JOIN해야한다,
select
    album0_.id as id1_2_0_,
    album0_1_.name as name2_2_0_,
    album0_1_.price as price3_2_0_,
    album0_.artist as artist1_0_0_ 
from
  Album album0_ inner join Item album0_1_ 
    on album0_.id=album0_1_.id 
where
  album0_.id=?

부모클래스에 구분자 컬럼을 지정하였기에 DTYPE컬럼이 생성되었다.
@DiscriminatorColumn(name="DTYPE")

자식클래스에 구분자 값을 지정해주어 AlbumClass를 INSERT 하면 A값이 들아갔다.
@DiscriminatorValue("A")


나. 단일 테이블 전략(SINGLE_TABLE)

단일 테이블 전략이란 한 테이블에 모든 컬럼을 다 때려넣고,
DTYPE 구분컬럼을 넣는다.

  • 장점
    • 조인이 필요 없으므로 일반적으로 조회 성능이 빠르다.
    • 조회 쿼리가 단순하다.
  • 단점
    • 자식 엔티티에 매핑한 컬럼은 모두 null을 허용학된다.
    • 단인 테이블에 모든것을 저정하므로 테이블이 커질 수 있다.
      상황에 따라서 데이터가 엄청많다면 조회 성능이 느려질 수 있다.
@Entity
@Inheritance(strategy= InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name="DTYPE")
public abstract class Item {
    @Id
    @GeneratedValue
    private Long id;

    private String name;
    private int price;
}

@Entity
@DiscriminatorValue("A") //DTYPE에 들어갈 컬럼 값을 설정 가능하다.
public class Album extends Item{
    private String artist;
}
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)
)

// INSERT는 한번만 들어간다
/* insert hellojpa.Album
    */ insert into
       Item (name, price, artist, DTYPE, id) 
       values (?, ?, ?, 'A', ?)

// SELECT가 간결하다.
select
  album0_.id as id2_0_0_,
    album0_.name as name3_0_0_,
    album0_.price as price4_0_0_,
    album0_.artist as artist5_0_0_ 
from Item album0_ 
  where album0_.id=? and album0_.DTYPE='A'

다. 구현 클래스마다 테이블 전략(TABLE_PER_CLASS)

  • 해당 전략은 DB설계자, ORM전문가 둘다 추천하지 않는다
    • 장점
      • 서브타입을 명확하게 구분하여 처리가능
    • 단점
      • 여러 자식테이블을 함께 조회할 때 성능이 느려진다(UNION SQL)
@Entity
@Inheritance(strategy= InheritanceType.TABLE_PER_CLASS)
public abstract class Item {
    @Id
    @GeneratedValue
    private Long id;

    private String name;
    private int price;
}

@Entity
public class Album extends Item{
    private String artist;
}
Album album = new Album();
album.setName("라일락");
album.setPrice(10000);
album.setArtist("아이유");

em.persist(album);

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

Item itemFind = em.find(Item.class, album.getId());
System.out.println(itemFind); // UNION

Item 테이블은 생성 안된다.


Mapped Superclass - 매핑정보 상속

@MappedSuperclass란 객체의 입장에서 자주 사용되는 공통 필드들을
하나의 클래스를 생성하여 공통필드들이 있는경우 사용된다.

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

  • 엔티티, 테이블과의 매핑을 하지 않는다.
  • 조회, 검색이 불가능하다. (em.find(BaseEntity))
  • 직접 생성해서 사용할 일이 없으므로 추상클래스를 권장한다.
  • 주로 등록일, 등록일자, 수정일, 수정일자와 같은 공통 정보를 적용할 경우 사용한다.
  • @Entity클래스는 @MappedSuperclass로 지정한 클래스만 상속 가능하다.
@MappedSuperclass
public abstract class BaseEntity {
    private String createBy;
    private LocalDateTime createdDate;
    private String updateBy;
    private LocalDateTime updateDate;
...

@Entity
@Inheritance(strategy= InheritanceType.JOINED)
@DiscriminatorColumn
public abstract class Item extends BaseEntity{ //BaseEntity 상속
    @Id
    @GeneratedValue
    private Long id;

    private String name;
    private int price;

@MappedSuperclass와 @Entity를 동시에 사용할 수 없다.
@MappedSuperclass는 자식의 공통컬럼을 위한 클래스이기 때문이다.

 

반응형
반응형
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/10   »
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31
글 보관함