Reference. 자바 ORM 표준 JPA 프로그래밍
책 목차 및 이전 글
@Entity
public class Member {
@Id
@Column (name = "ID")
private String id;
...
}
- JPA가 제공하는 데이터베이스 기본 키 생성 전략
- 직접 할당: 기본 키를 애플리케이션에서 직접 할당
- 자동 생성: 대리키 사용 방식
- IDENTITY: 기본키 생성을 데이터베이스에 위임
- SEQUENCE: 데이터베이스 시퀀스를 사용해서 기본 키를 할당
- TABLE: 키 생성 테이블을 사용
- 자동 생성 전략이 다양한 이유는 데이터베이스 벤더마다 지원 방식이 다름 → 오라클은 시퀀스를 제공, MySQL은 시퀀스 X
- TABLE 전략은 키 생성 테이블을 만들어 시퀀스처럼 사용하는 방식 → 모든 데이터베이스에서 사용이 가능
- 자동 생성 전략을 사용하려면 @GeneratedValue를 추가 후 생성 전략을 선택
@Entity
public class Board {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
...
}
<주의>
키 생성 전략을 사용하려면 persistence.xml
에 hibernate.id.new_generator_mappings
속성 추가
<property name="hibernate.id.new_generator_mappings" value="true" />
4.6.1 기본 키 직접 할당 전략
Board board = new Board();
board.setId("id1"); //기본 키 직접 할당
em.persist(board);
- @Id 적용 가능 자바 타입
자바 기본형
자바 래퍼(Wrapper) 형
String
java.util.Date
java.sql.Date
java.math.BigDecimal
java.math.BigInteger
- 기본 키 직접할당 전략에서 식별자 값이 없으면 예외 발생
→ 하이버네이트 구현체를 사용할 경우
javax.persistence.PersistenceException
발생
4.6.2 IDENTITY 전략
- 기본키 생성을 데이터베이스에 위임하는 전략
- 주로 MySQL, PostgreSQL, SQL Server, DB2에서 사용
CREATE TABLE BOARD(
ID INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
DATA VARCHAR(255)
);
INSERT INTO BOARD(DATA) VALUES('A');
INSERT INTO BOARD(DATA) VALUES('B');
- IDENTITY 전략을 사용하려면
@GeneratedValue(strategy = GenerationType.IDENTITY)
로 지정
@Entity
public class Board {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
...
}
IDENTITY 사용 코드
Board board = new Board();
em.persist(board);
System.out.println("board.id = " + board.getId());
//출력: board.id = 1
<참고>
- IDENTITY 전략과 최적화
- 기본 키 값을 조회하기 위해선 INSERT 후 추가 SELECT가 필요 (2번통신)
- JDBC3에 추가 된
Statement.getGeneratedKeys()
를 통해 저장과 동시에 키 값 조회(1번통신)
<주의>
- 엔티티가 영속 상태가 되려면 식별자가 반드시 필요
- IDENTITY 전략은 데이터베이스를 저장해야 식별자를 구할 수 있음
→
em.persist()
를 호출하는 즉시 INSERT SQL이 데이터베이스에 전달
- 트랜잭션을 지원하는 쓰기 지연이 동작하지 않음
4.6.3 SEQUENCE 전략
- 유일한 값을 순서대로 생성하는 특별한 데이터베이스 오브젝트
- 오라클, PostgreSQL, DB2, H@ 데이터베이스에서 사용 가능
CREATE TABLE BOARD(
ID BIGINT NOT NULL PRIMARY KEY,
DATA VARCHAR(255)
);
--시퀀스 생성
CREATE SEQUENCE BOARD_SEQ START WITH 1 INCREMENT BY 1;
@Entity
@SequenceGenerator (
name = "BOARD_SEQ_GENERATOR",
sequenceName = "BOARD_SEQ",
initialValue = 1, allocationSize = 1
)
public class Board {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE,
generator = "BOARD_SEQ_GENERATOR")
private Long id;
}
@GeneratedValue.generator
를 통해 생성된 시퀀스를 할당 (BOARD_SEQ_GENERATOR
)
SEQUENCE 사용 코드
Board board = new Board();
em.persist(board);
System.out.println("board.id = " + board.getId());
//출력: board.id = 1
SEQUENCE - IDENTITY 실행 순서
- SEQUENCE 전략 (식별자 조회 → 저장)
-
em.persist()
를 호출
- 데이터베이스 시퀀스를 사용해서 식별자 조회
- 조회한 식별자를 엔티티에 할당 후 영속성 컨텍스트에 저장
-
- IDENTITY 전략 (저장 → 식별자 조회)
-
em.persist()
를 호출
- 엔티티를 데이터베이스에 저장
- 식별자를 조회해서 엔티티 식별자에 할당
-
@SequenceGenerator 속성
속성 | 기능 | 기본값 |
---|---|---|
name | 식별자 생성기 이름 (id 역할) | 필수 |
sequenceName | 데이터베이스에 등록되어 있는 시퀀스 이름 | hibernate_sequence |
initialValue | 시퀀스 DDL을 생성할때 처음 시작하는 수 | 1 |
allocationSize | 시퀀스 한 번 호출에 증가하는 수 | 50 |
catalog, schema | 데이터베이스 catalog, schema 이름 | |
제목 없음 |
매핑 시퀀스 생성 DDL
create sequence [sequenceName]
start with [initialValue] increment by [allocationSize]
<주의>
- allocationSize default값이 50
- 기본 값이 50인 이유는 최적화 때문, 시퀀스 증가값이 1이면 allocationsSize값을 1로 변경
<참고>
- SEQUENCE 전략과 최적화
- 데이터베이스 시퀀스를 통해 식별자 조회하는 추가 작업이 필요
- SELECT BOARD_SEQ.NEXTVAL FROM DUAL
- INSERT INTO BOARD...
- JPA는 시퀀스 접근 횟수를 줄이기 위해 한번에 시퀀스 값을 증가시킨 후 메모리에 할당
- 예) 50증가 후 메모리 1~50까지 식별자 할당, 51이 되면 100으로 증가 후 메모리 할당
- 이런 상황이 부담스럽고 INSERT 성능이 중요하지 않으면 allocationSize의 값을 1로 설정
- 데이터베이스 시퀀스를 통해 식별자 조회하는 추가 작업이 필요
- @SequenceGenerator는 @GeneratedValud 옆에 사용 가능
@Entity
public class Board {
@ID
@GeneratedValue(...)
@SequenceGenerator(...)
private Long id;
...
}
4.6.4 TABLE 전략
- 키 생성 전용 테이블을 하나 만들고 이름과 값으로 사용할 컬럼을 생성 (시퀀스를 흉내)
- 테이블을 사용하므로 모든 데이터베이스 적용 가능
create table MY_SEQUENCES (
sequence_name varchar(255) not null,
next_val bigint,
primary key ( sequence_name )
)
@Entity
@TableGenerator(
name = "BOARD_SEQ_GENERATOR",
table = "MY_SEQUENCES",
pkColumnValue = "BOARD_SEQ", allocationSize = 1)
)
public class Board {
@Id
@GeneratedValue(strategy = GenerationType.TABLE,
generator = "BOARD_SEQ_GENERATOR")
private Long id;
}
TABLE 전략 사용 코드
Board board = new Board();
em.persist(board);
System.out.println("board.id = " + board.getId());
//출력: board.id = 1
MY_SEQUENCES 결과 테이블
sequence_name | next_val |
---|---|
BOARD_SEQ | 2 |
MEMBER_SEQ | 10 |
PRODUCT_SEQ | 50 |
@TableGenerator 속성
속성 | 기능 | 기본값 |
---|---|---|
name | 식별자 생성기 이름 | 필수 |
table | 키생성 테이블명 | hibernate_sequences |
pkColumnName | 시퀀스 컬럼명 | sequence_name |
valueColumnName | 시퀀스 값 컬럼명 | next_val |
pkColumnValue | 키로 사용할 값 이름 | 엔티티 이름 |
initialValue | 초기 값, 마지막 생성된 값이 기준 | 0 |
allocationSize | 시퀀스 한 번 호출에 증가하는 수 | 50 |
catalog, schema | 데이터베이스 catalog, schema 이름 | |
uniqueConstraints | 유니크 제약 조건을 지정 |
매핑 시퀀스 생성 DDL, 테이블명 {table}
{pkColumnName} | {valueColumnName} |
---|---|
{pkColumnValue} | {initialValue} |
<참고>
- TABLE 전략과 최적화
- 값을 조회하면서 SELECT 쿼리를 사용 다음 값으로 증가 시키기 위해 UPDATE쿼리 사용
- SEQUENCE 전략과 비교해서 한번 더 통신하는 단점
- @TableGenerator.allocationSize를 사용하여 최적화 (SEQUENCE의 최적화와 동일)
4.6.5 AUTO 전략
- 데이터베이스 방언에 따라 IDENTITY, SEQUENCE, TABLE 전략 중 하나 자동 선택
→
오라클: SEQUENCE
,MySQL: IDENTITY
사용
- @GeneratedValue.strategy의 기본값은 Auto
@Entity
public class Board {
@Id
@GeneratedValue //default GenerationType.AUTO
//@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
}
4.6.6 기본 키 매핑 정리
em.persist()
를 호출한 직후 식별자 할당 전략별 발생하는 일- 직접 할당: 호출 전 직접 식별자 값을 할당, 없으면 예외 발생
- SEQUENCE: DB 시퀀스에서 식별자 값 획득 후 영속성 컨텍스트 저장
- TABLE: DB시퀀스 생성용 테이블 식별자 값 획득 후 영속성 컨텍스트 저장
- IDENTITY: DB에 엔티티를 저장 후 식별자 값을 획득하여 영속성 컨텍스트에 저장
<참고>
- 권장하는 식별자 선택 전략
- 데이터베이스 기본 키는 3가지 조건을 모두 만족
- null값은 허용하지 않음
- 유일 값
- 불변 값
- 테이블의 기본 키를 선택하는 전략 크게 2가지
- 자연 키(natural key)
- 비즈니스에 의미가 있는 키
- 예: 주민등록번호, 이메일, 전화번호
- 대리 키(surrogate key)
- 비즈니스와 관련없는 임의로 만들어진 키, 대체키 라고도 불림
- 예: 오라클 시퀀스, auto_increment, 키생성 테이블
- 자연 키(natural key)
- 데이터베이스 기본 키는 3가지 조건을 모두 만족
- 자연키 보다는 대리키를 권장
- 현실과 비즈니스 규칙은 쉽게 변할 수 있으므로 대리키를 권장
- 비즈니스 환경은 언젠가 변화
- 현재와 미래까지 충족하는 자연 키를 찾기가 어려움
- 대리 키를 기본 키로 사용하고 자연 키 후보(주민등록번호)를 유니크 인덱스 설정을 권장
- JPA는 모든 엔티티에 일괄된 방식으로 대리 키 사용을 권장
'개발서적 > 자바 ORM 표준 JPA' 카테고리의 다른 글
[자바 ORM 표준 JPA 프로그래밍] 5.1 단방향 연관관계 (0) | 2021.08.12 |
---|---|
[자바 ORM 표준 JPA 프로그래밍] 4.7 필드와 컬럼 매핑: 레퍼런스 (0) | 2021.08.03 |
[자바 ORM 표준 JPA 프로그래밍] 4.4 - 4.5 데이터베이스 스키마 자동 생성, DDL 생성 기능 (0) | 2021.08.03 |
[자바 ORM 표준 JPA 프로그래밍] 4.1 - 4.3 @Entity, @Table, 다양한 매핑 (0) | 2021.08.03 |
[자바 ORM 표준 JPA 프로그래밍] 3.6 준영속 (0) | 2021.07.28 |