Search
Duplicate

엔티티 매핑

객체와 테이블 매핑

@Entity
1.
@Entity가 붙은 클래스는 JPA가 관리, 엔티티라 한다.
2.
JPA를 사용해서 테이블과 매핑할 클래스는 @Entity 필수
3.
주의사항
a.
기본 생성자 필수(파라미터가 없는 public or protected)
b.
final 클래스, enum, interface inner 클래스 사용 x
c.
저장할 필드에 final 사용 X
4.
속성 정리
a.
name
JPA에서 사용할 엔티티 이름 지정.
기본값 클래스 이름을 그대로 사용(예: Member)
같은 클래스 이름이 없으면 가급적 기본값을 사용한다.
@Table
1.
엔티티와 매핑할 테이블 지정
2.
속성 정리
name : 매핑할 테이블 이름
catalog: 데이터베이스 catalog 매핑
schema: 데이터베이스 schema 매핑
uniqueConstraint(DDL): DDL 생성 시 유니크 제약 조건 생성

데이터베이스 스키마 자동 생성

DDL을 애플리케이션 실행 시점에 자동 생성
테이블 중심 → 객체 중심
데이터베이스 방언을 활용해서 데이터베이스에 맞는 적절한 DDL 생성
이렇게 생성된 DDL은 개발 장비에서만 사용
생성된 DDL은 운영서버에서는 사용하지 않거나, 적절히 다듬은 후 사용
속성 정리
1.
hibernate.hbm2ddl.auto
a.
create: 기존 테이블 삭제 후 다시 생성(DROP + CREATE)
b.
create-drop: create와 같으나 종료시점에 테이블 DROP
c.
update: 변경분만 반영(운영 DB에는 사용하면 안됨)
d.
validate: 엔티티와 테이블이 정상 매핑되었는지만 확인
e.
none: 사용하지 않음
주의 사항
1.
운영 장비에는 절대 create, create-drop, update 사용하면 안된다.
2.
개발초기 : create or update
3.
테스트 서버: update or validate
4.
스테이징과 운영서버: validate or none

필드와 컬럼 매핑

package hellojpa; import javax.persistence.*; import java.time.LocalDate; import java.util.Date; @Entity public class Member2 { @Id @GeneratedValue @Column(name = "MEMBER_ID") private Long id; private String username; @Enumerated(EnumType.STRING) private RoleType roleType; @Temporal(TemporalType.TIMESTAMP) private Date cereatedDate; private LocalDate lastModifiedDate; @Lob private String description; @Transient private int tmp_number; }
Java
복사

1. @Enumerated

자바 Enum 타입을 매핑할 때 사용
ORDINAL 타입을 사용하지 말자.
→ enum타입이 추가,변경,삭제 되어 순서가 달라질 경우 사이드 이펙트가 생긴다.
EnumType.ORDINAL: ENUM 순서를 데이터베이스에 저장
EnumType.STRING: ENUM 이름을 데이터베이스에 저장

2. @Temporal

날짜 타입(Date, Calendar)을 매핑할 때 사용
LocalDate(년월), LocalDateTime(년월일)을 사용할 때는 생략 가능(최신 하이버네이트 지원)

3. @Lob

데이터베이스 BLOB, CLOB 타입과 매핑
매핑하는 필드 타입이 문자면 CLOB, 나머지는 BLOB매핑
CLOB: String, char[], java.sql.CLOB
BLOB: byte[], java.sql.BLOB

4. @Transient

필드 매핑이 안되게 하는 어노테이션

5. @Column - 제일 자주 쓰이는 어노테이션

1.
속성
a.
name: 필드와 매핑할 테이블의 컬럼 이름
b.
insertable, updatable: 등록, 변경 가능여부
c.
nullable: null 허용 여부(false로 하면 not null 조건이 붙는다)
d.
unique: @Table의 uniqueConstrainst 와 같지만 한 컬럼에 간단히 유니크 제약조건을 걸 때 사용
→ 사용 빈도가 낮다(제약조건의 이름이 너무 난수값이라 알아보기 힘듬) → @Table(uniqueConstraint())에 사용하는 것을 권장.
e.
columnDefinition: 컬럼 정보를 직접 줄 수 있다.
f.
length: 문자 길이 제약조건. ( String 타입에서만 사용한다)
g.
precision, scale(DDL): BigDecimal 타입에서 사용한다.(아주 큰 숫자)

기본 키 매핑

