본문 바로가기
JPA

[JPA] 기본키 생성 전략

by doodoom 2023. 7. 7.

0. 이 글을 쓰게 된 이유

jpa의 id 생성 전략을 다양하게 활용해보고자 이 글을 쓰게되었다.

1. GenerationType

GenerationType이란 기본 키 생성 전략을 정의하는 역할을 하는 enum이다. 사용법은 다음과 같다.

@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

GenerationType에 어떤 전략이 살펴보자.

2. GenerationType.IDENTITY

IDENTITY은 기본키 생성을 데이터베이스의 열의 식별 속성으로 위임한다. 즉, 데이터베이스 자체에서 기본키 생성을 관리한다.
대표적으로 MySql의 auto increment, PostgreSQL의 serial을 활용한다.

2.1 장점

다음 순서의 키를 알기위해 데이터베이스에 조회를 하지않는다. 그래서 쿼리를 줄일 수 있는 장점이 있다.

2.2 단점

만약 새로운 데이터를 가지고있는 객체(id를 가지고있지 않음)를 저장하기 위해 영속화를 하고 해당 객체의 id를 활용할 경우 지연쓰기가 적용되지않고 바로 insert 쿼리가 나간다. insert 쿼리를 통해 데이터베이스에서 id를 알 수 있기 때문이다. 즉, 영속성ㅇ 커텍스트의 지연쓰기 기능을 제대로 활용하지 못한다.
보통은 어플리케이션과 데이터베이스의 네트워크 연결 횟수가 최대한 적을 수록 성능 상 이점이 있다. 그래서 jpa는 쓰기 지연 저장소에 삽입, 수정 ,삭제와 관련된 쿼리를 모아두고 한번에 실행하는데, 이 경우에 이 기능을 제대로 활용하지 못한다는 의미이이다.

3. GenrationType.SEQUENCE

@Entity
@NoArgsConstructor
@Getter
@SequenceGenerator(name = "member_seq_generator", sequenceName = "member_sequence",
    initialValue = 10, allocationSize = 100)
public class Member {

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "member_seq_generator")
    private Long id;
}

SEQUENCE 전략은 데이터베이스의 SEQUENCE 인스턴스를 만들어서 활용하는 전략이다. 만약 사용하려는 데이터베이스 벤더가 sequence 같은 인스턴스를 제공하지 않으면 테이블을 만들어서 사용한다(GenrationType.TABLE과 일치, sequence과 비슷하게 활용한다).
이 전략은 데이터베이스의 sequence에 그 다음 값을 저장해두고 사용하는 방식이다. 데이터를 삽입할 때 항상 데이터베이스에서 id를 가져오고, allocationSize만큼 메모리에 올려둔 다음 활용한다. 작동 순서는 다음과 같다.

  1. 데이터베이스의 sequence에서 다음 id 값을 가져온다.
  2. allocationSize만큼 메모리에 올려놓는다.
  3. 데이터들을 저장한다. 이때 메모리에 있는 id 값들은 모두 사용 가능하다.
  4. 만약 메모리에 있는 id 값을 모두 사용할 경우 1, 2, 3 과정을 반복한다.
  5. 다음 id 값을 sequence에 저장한다.

대표적으로 Oracle의 sequence 인스턴스를 활용한다.

3.1 장점

  1. IDENTITY와는 다르게 먼저 데이터베이스의 sequence를 조회하기 때문에 지연 쓰기가 가능하여 성능 향상을 할 수 있다.
  2. batch update를 활용할 때, allocationSize를 잘 활용하여 메모리를 아낄 수 있다.

3.2 단점

  1. 단순히 하나만 삽입해도 db에 2번 다녀와야한다.
  2. allocationSize만큼 데이터를 삽입하지 않으면 그만큼 id 값이 비게된다.

4.GenrationType.TABLE

@Entity
@NoArgsConstructor
@Getter
@TableGenerator(
        name = "MEM_SEQ_GENERATOR",
        table = "MEM_SEQUENCES",
        pkColumnValue = "MEM_SEQ",
        allocationSize = 1
)
public class Member {

    @Id
    @GeneratedValue(strategy = GenerationType.TABLE, generator = "member_seq_generator")
    private Long id;
}

TABLE 전략은 SEQUENCE 역할을 하는 TABLE을 데이터베이스에 생성한다. 해당 테이블의 컬럼은 다음과 같다.

sequence_name : 여러 Entity에서 해당 테이블을 사용할 수 있도록 이름을 설정
next_val : 다음 id 값을 저장

SEQUENCE 전략과의 차이점은 sequence_name의 유무이다. TABLE 전략은 sequence_name을 설정함으로써 여러 Entity에서 한 테이블을 사용할 수 있도록 한다. 하지만 SEQUENCE는 next_val만 제공한다. 물론 SEQUENCE도 여러 Entity에서 사용할 수 있지만, 컬럼이 하나이므로 기본키를 하나의 필드로 관리한다.

4.1 장점

  1. 모든 데이터베이스 벤더에서 가능하다. 데이터베이스의 기본 기능인 테이블을 사용하기 때문이다.
  2. IDENTITY와는 다르게 먼저 데이터베이스의 TABLE을 조회하기 때문에 지연 쓰기가 가능하여 성능 향상을 할 수 있다.
  3. batch update를 활용할 때, allocationSize를 잘 활용하여 메모리를 아낄 수 있다.

4.2 단점

  1. 최적화 되지 않은 테이블을 사용하기 때문에 성능 상 큰 이슈가 있다.

성능 상 이슈가 크기 때문에 사용자가 사용하는 어플리케이션에서는 적절하지 않다고 생각한다.

5.GenrationType.AUTO

데이터베이스 벤더에 따라 자동으로 3가지 전략 중 하나를 선택한다.
ddl-auto가 true라면 hibernate에서 자동으로 데이터베이스의 벤더에 따른다. 데이터베이스 자체에서는 추천하는 기능이 없다.
대표적으로 MySql은 Sequence를 활용한다(hibernate 버젼에따라 auto increment를 활용하는 경우도 있다).
예측과 다르게 테이블이나 sequence 객체를 만들어지고 해당 엔티티 id 생성 전략이 명확하지 않으니 사용을 지양하는 편이 좋다고 생각한다.