Spring

[JPA] 기본 키(PK) 매핑 방법과 생성 전략

📝 작성 : 2020.08.18  ⏱ 수정 : 
728x90

기본 키(Primary key)?

주 키 또는 프라이머리 키라고 하며, 관계형 데이터베이스에서 레코드의 식별자로 가장 적합한 것으로 선택, 정의된 후보 키입니다.(출처: 위키백과)

 

기본 키의 (제약) 조건

기본 키는 데이터베이 테이블의 레코드를 식별하기 때문에 따라오는 제약 조건이 있습니다.

  • 기본 키의 값은 고유해야 하며(중복 X) 변하지 않아야 합니다.
  • 두 개 이상의 열을 기본 키로 설정한 경우 한 열에 중복된 값이 있을 수는 있지만 기본 키에 정의된 모든 열의 값의 조합은 고유해야 합니다.
  • 기본 키는 NULL값을 허용하지 않습니다.
  • 테이블은 하나의 기본 키만 가질 수 있습니다.

 

기본 키 선택 전략

  • 자연 키(natural key) : 주민등록번호, 이메일, 전화번호 등과 같이 비즈니스적인 의미가 있는 키입니다.
  • 대리 키(surrogate key, 대체키) : 채번과 같이 비즈니스와 관련 없는 임의로 만들어진 키입니다.

자연 키보다는 대리 키를 기본 키로 사용합니다.

 

기본 키(Primary Key) 매핑 방법

  1. 직접 할당 : user.setId(1L);과 같이 기본 키를 직접 할당합니다.
  2. 자동 생성 : 데이터베이스에서 자동으로 생성합니다. DB 벤더마다 지원하는 방식이 다르기 때문에 여러 가지 방법이 있습니다.
    전략 설명
    IDENTITY 기본 키 생성을 데이터베이스에 위임합니다.
    SEQUENCE 데이터베이스의 시퀀스를 사용합니다.
    TABLE 기본 키 전용 테이블을 만들고 시퀀스처럼 사용합니다. 테이블을 활용하기 때문에 모든 데이터베이스에서 사용 가능합니다.
    AUTO 데이터베이스 방언(Database disalect)에 맞게 자동으로 설정됩니다.(Default)

 

1. 직접 할당

@javax. persistence.Id만 사용합니다.

JPA 표준에는 기본 키 값 없이 저장하면 어떤 예외가 발생하는지에 대한 정의가 없습니다. hibernate사용 시 org.hibernate.id.IdentifierGenerationException예외가 발생합니다.

Jakarta Persistence에 따르면 직접 할당은 아래의 타입으로만 가능하다고 합니다.

  • primitive type
  • java.lang.String
  • enum
  • Java serializable types
    • primitive wapper type
    • java.util.Date
    • java.sql.Date
    • java.time.LocalDateTime
    • java.math.BigDecimal
    • java.math.BigInteger
    • 사용자가 직접 정의한 Serializable interface를 구현한 타입

하지만 Hibernate는 UUID와 같은 다른 유형의 타입도 가능하다고 합니다.(하이버네이트 공식문서)

 

2. 자동생성

@javax.persistence.Id@javax. persistence.GeneratedValue를 같이 사용합니다.

hibernate.id.new_generator_mapping = true 속성을 추가해야 한다.
(과거 버전과의 호환성 때문에 기본 값이 false)

 

1. IDENTITY

  • @GeneratedValue(strategy = GenerationType.IDENTITY)
  • 주로 MySQL, PostgreSQL, SQL Server, DB2 등에서 사용합니다.

 

Identity 전략의 최적화

DB에 값을 저장하고 나서야 PK값을 알 수 있기 때문에 기본 키를 할당하려면 DB를 먼저 조회해야 합니다.
Entity가 영속 상태가 되려면 식별자가 반드시 필요한데, IDENTITY 전략은 Entity를 DB에 저장해야 식별자를 구할 수 있으므로 모순이 생깁니다.
따라서 em.persist()를 호출하는 즉시 DB에 INSERT 됩니다. 그렇기 때문에 트랜잭션에서 지원하는 쓰기 지연이 동작하지 않습니다.
JDBC3에 추가된 Statement.getGeneratedKeys()를 통해 데이터를 저장함과 동시에 생성된 PK값도 얻을 수 있습니다. Hibernate는 이 메서드를 이용해 DB와 한 번만 통신합니다.

 

