-
해당 내용은
자바 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