이 글은 김영한 님의 저서 「자바 ORM 표준 JPA 프로그래밍」을 학습한 내용을 정리한 글입니다. 모든 출처는 해당 저서에 있습니다.
기본 키를 애플리케이션에서 직접 할당하는 방식
@Id
로 매핑@Id
적용 가능 자바 타입int
, float
등Integer
, Float
등String
java.util.Date
java.sql.Date
java.math.BigDecimal
java.math.BigInteger
em.persist()
로 엔티티를 저장하기 전 기본 키 직접 할당Board board = new Board();
board.setId("id1") //기본 키 직접 할당
em.persist(board);
💡 식별자 값 없이 저장시 예외가 발생한다.
대리 키 사용하여 생성하는 방식
@Id
에 @GeneratedValue
추가하여 원하는 키 생성 전략 선택
데이터베이스 벤더마다 기본 키 생성 지원 방식이 다르므로 종류가 다양함
💡
hibernate.id.new_generator_mappings
- 키 생성 전략 사용시 persistence.xml에 필수로 추가
- 기본값 : false(과거 버전과의 호환성 유지를 위함)
- 기존 하이버네이트 시스템 유지보수를 제외하고는 true로 설정할 것
MySQL
, PostgreSQL
, SQL Server
, DB2
에서 사용INSERT
)한 후 기본 키 값 조회 가능@Entity
public class Board {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY) //IDENTITY 전략 사용
private Long id;
...
}
private static void logic(EntityManager em) {
Board board = new Board();
em.persist(board); //엔티티 저장
System.out.println("board.id = " + board.getId()); //할당된 식별자 값 출력
}
board.id = 1 //저장 시점에 데이터베이스가 생성한 값을 JPA가 조회
💡
Statement.getGeneratedKeys()
- JDBC3에 추가된 메소드
- 데이터를 저장하면서 동시에 생성된 기본 키 값 조회 가능
- 데이터베이스와 한 번만 통신 → 최적화
💡 주의
em.persist()를 호출하는 즉시 INSERT SQL이 데이터베이스에 전달
→ 트랜잭션을 지원하는 쓰기 지연 동작 X
데이터베이스 시퀀스를 사용하여 기본키를 생성하는 전략
→ 사용하는 데이터베이스에 의존
💡 데이터베이스 시퀀스
유일한 값을 순서대로 생성하는 특별한 데이터베이스 오브젝트
시퀀스를 지원하는 ORACLE
, PostgreSQL
, DB2
, H2
에서 사용
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" //시퀀스 생성기 선택
) //id 식별자 값 할당 담당
private Long id;
...
}
private static void logic(EntityManager em) {
Board board = new Board();
em.persist(board);
System.out.println("board.id = " + board.getId());
}
em.persist()
호출시 데이터베이스 시퀀스 사용하여 식별자 조회board.id = 1
@SequnceGenerator
속성
속성 | 기능 | 기본값 |
---|---|---|
name | 식별자 생성기 이름 | 필수 |
sequenceName | 데이터베이스에 등록되어 있는 시퀀스 이름 | hibernate_sequence (하이버네이트 기준) |
initialValue | ◾ DDL 생성 시에만 사용 ◾ 시퀀스 DDL 생성 시 처음 시작하는 수 지정 | 1 |
allocationSize | 시퀀스 한 번 호출에 증가하는 수 (성능 최적화에 사용) | 50 |
catalog, schema | 데이터베이스 catalog, schema 이름 |
매핑할 DDL
@GeneratedValue
옆에 사용해도 됨
💡 SEQUENCE 전략과 최적화
- 문제점 : 데이터베이스와 2번 통신
- 식별자를 구하기 위해 데이터베이스 시퀀스 조회
ex)SELECT BOARD_SEQ.NEXTVAL FROM DUAL
- 조회한 시퀀스를 기본 키 값으로 사용해 데이터베이스에 저장
ex)INSERT INTO BOARD...
allocationSize
- 시퀀스 접근 횟수 감소 위해 사용
- 설정한 값만큼 한 번에 시퀀스 값을 증가시킨 후 그만큼 메모리에 시퀀스 값 할당
ex)allcoationSize = 50
→ 시퀀스 한 번에 50 증가 후 1~50까지 메모리에서 식별자 할당
→ 51에 시퀀스 값 100으로 증가 후 51~100까지 메모리에서 식별자 할당- 장점 : 시퀀스 값을 선점하므로 여러 JVM이 동시 동작해도 기본 키 값이 충돌하지 않음
- 단점 : 시퀀스 값이 한 번에 대량 증가
- 보완 : 대량 증가를 원치 않고
INSERT
성능 중요치 않을 경우
→allocationSize
의 값 1로 설정- 적용
- 과거 : 시퀀스 값을 하나씩 할당받아
allocationSize
만큼 사용
ex)allocationSize = 50
=> 반환된 시퀀스의 값이 '1' → 1~50까지 사용
=> 반환된 시퀀스의 값이 '2' → 51~100까지 사용- 현재 :
hibernate.id.new_generator_mapping
를 true로 설정
→ true로 설정하지 않을 경우 과거 방식으로 최적화
create table MY_SEQUENCES (
sequence_name varchar(255) not null , //시퀀스 이름으로 사용
next_val bigint, //시퀀스 값으로 사용
primary key ( sequence_name )
)
@Entity
@TableGenerator( //테이블 키 생성기 등록
name = "BOARD_SEQ_GENERATOR", //테이블 키 생성기 이름
sequenceName = "MY_SEQUENCES", //키 생성용 테이블로 매핑
pkColumnValue = "BOARD_SEQ", allocationSize = 1)
)
public class Board {
@Id
@GeneratedValue(strategy = GenerationType.TABLE, //테이블 전략 사용
generator = "BOARD_SEQ_GENERATOR" //테이블 키 생성기 지정
) //id 식별자 값 할당 담당
private Long id;
...
}
private static void logic(EntityManager em) {
Board board = new Board();
em.persist(board);
System.out.println("board.id = " + board.getId());
}
board.id = 1
next_val
컬럼 값 증가INSERT
하면서 초기화@TableGenerator
속성 | 기능 | 기본값 |
---|---|---|
name | 식별자 생성기 이름 | 필수 |
table | 키생성 테이블명 | hibernate_sequence (하이버네이트 기준) |
pkColumnName | 시퀀스 컬럼명 | sequence_name (하이버네이트 기준) |
ValueColumnName | 시퀀스 값 컬럼명 | next_val (하이버네이트 기준) |
pkColumnValue | 키로 사용할 값 이름 | 엔티티 이름 |
initialValue | ◾ 초기 값 지정 ◾ 마지막으로 생성된 값이 기준 | 0 |
allocationSize | 시퀀스 한 번 호출에 증가하는 수 (성능 최적화에 사용) | 50 |
catalog, schema | 데이터베이스 catalog, schema 이름 | |
uniqueConstraints (DDL) | 유니크 제약 조건 지정 |
💡 TABLE 전략과 최적화
- 문제점 : 데이터베이스와 3번 통신
- SELECT 쿼리를 사용하여 값을 조회
- 조회한 값을 이용하여 데이터베이스에 저장
- 다음 값으로 증가시키기 위해 UPDATE 쿼리 사용
- 해결 방법 :
allocationSize
사용하여 최적화
선택한 데이터베이스 방언에 따라 IDENTITY
, SEQUENCE
, TABLE
전략 중 하나를 자동으로 선택
@GeneratedValue.starategy
의 기본값 = AUTO
@Entity
public class Board {
@Id
@GeneratedValue(strategy = GenerationType.AUTO) // @GeneratedValue와 같음
private Long id;
...
}
데이터베이스 변경시 코드 수정 필요 x
→ 키 생성 전략이 확정되지 않은 개발 초기 단계나 프로토타입 개발 시 사용
선택되는 전략에 따라 대처가 달라짐
SEQUNCE
나 TABLE
전략 선택된 경우영속성 컨텍스트는 엔티티를 식별자 값으로 구분하므로, 엔티티를 영속 상태로 만들기 위해서는 식별자 값이 반드시 필요함
em.persist()
호출 직후
전략 | 설명 |
---|---|
직접 할당 | ◾ em.persist() 호출 전 애플리케이션에서 직접 식별자 값 할당◾ 식별자 값 존재 x → 예외 발생 |
SEQUENCE | 데이터베이스 시퀀스에서 식별자 값 획득 → 영속성 컨텍스트에 저장 |
TABLE | 데이터베이스 시퀀스 생성용 테이블에서 식별자 값 획득 → 영속성 컨텍스트에 저장 |
IDENTITY | ◾ 데이터베이스에 엔티티 저장하여 식별자 값 획득 → 영속성 컨텍스트에 저장 ◾ 테이블에 데이터 저장 후 식별자 값 획득 가능 |
데이터베이스 기본 키 제약 조건
기본 키 선택 전략
자연 키보다는 대리 키 사용 권장
대리 키를 기본 키로 사용하되, 자연 키의 후보가 되는 컬럼들은 필요에 따라 유니크 인덱스 설정하여 사용