2. SEQUENCE

  • @GeneratedValue(strategy = GenerationType.SEQUENCEW)
  • Oracle, PostgreSQL, DB2, H2에서 사용합니다.
  • @SequenceGenerator를 통해 시퀀스의 정보를 입력합니다.
  • @SequenceGenerator는 클래스 레벨, 필드 레벨 모두 사용 가능합니다.
@SequenceGenerator(
        name = "BOARD_SEQ_GENERATOR",
        sequenceName = "BOARD_SEQ",
        initialValue = 1,
        allocationSize= 3)
@Entity
public class Board {
    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "BOARD_SEQ_GENERATOR")
    private Long id;
}
-- CREATE SEQUENCE [sequenceName] START WITH [initialValue] INCREMENT BY [allocationSize];
CREATE SEQUENCE BOARD_SEQ START WITH 1 INCREMENT BY 3;

Sequence 전략의 최적화

em.persist()를 호출하면 DB 시퀀스를 사용하여 식별자를 조회, 조회한 식별자를 할당한 후 영속성 콘텍스트에 저장합니다.
시퀀스를 조회하는 추가 작업이 필요하기 때문에 DB와 2번 통신한다. 이 시퀀스에 접근하는 횟수를 줄이기 위해 allocationSize속성을 사용합니다.
시퀀스를 조회할 때 한 번에 allocationSize만큼 증가시킨 후 메모리에서 식별자를 할당합니다. 이를, Pooled optimizer라고 합니다.
무작정 큰 숫자를 설정한 경우 애플리케이션이 중간에 종료되었을 때 시퀀스 공백이 생기기 때문에 적당한 값인 50을 사용합니다.

@SequenceGenerator

속성 설명 기본 값
name 식별자 생성기 이름(필수) 필수
sequenceName DB에 등록되어있는 시퀀스 이름 hibernate_sequence
initialValue 시퀀스 DDL을 생성할 때 처음 시작하는 수 지정 1
allocationSize 시퀀스 한 번 호출에 증가하는 수(성능 최적화에 사용) 50
catalog, schema DB catalog, schema 이름  

 

3. TABLE

  • 우선, Table 전략은 사용을 권장하지 않습니다.
  • @GeneratedValue(strategy = GenerationType.TALBE
  • 시퀀스 대신 테이블을 사용한다는 것만 제외하면 시퀀스 전략과 내부 동작 방식이 같습니다.
  • 시퀀스 테이블에 값이 없으면 JPA가 값을 INSERT 하면서 초기화하므로 값을 미리 넣어둘 필요가 없습니다.
  • @TableGenerator를 통해 Table 정보 입력합니다.
  • @TableGenerator는 클래스 레벨, 필드 레벨 모두 사용 가능합니다.
@TableGenerator(
    name = "BOARD_SEQ_GENERATOR",
    table = "MY_SEQ",
    pkColumnValue = "BOARD_SEQ",
    initialValue = 0, // Sequence 전략과는 다르게 기본 값은 0 입니다. 0으로 설정되어야 실제 DB에 저장될때 1부터 저장됩니다.
    allocationSize = 3)
@Entity
public class Board {
     @Id @GeneratedValue(strategy = GenerationType.TABLE, generator = "BOARD_SEQ_GENERATOR")
     private Long id;
}

Table 전략의 최적화

Sequence 전략과 동일하게 allocationSize를 통해 최적화합니다.

@TableGenerator

속성 설명 기본 값
name 식별자 생성기 이름(필수) 필수!
table 키 생성 테이블명 hibernate_sequences
pkColumnName 시퀀스 컬럼명 sequence_name
valueColumnName 시퀀스 값 컬럼명 next_val
pkColumnValue 키로 사용할 값 이름 Entity 이름
initailValue 초기 값, 마지막으로 생성된 값이 기준 0
allocationSzie 시퀀스 한 번 호출에 증가하는 수(성능 최적화에 사용) 50
catalog, schema DB catalog, schema 이름  
uniqueConstraints(DDL) 유티크 제약 조건 지정  

 

4. AUTO

  • @GeneratedValue(strategy = GenerationType.AUTO
  • @GeneratedValue.strategy의 기본 값이므로 @GeneratedValue만 써도 됩니다.
  • DB에 따라 IDENTITY, SEQUENCE, TABLE 전략 중 하나를 자동으로 선택합니다.
  • DB를 변경해도 코드를 수정할 필요가 없습니다.
  • SEQUENCE나 TABLE 전략이 선택되면 시퀀스나 키 생성용 테이블을 미리 만들어두어야 하는데 스키마 자동 생성 기능을 사용한다면 하이버네이트가 기본값을 사용해서 자동으로 만들어 줍니다.
반응형