04. 엔티티 매핑

들어가며#

JPA 에서 중요하게 봐야하는 것들#

  • 객체랑 엔티티를 어떻게 맵핑할지
  • 영속성 컨텍스트

엔티티 매핑 소개#

  • 객체와 테이블 매핑: @Entity, @Table
  • 필드와 컬럼 매핑: @Column
  • 기본 키 매핑: @Id
  • 연관관계 매핑: @ManyToOne, @JoinColumn
    • 다음 장에서 알아볼 예정

객체와 테이블 매핑#

@Entity#

  • 해당 어노테이션이 붙은 클래스는 JPA 가 관리
  • JPA를 사용해서 테이블과 매핑하려면 @Entity 붙임
  • 주의
    • 기본 생성자 필수 (파라미터 없는 public or protected 생성자)
      • 스펙상 그럼 (리플렉션등 에 필요..)
    • final, enum, interface, inner 클래스 사용 X
    • 저장할 필드에 final X

@Table#

  • 엔티티와 매핑할 테이블 지정
    • arguments
      • name: 매핑할 테이블 이름이 다를 때 지정
      • 나머진 뒤에서 설명

DB 스키마 자동 생성#

  • DDL을 앱 실행 시점에 자동 생성
    • 운영단계에서 사용하면 안됨..
    • 개발단계에서 로컬 PC에서 사용하길 권장
  • 테이블 중심 -> 객체 중심 을 위해서
  • DB dialect 활용해서 DB에 맞는 적절한 DDL을 생성
  • 이렇게 생성된 DDL은 개발 장비에서만 사용
  • 생성된 DDL은 적절히 다음은 후에 사용
  • @Column(unique = True, length = 10) 과 같이 필드 단위 DDL 도 가능

속성#

  • persistence.xml에 <property name="hibernate.hbm2ddl.auto" value="create" /> 를 설정
  • create
    • 시작시 drop 테이블하고 create 로 다시 만듦
  • create-drop
    • create와 같고 종료 시 테이블 삭제
  • update
    • 테이블 삭제안하고 컬럼 추가를 해줌
    • 삭제는 안함
  • validate
    • 엔티티와 테이블이 정상 매핑되었는지 확인용
  • none
    • ddl 사용 안함
hivernate.hbm2ddl.auto 를 create로 설정했을 때
Hibernate:
drop table USER if exists
Hibernate:
create table USER (
id bigint not null,
name varchar(255),
primary key (id)
)

DDL 옵션 사용 시 주의사항#

  • 운영장비에선 절대 create, create-drop, update 사용 XXXXXXXXX
  • 개발 초기 create, update
  • 테스트 서버 (update), validate (공용 개발 서버)
  • 스테이징과 운영 서버는 validate, none

필드와 컬럼 매핑#

필드 종류#

@Column#

  • name: 테이블 컬럼 이름
  • insertable, updatable: 등록 변경 가능 여부 (기본 TRUE)
  • nullable(DDL): false면 NOT NULL 제약 조건 걸림 (기본 TRUE)
  • unique(DDL): 이름이 랜덤으로 나옴, 그래서 잘 안씀
    • @Table annotation으로 uniqueConstraints 옵션을 주로사함
  • columnDefinition: 컬럼 정보를 직접 줄 수 있음
  • length(DDL): 문자열 제한
  • precision, scale(DDL): BigInteger에 주로 사용

@Enumerated#

  • value
    • ORDINAL (default): enum 순서를 DB에 저장 (왜 안되는가?)
      • 데이터 마이그레이션이 필요하게 되버림 (쓰지않는 것을 권장)
    • STRING : enum 이름을 DB에 저장
      • DB 성능이 걱정될 수 있지만 요즘 성능이 좋아 걱정안해도 됨

@Temporal#

  • LocalDateTime, LocalDate 를 최신 하이버네이트 지원하기 때문에 @Temporal을 안써도 됨

@Lob#

  • clob: 필드 타입이 String일 때
  • blob: 필드 타입이 Byte일 때

