GeneratedValue 전략

Posted by sJun on November 03, 2020 ·

테이블을 구성할 때 중요한 것 중 하나는 PK 입니다.

JPA는 이 PK를 객체와 매핑시켜주기 위해 애노테이션을 사용합니다.

@Id @GeneratedValue(stargey = GeneratedType.XXX)
private Long id;

@Id 객체를 테이블의 Id로 매핑시켜주는 애노테이션입니다.

여기서 중요한 것은 GeneratedValue입니다.

GeneratedValue는 테이블의 PK 값을 설정할 때 어떤 전략으로 사용할 것인지 묻는 역할을 합니다.

이 전략에는 4가지 방식이 있습니다.


  1. IDENTITY

  2. SEQUENCE

  3. TABLE

  4. AUTO


첫번째, IDENTITY는 키 생성 방식을 DB에 전부 맡겨버립니다.

IDENTITY의 중요한 점은 PK의 생성 시점이 DB가 데이터를 생성하는 시점과 같다는 점입니다.

em.persist(member);

이전 포스팅에서 persist는 member 객체를 영속성 컨텍스트로 관리한다는 뜻이라고 말했습니다.

하지만 영속성 컨텍스트는 객체의 Id값이 정해져있지 않으면 영속성 컨텍스트를 만들어내지를 못합니다.

그래서 JPA는 원래라면 persist후에 commit() 시점에서야 DB에 INSERT 쿼리문을 내보내는게 정상이지만 IDENTITY 방식일 때는 persist와 동시에 쿼리문을 보내 PK값을 받아옵니다.


두번째, SEQUENCE 입니다.

DB 시퀀스 오브젝트를 사용하는 방식입니다. SEQUENCE라 함은 유일한 값을 생성해주는 오라클 객체입니다.

이 SEQUENCE 값을 이용하면 기본키와 같이 순차적으로 증가하는 컬럼의 자동 생성이 가능합니다.

SEQUENCE 전략은 @SequenceGenerator 라는 것이 필요로합니다.

@Entity
@Getter @Setter
@SequenceGenerator(
        name = "MEMBER_SEQ_GENERATOR",
        sequenceName = "MEMBER_SEQ",
        initialValue = 1, 
        allocationSize = 50
)
public class Member {
    @Id @GeneratedValue(strategy = GenerationType.SEQUENCE,
    generator = "MEMBER_SEQ_GENERATOR")
    private Long id;
    ...

먼저 Member라는 클래스 위에 @SequenceGenerator의 정보를 적어주고 Id의 generator의 이름과 위 설정 정보의 이름과 매칭 시켜주면 됩니다.

initialValue는 최초 시작하는 시퀀스의 넘버입니다.

allocationSize는 한 번에 불러올 sequence 값을 위한 것인데, 기본 옵션은 50으로 설정되있습니다.

시퀀스 전략을 사용하면 디버깅 코드에 다음과 같이 나타납니다.

Hibernate: 
    call next value for MEMBER_SEQ
Hibernate: 
    /* insert com.demo.Member
        */ insert 
        into
            Member
            (username, id) 
        values
            (?, ?)

여기서 insert 문 이외에 call next ~~~ 문이 보이는데 JPA 입장에서 보면

“영속성 컨텍스트를 만들어야하니까 PK값 내놔”

정도가 되겠네요.

그런데 PK값이 필요할 때마다 call을 해서 그 때마다 영속성 컨텍스트를 만든다면 낭비일 수 밖에 없습니다.

50개의 PK값을 생성하려면 50번의 call을 해야하고, 숫자가 더 많아 질수록 성능이 하향 되겠죠.

그래서 allocationSize = 50 이라는 정보를 통해 미리 50개를 불러와버리는 겁니다.

이미 50개의 sequence를 불러왔으니 굳이 50번을 불러올 필요없이 추후에 51번 째 Id 값이 생길 때 다시 한번 더 불러오면 됩니다. 다음 sequence는 51번째부터 100번째까지 불러오는 것이죠.


세번째로, Table 전략은 Sequence 전략과 비슷하게 @TableGenerator라는 설정 정보가 필수적입니다.

@Entity
@Getter @Setter
@TableGenerator(
        name = "MEMBER_TABLE_GENERATOR",
        table = "MY_TABLE",
        allocationSize = 50
)
public class Member {
    @Id @GeneratedValue(strategy = GenerationType.TABLE,
    generator = "MEMBER_TABLE_GENERATOR")
    private Long id;
    ...

Sequence와 매우 비슷하게 보이지만 Table 전략은 사용자가 직접 테이블을 만들어준다는 점이 차이점입니다.

DB 종류에 상관없이 적용 가능하다는 장점이 있지만 아무래도 테이블을 직접 생성한다는 점에서 성능 하향의 여지가 있습니다.


마지막 AUTO 입니다.

AUTO는 데이터베이스의 종류에 따라 IDENTITY, TABLE, SEQUENCE 전략을 알아서 선택해줍니다.

개발자가 Oracle DB를 사용하면 SEQUENCE 전략을 자동으로 선택하고 MySQL이라면 IDENTITY 전략을 선택해주신다 보시면 됩니다.