1.
직접 할당: @Id만 사용
2.
자동 생성(@GeneratedValue)(strategy)
a.
IDENTITY
i.
데이터베이스에 위임, MYSQL
ii.
주로 MySQL, PostgreSQL, SQL Server, DB2에서 사용
iii.
JPA는 보통 트랜잭션 커밋 시점에 INSERT SQL 수행
iv.
AUTO_INCREMENT는 DB에 INSERT SQL을 실행 한 이후에 ID값을 알 수 있다.
영속성 관리 시점에서 1차캐시에 @Id값을 알 수 없다는 말이된다.
그렇기에 이 케이스에서는 persist() 수행 시 바로 insert 쿼리가 수행된다.
그렇기에 IDENTITY케이스에서는 지연쓰기가 제한된다 . (하지만 크게 성능 하락이 있거나 하지는 않다.)
b.
SEQUENCE
i.
데이터베이스 시퀀스 오브젝트 사용, ORACLE
ii.
@SequenceGenerator 필요
@Entity @SequenceGenerator( name = "MEMBER_SEQ_GENERATOR", sequenceName = "MEMBER_SEQ", initialValue = 1 ) public class Member2 { @Id @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "MEMBER_SEQ_GENERATOR") @Column(name = "MEMBER_ID") private Long id; ... }
Java
복사
iii.
영속화(persist())시 시퀀스에서 next value를 가져와서 해당 값을 @Id에 가지고 1차 캐싱을 해준다.
iv.
지연 쓰기가 가능하다.
3.
Table
a.
키 생성 용 테이블 사용, 모든 DB에서 사용
b.
키 생성 전용 테이블을 하나 만들어서 데이터베이스 시퀀스를 흉내내는 전략
c.
모든 데이터 베이스에서 사용할 수 있지만, 성능이 떨어진다.
d.
@TableGenerator 필요
@Entity @TableGenerator( name = "MEMBER_SEQ_GENERATOR", table = "MY_SEQUENCES", pkColumnName = "MEMBER_SEQ", allocationSize = 1) public class Member2 { @Id @GeneratedValue(strategy = GenerationType.TABLE, generator = "MEMBER_SEQ_GENERATOR") @Column(name = "MEMBER_ID") private Long id; ... }
Java
복사
e.
속성
name : 식별자 생성기 이름 (필수)
table : 키생성 테이블 명 (MEMBER_SEQ_GENERATOR)
pkColumnName: 시퀀스 컬럼명(MY_SEQUENCES)
pkColumnNa: 시퀀스 값 컬럼명
pkClumnValue: 키로 사용할 값 이름
initialValue: 초기 값, 마지막으로 생성된 값이 기준이다.
allocationSize: 시퀀스 한 번 호출에 증가하는 수(성능 최적화에 사용됨)
catalog, schema: 데이터베이스 catalog, schema 이름
uniqueConstraints(DDL): 유니크 제약 조건을 지정할 수 있다.
4.
AUTO: 방언에 따라 자동 지정, 기본값
5.
권장하는 식별자 전략
a.
기본 키 제약 조건: not null, unique, not update
b.
이 조건을 계속 만족하는 자연키는 찾기 힘들기 때문에 대리키(대체키)를 사용하자.
c.
예를 들면 주민등록번호도 기본 키로 적절하지 않다. -> 기본 키가 주민등록번호가 되면 연관매핑을 맺은 다른 테이블에서도 외래키로 주민번호를 사용하기에 여기저기에 개인정보가 퍼지게 된다.
d.
권장: Long형 + 대체키 + 키 생성전략 사용.(AUTO나 SequenceObject쓰자 + 회사내의 룰대로 사용하자)
고급: 기본키 생성 전략이 SEQUENCE인 경우
Q. 시퀀스를 얻느라 한 번, 엔티티를 저장한다고 한 번... 잦은 네트워크 통신이 발생하는데 더 줄일순 없을까?
A. 최적화 방안(allocationSize): 미리 인자값 갯수만큼 가져와서 사용한다.
@Entity @SequenceGenerator( name = "MEMBER_SEQ_GENERATOR", sequenceName = "MEMBER_SEQ", initialValue = 1, allocationSize = 50 ) public class Member2 { @Id @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "MEMBER_SEQ_GENERATOR") @Column(name = "MEMBER_ID") private Long id; ... }
Java
복사
미리 50개의 시퀀스를 가져와서 사용한다.
그렇다면 1-100까지 100개의 next value를 가져온 다음 커밋이 롤백된다면 시퀀스는 어떻게 되는가?
→ 구멍이 된다. 그리고 100까지 구멍으로 두는게 더 낫다. 왜냐하면 시퀀스를 100까지 받았다고 하면 100번이라 로깅도 남기고 비즈니스 로직을 수행하게 되는데, 문제가 생겨 롤백이 되었을때 시퀀스도 롤백되면 로그에는 다시 100이 남을 것이고 다음 비즈니스 로직에서 시퀀스를 받을 경우 다시 100번이 됩니다. 이럴 경우 어디가 정상이고 어디가 비정상 로깅인지 확인이 어려워집니다.

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

요구사항 분석

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

기능 목록

회원 기능
회원등록
회원조회
상품 기능
상품등록
상품수정
상품조회
주문 기능
상품 주문
주문내역조회
주문취소

도메인 모델 분석

회원과 주문의 관계; 회원은 여러 번 주문 할 수 있다. → 일대다 관계(1:N)
주문과 상품의 관계: 주문 시 여러 상품을 선택할 수 있다. 반대로 상품도 여러 번 주문될 수 있다. → 다대다 관계(N:M)
→ 주문 상품(OrderItem)이라는 모델을 만들어서 일대다(1:N), 다대일(N:1) 관계로 풀어낸다.

테이블 설계

엔티티 설계와 매핑

Source

Code

데이터 중심 설계의 문제점

1.
현재 방식은 객체 설계를 테이블 설계에 맞춘 방식
2.
테이블의 외래키를 객체에 그대로 가져옴
3.
객체 그래프 탐색이 불가능
4.
참조가 없으므로 UML도 잘못됨