04. 엔티티 매핑
들어가며#
JPA 에서 중요하게 봐야하는 것들#
- 객체랑 엔티티를 어떻게 맵핑할지
 - 영속성 컨텍스트
 
엔티티 매핑 소개#
- 객체와 테이블 매핑: 
@Entity,@Table - 필드와 컬럼 매핑: 
@Column - 기본 키 매핑: 
@Id - 연관관계 매핑: 
@ManyToOne,@JoinColumn- 다음 장에서 알아볼 예정
 
 
객체와 테이블 매핑#
@Entity#
- 해당 어노테이션이 붙은 클래스는 JPA 가 관리
 - JPA를 사용해서 테이블과 매핑하려면 @Entity 붙임
 - 주의
- 기본 생성자 필수 (파라미터 없는 public or protected 생성자)
- 스펙상 그럼 (리플렉션등 에 필요..)
 
 - final, enum, interface, inner 클래스 사용 X
 - 저장할 필드에 final X
 
 - 기본 생성자 필수 (파라미터 없는 public or protected 생성자)
 
@Table#
- 엔티티와 매핑할 테이블 지정
- arguments
- name: 매핑할 테이블 이름이 다를 때 지정
 - 나머진 뒤에서 설명
 
 
 - arguments
 
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로 설정했을 때
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 성능이 걱정될 수 있지만 요즘 성능이 좋아 걱정안해도 됨
 
 
 - ORDINAL (default): enum 순서를 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. 요구사항 분석과 기본 매핑#
요구사항#
- 회원은 상품을 주문할 수 있음
 - 주문 시 여러 종류의 상품을 선택할 수 있음
 
도멘 모델 분석#
- 회원 : 주문 = 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 (스네이크 케이스) 로 변환하여 매핑하는게 기본 값임. 그리고 바꿀 수 있음
 @Enumerated의EnumType.ORDINAL은 지양 -> 수정시 오류의 원인이 될 수 있음