@GeneratedValue

konu·2023년 12월 19일
0

스프링

목록 보기
3/8

 

1. @GeneratedValue

Provides for the specification of generation strategies for the values of primary keys.

pk의 생성 전략을 설정할 수 있는 어노테이션이다.

 

public @interface GeneratedValue {

    /**
     * (Optional) The primary key generation strategy
     * that the persistence provider must use to
     * generate the annotated entity primary key.
     */
    GenerationType strategy() default AUTO;

    /**
     * (Optional) The name of the primary key generator
     * to use as specified in the {@link SequenceGenerator} 
     * or {@link TableGenerator} annotation.
     * <p> Defaults to the id generator supplied by persistence provider.
     */
    String generator() default "";
}

 

@GeneratedValue의 속성은 전략과 제너레이터 이름
두 가지뿐이다.

 

 


(전략 하면 이 분...)

 

2. pk 생성 전략

 

public enum GenerationType { 

    /**
     * Indicates that the persistence provider must assign 
     * primary keys for the entity using an underlying 
     * database table to ensure uniqueness.
     */
    TABLE, 

    /**
     * Indicates that the persistence provider must assign 
     * primary keys for the entity using a database sequence.
     */
    SEQUENCE, 

    /**
     * Indicates that the persistence provider must assign 
     * primary keys for the entity using a database identity column.
     */
    IDENTITY,

    /**
     * Indicates that the persistence provider must assign
     * primary keys for the entity by generating an RFC 4122
     * Universally Unique IDentifier.
     */
    UUID,

    /**
     * Indicates that the persistence provider should pick an 
     * appropriate strategy for the particular database. The 
     * <code>AUTO</code> generation strategy may expect a database 
     * resource to exist, or it may attempt to create one. A vendor 
     * may provide documentation on how to create such resources 
     * in the event that it does not support schema generation 
     * or cannot create the schema resource at runtime.
     */
    AUTO
}

 

총 다섯 가지의 전략이 있다.
순서대로 살펴 보자.

 

1) TABLE

말 그대로 다음 pk 값을 들고 있는 테이블을 따로 두는 전략이다.
언뜻 보면 시퀀스 방식과 거의 동일하다.

 

@Id @GeneratedValue(strategy = GenerationType.TABLE)
    @Column(name = "MEMBER_ID")
    private Long id;

위와 같이 TABLE 전략을 속성으로 걸어주면
자동으로 hibernate_sequences라고 하는 테이블을 생성해준다.
(ddl을 자동으로 생성한다는 설정 하에)

다만, 지금은 pk 생성 테이블과 테이블에 들어갈 회원 pk에 대한 정보를
설정하지 않았기 때문에 전부 디폴트 값으로 설정되어 있다.

 

@TableGenerator(
        name = "member_seq",
        table = "jpa_seq",
        allocationSize = 50
)
@Entity
public class Member {

    @Id @GeneratedValue(strategy = GenerationType.TABLE, generator = "member_seq")
    @Column(name = "MEMBER_ID")
    private Long id;

 

그래서 위처럼 @TableGenerator를 통해
pk 생성 테이블에 대한 정보를 추가해야 한다.

'name' 속성은 @GeneratedValue의 'generator' 속성과 매칭하기 위해 필요하다.
'table' 속성은 DB에 정의될 pk 생성 테이블의 이름을 정한다.
'allocationSize' 속성은 밑에서 SEQUENCE 전략에서 설명하겠다.

 

참고!

TABLE 전략은 SEQUENCE 전략에 비해
pk 순서 생성에 있어서 성능적인 손해가 발생하기 때문에 추천되지 않는다.

 

 

2) SEQUENCE

@SequenceGenerator(
        name = "member",
        sequenceName = "member_sequence",
        allocationSize = 50
)
@Entity
public class Member {

    @Id @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "member")
    @Column(name = "MEMBER_ID")
    private Long id;

 

기본 생김새는 TABLE 전략과 거의 동일하다.
대신에 하단 이미지처럼 테이블이 아닌 시퀀스가 생성된다는 점이 다르다.

allocationSize란?

@TableGenerator와 @SequenceGenerator에서 보았던 요놈은
말 그래도 pk의 '할당 크기'를 뜻한다.

만약 우리가 30개의 Item 엔티티를 저장한다고 가정해보자.
그러면 위 ITEM_SEQ의 next_val을 30번 들락거려야 한다.

그런데 만약 DB에서 한번에 50개를 사용하도록 허락했다면?
그 때는 50개의 Item 엔티티를 넣을 때까지는 ITEM_SEQ를 다시 들를 일이 없다.

그러면 DB 입장에서는 한 번 읽을 때 next_val은
1에서 2가 아닌 51로 넘어간다.

이로 인해 다른 쓰레드가 동시에 읽더라도
pk가 겹치는 동시성 이슈가 발생하지 않는다.

 

참고!

위에서 TABLE 전략에서 밝혔듯
DB 시퀀스는 값 생성에서 최적화에 이점이 있기 때문에
TABLE보다 추천되는 방식이다.

 

 

3) IDENTITY

@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "MEMBER_ID")
    private Long id;

 

IDENTITY 방식은 DB의 auto_increment에 의존하여
pk를 생성한다.

그래서 persist 호출 시점에는 pk 값을 알지 못한다.
pk 값을 모르면 영속성 컨텍스트 입장에서는 엔티티를 관리할 수 없다.
따라서 persist 호출 시점에 자동으로 flush 된다.

그래서 배치로 쿼리를 날릴 수가 없고,
persist 호출 시마다 쿼리가 날라간다.

참고!

auto_increment의 동작 방식은 DB마다 다르지만
JPA는 그러한 DB dialect에 구애받지 않고
IDENTITY를 설정할 수 있게 해준다.

그런데 문제는 여러 DB를 한꺼번에 사용할 때에는
각 DB 환경의 방식이 다를 수 있기 때문에
IDENTITY 전략이 그닥 믿을만 하지 않은 것 같다.

 

 

4) UUID

말 그대로 UUID를 생성해서 넣어주는 방식이다.

5) AUTO

DB에 맞는 방식을 사용하도록 DB에 위임한다.

 

 


(그래서?)

 

3. 결론

가장 권장되는 방식은 SEQUENCE와 IDENTITY.

나는 보기 깔끔한 게 좋다면 IDENTITY,
나는 실용적, 효율적인 게 좋다면 SEQUENCE를 사용하세요!

(번외편으로,
모험이 좋다면 AUTO를 추천드립니다)

profile
日日是好日

0개의 댓글