06. 엔티티 매핑(2) - 기본 키 매핑
SPRING/JPA

06. 엔티티 매핑(2) - 기본 키 매핑

기본 키 매핑은 중요한 요소들이 몇몇 있어 따로 챕터를 배정해봤습니다.

기본 키 매핑

'@Id'를 통해 매핑을 합니다.

 

직접 할당 VS 자동생성

DB는 무척이나 방대하기 때문에 기본 키를 직접 할당하기보다는 자동으로 할당하는 것을 선호합니다.

 

직접 할당

기본키 필드에 매핑을 의미하는 '@Id'만을 입력하고 데이터 생성 시 기본키를 직접 입력하면 됩니다.

@Id
private Long id;

 

자동생성

기본키 필드에 '@GeneratedValue'를 입력하고, 속성 값에 따라 전략이 달라집니다.

// Case 1
@Id
@GeneratedValue(starategy = GenerationType.AUTO)
private Long id;

// Case 2
@Id
@GeneratedValue(starategy = GenerationType.IDENTITY)
private Long id;

// Case 3
@Id
@GeneratedValue(starategy = GenerationType.SEQUENCE)
private Long id;

1. GenerationType.AUTO

각 DB 방언에 맞게 3가지 전략(TABLE, IDENTITY, SEQUENCE)중 자동으로 적용하는 방식입니다.

예시

     - MySQL : GenerationType.IDENTITY

     - Oracle : GenerationType.SEQUENCE

 

2. GenerationType.IDENTITY

기본키 생성을 DB에 위임하는 방법입니다. (= DB가 기본키를 자동으로 생성)

그림1 key가 identity로 생성

주로 MySQL, PostgreSQL, SQL Server, DB2 등에서 사용됩니다.

 

순서는 다음과 같습니다.

 

그림2 Identity의 전체적인 진행

STEP 1. 자바 애플리케이션이 데이터 입력

Member(id, name) values(null, ?)

Identity 전략에서만 예외적으로 PERSIST 시점에서 즉시 INSERT 쿼리를 실행 해 엔티티를 DB에 저장합니다.

(영속성 컨텍스트는 기본키가 존재해야 하기 때문에, 기본키를 생성하는 것이 최우선)

기본키 생성을 위임했기 때문에, 기본키 id에는 null이 입력되는 것을 알 수 있습니다.

 

STEP 2. DB의 기본키 자동 생성

자바 애플리케이션은 알 수 없는 진행상황입니다.

DB에서 식별자를 조회 후 할당을 해 DB가 입력된 데이터에 기본키를 자동으로 생성합니다. (식별자 = flag)

그림3 DB의 식별자

 

STEP 3. Member DB 조회 시 발생하는 일

기본키 생성을 위임했기 때문에 추가적인 'SELECT' 쿼리가 날아가야 할 것 같지만, Persist 시점에서 이미 데이터를 INSERT + SELECT를 수행했기 때문에 기본키 값을 미리 알고 있습니다.

 


3. GenerationType.SEQUENCE

DB의 유일한 값을 순서대로 생성하는 Sequence 오브젝트를 통해 기본키 값을 할당합니다.

오라클, PostgreSQL, DB2, H2 데이터베이스에서 사용됩니다.

 

 

생성

테이블마다 Sequence를 각각 만들어 제어하고 싶다면, 각 Sequence명을 두어 관리를 해야 합니다.

@Entity
@SequenceGenerator(
    name = "MEMBER_SEQ_GENERATOR",	// JPA에서 관리할 Sequence명
    sequenceName = "MEMBER_SEQ",    	// 매핑할 DB의 Sequence명
    initialValue = 1 			// Sequence 시작값
    allocationSize = 1)
public class Member { 
    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE,
    				generator = "MEMBER_SEQ_GENERATOR")
    private Long id;	// 보통 Long 타입을 자주 씁니다.
}

 

그림4 SEQUENCE 생성

순서는 다음과 같습니다.

그림5 SEQUENCE 생성

 

STEP1. 식별자 조회

영속성 컨텍스트에 저장하기 위해서 먼저 기본키 값을 가져와야 합니다.

그래서 PERSIST호출 시점에서 먼저 SEQUENCE를 사용해서 식별자를 조회합니다.

 

STEP2. 엔티티에 할당 후 저장

조회한 식별자를 엔티티에 할당한 후 엔티티를 영속성 컨텍스트에 저장합니다.

트랜잭션을 커밋해서 플러시가 일어나면 엔티티를 DB에 저장한다.

Identity와 달리 commit 시점에 데이터를 저장함을 알 수 있습니다. (보통의 JPA 순서)

 

PLUS. 최적화 (allocationSize)

식별자를 호출할 때 계속해서 'call next value'을 반복합니다. 이렇게 되면, 계속 DB와 애플리케이션 사이를 왔다 갔다 해야 하기 때문에 최적화에 불리합니다. 이를 방지하기 위해서 식별자를 한 번에 대량으로 가져오는 방법을 만들게 되는데

이를 allocationSize로 조절이 가능합니다!

 

allocationSize는 한번에 가져올 식별자의 수를 뜻합니다. 80개의 데이터를 저장해봅시다. 

 

Sequence 식별자는 반드시 1부터 시작을 하기 때문에 반드시 2번 호출됩니다.

두 번째 Sequence값은 51입니다. 이는 1~50까지의 식별자를 한 번에 가져왔다는 뜻과 같으며,

PK값이 51이 되면 다음 식별자 묶음 51~100을 가져오게 됩니다. 그러면 Sequence값은 101이 되겠죠

그림 6 allocationSize로 인한 식별자 조회 최적화

 

4. GenerationType.TABLE

키 생성 전용 테이블을 하나 만들어 DB Sequence를 흉내 내는 전략입니다.

모든 데이터베이스에서 사용이 가능하나 테이블을 직접 사용하다 보니, 성능이 상대적으로 떨어집니다.

 

생성

@Entity
@TableGenerator(
    name = "MEMBER_TBL_GENERATOR",	// 식별자 생성기 이름
    table = "MY_SEQUENCE_TBL",    	// 매핑할 DB의 Sequence명
    pkColumnValue = "MEMBER_SEQ", 	// key column명
    allocationSize = 1)				// Sequence 한 번 호출에 증가하는 수
public class Member { 
    @Id
    @GeneratedValue(strategy = GenerationType.Table,
    				generator = "MEMBER_TBL_GENERATOR")
    private Long id;	// 보통 Long 타입을 자주 씁니다.
}

그림 TABLE 전략 SQL

 

그림 TABL 전략 DB테이블 변화

추가로 테이블의 최적화 방식 역시 Sequence 최적화 방식과 동일합니다.