@Transient#

  • 매핑안하고 앱에서 엔티티 객체에 메모리에서만 사용할 때

기본 키 매핑#

@Id#

  • 직접 할당

@GeneratedValue#

AUTO#

  • 기본값, DB 마다 상이

IDENTITY 전략#

  • 기본키 생성을 DB에 위임
  • 난 모르겠고 DB야 알아서 해줘~
  • 대게 auto_increment

SEQUENCE 전략#

  • ORACLE 에서 많이 사용
  • id 타입으로 Long 을 주로 씀
  • 시퀀스 이름 지정 가능

TABLE 전략#

  • 키 생성 전용 테이블을 하나 만들어서 DB 시퀀스를 흉내내는 전략
  • 모든 DB에 적용가능
  • 성능이..
  • 그래서 운영에서 쓰기 어려운 경우가 있다고 함

권장하는 식별자 전략#

  • 기본 키 제약 조건
    • null 아님
    • 유일
    • 변하면 안됨
  • 미래까지 이런 자연키를 찾기 어려움
    • 대리키(대체키)를 사용하자 (Auto generated value)
  • 예로 주민등록번호는 기본 키로 적절하지 않음
  • 권장 방법
    • Long + 대체키 (Sequence, uuid) + 키 생성전략 사용
    • auto_increment or sequence or uuid

Advanced#

IDENTITY 의 애매함#

  • DB에 값이 생성되야 ID를 확인할 수 있음
  • 영속성 컨텍스트는 ID를 몰라 1차 캐시에 저장이 어려움
  • 그래서 바로 persist 시점에 insert 쿼리가 날아가고 ID를 확인할 수 있음

SEQUENCE 전략의 특징#

  • 위와 마찬가지로 DB를 거쳐야 ID를 알아낼 수 있음
  • insert하지 않고 call next value for MEMBER_SEQ 쿼리가 실력됨
  • 마찬가지로 persist 시점에 날아가게 됨
  • 성능이 좋자 않은데 라는 고민을 알 수 있음
    • allocationSize 가 기본이 50임... 왜?
    • 저장할때마다 call next 쿼리르 날려버리면 통신이 이루어져야 하므로 코스트가 듦
    • DB 에 미리 allocationSize 만큼 앱에서 사용 하겠다고 하는 것임
    • 여러 앱에 동시성 이슈 없이 사용 가능!?!?

Table 전략에서도#

  • @TableGenerator 에도 allocationSize 가 있음

실전 예제 - 1. 요구사항 분석과 기본 매핑#

실습 코드 jpashop 링크

요구사항#

  • 회원은 상품을 주문할 수 있음
  • 주문 시 여러 종류의 상품을 선택할 수 있음

도멘 모델 분석#

  • 회원 : 주문 = 1 : n
  • 주문 : 주문상품 = 1 : n
  • 주문상품: 상품(물품) = n : 1

Table#

  • Member
    • id
    • name
    • city
    • street
    • zipcode
  • Order
    • id
    • memberId (FK)
    • orderDate
    • status
  • OrderItem
    • id
    • orderId (FK)
    • itemId (FK)
    • orderPrice
    • count
  • Item
    • id
    • name
    • price
    • stockQuantity

데이터 중심 설계의 문제점#

  • 현재 방식은 객체 설계를 테이블 설계에 맞춘 방식
  • 테이블의 외래키를 객체에 그대로 가져옴
  • 객체 그래프 탐색이 불가능
  • 참조가 없음

느낀점#

  • @Table @Column 에 제약사항을 클래스에 명시하여 DB를 직접 보지 않더라도 코드에서 바로 볼 수 있도록 구성하는 점이 좋음
  • spring boot 에서 기본적으로 Entity 필드들을 order_date (스네이크 케이스) 로 변환하여 매핑하는게 기본 값임. 그리고 바꿀 수 있음
  • @EnumeratedEnumType.ORDINAL 은 지양 -> 수정시 오류의 원인이 될 수 있음
Last updated on