본문 바로가기
Server/[JAVA] JPA - Hibernate

02. 알아두면 쓸떼있는 GeneratedValue 이야기 Part1

by YangsDev 2020. 6. 4.

오늘의 글의 시작

시작하며

우리는 Entity를 만들 때 우리는 PK (Primary Key)를 지정한다.

그중에서도 Primary Key를 선택할 때, 전략적으로 생각했을 때 순차 증가 값을 포기할 수가 없다.

과연 그럼 JPA에서는 이런 대체 키를 어떻게 생성하고, 어떤식으로 동작하는지에 대해 알아보도록 하자.

 

GeneratedValue란?

import javax.persistence.*;
import java.io.Serializable;
import java.time.LocalDateTime;

@Table(name = "coupon")
@Entity
@Getter
@Setter
public class Coupon implements Serializable {
    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "coupon_id", insertable = false, nullable = false)
    private Long couponId;

    @Column(name = "coupon", nullable = false, unique = true)
    private String coupon;

    @Column(name = "coupon_status", nullable = false)
    private CouponStatus status;

    @Column(name = "reg_timestamp", nullable = false)
    private LocalDateTime regTimestamp;

    @Column(name = "expired_timestamp", nullable = false)
    private LocalDateTime expiredTimestamp;
}

JPA는 이러한 기본키를 자동생성 하는 부분에 대해 '@Id'와 '@GeneratedValue' 어노테이션을 통해 제공을 한다.

여기엔 자동 생산 전략을 4가지로 제공 하게 된다.

모드 설명
Auto (Default) JPA가 알아서 하겠음
IDENTITY 기본키 생성을 DB에게 위임
SEQUENCE DB에서 `SEQUENCE`를 지원한다면, 사용 아니라면 Table Mode
TABLE DB에 키용 테이블이 생김 (hibernate_sequence)

GeneratedType.AUTO 가 동작하는 방식

AUTO (Default)로 사용 하면 위 순서도 대로 시스템이 동작하게 된다.

1. UUID라면, UUID로 처리 한다.

2. UUID가 아니고 Number Data Type이라면 `hibernate.id.new_generator_mapping`의 여부를 확인하게 된다.

2-1. FALSE라면, Native Genertaor를 사용하게 된다 (MySQL이라면 `auto_increment`)

3. hibernate.id.new_generator_mapping 이 TRUE 라면 `SequenceStyleGenetrator` 를 사용하게 된다.

3-1. Sequence Call을 하게 되고,  ID를 받는다

3-2. 만약 사용하려고 하는 DBMS가 Sequence API를 지원하지 않는다면 `GeneratedType.TABLE`로 동작하게 된다.

 

 

GeneratedType.IDENTITY 가 동작하는 방식

이 방식은 ID 생성에 대해 DB에 모든 것을 위임시키는 형태이다.

실제 입력 단계에서는 NULL로 설정되어, DB에 넘어가게 된다.

결론적으로ID를 JPA에서 채워서 넣지는 않는다.

하지만, 영속성 컨텍스트의 무결성을 유지 하기 위하여, 데이터를 입력하고 몇번 ID로 저장 하였는지 받아오게 된다.

 

GeneratedType.IDENTITY에서의 BULK INSERT?

최근에 JPA (Hibernate 구현체)를 BULK INSERT를 해야 하는 경우가 있었다.

성능이 너무 안나오길래, SQL 쿼리를 찍어보니 그냥 그대로 날아가고 있었다.

 

하이버네이트 공식 문서에서 그 이유를 알게 되었다.

 

Hibernate ORM 5.4.17.Final User Guide

Fetching, essentially, is the process of grabbing data from the database and making it available to the application. Tuning how an application does fetching is one of the biggest factors in determining how an application will perform. Fetching too much dat

docs.jboss.org

블로그에서 가장 많이 나오는 짤 중 하나인듯..

Hibernate disables insert batching at the JDBC level transparently if you use an identity identifier generator.

'IDENTITY'를 통해 데이터를 입력 할 경우 JDBC 레벨에서 batch insert를 비활성화 한다는 것 이다.

 

근데 왜 그랬는지에 대한 이야기는 공식 문서에 없길래, 다른 블로그에 있는 StackOverflow글을 가져왔다.

https://stackoverflow.com/questions/27697810/hibernate-disabled-insert-batching-when-using-an-identity-identifier-generator/27732138#27732138

 

Hibernate disabled insert batching when using an identity identifier generator

The Hibernate documentation says: Hibernate disables insert batching at the JDBC level transparently if you use an identity identifier generator. But all my entities have this configuration:...

stackoverflow.com

 

The only drawback is that we can’t know the newly assigned value prior to executing the INSERT statement. This restriction is hindering the “transactional write behind” flushing strategy adopted by Hibernate. For this reason, Hibernates disables the JDBC batch support for entities using the IDENTITY generator.

한줄요약 : hibernate의 'transactional write behind'에 문제가 생길 수 있어서 니네가 아무리 요청해도 내가 꺼버릴꺼야..

 

 

 

GeneratedType.IDENTITY 상태에서 Batch Insert는?

??????????????????????

SEQUENCE 쓰세요 여러분(?) 이 답이 라고 이야기 하면 이건 무책임 한 이야기 이고, 만약 뒤에 연결되는 DB가 MYSQL이라면 아래의 글을 꼭 보길 바란다.

 

HomoEfficio/dev-tips

개발하다 마주쳤던 작은 문제들과 해결 방법 정리. Contribute to HomoEfficio/dev-tips development by creating an account on GitHub.

github.com

한줄요약짤

MYSQL에서는 `SEQUENCE`API가 없다.. 그렇기 때문에 Hibernate는 Table형태로 진행하려고 하는데 그럼 위와 같은 재앙이 일어나기 시작한다. (말잇못..)

 

암튼 본론으로 돌아와서, 아까 그 Stackoverflow 글에 이야기를 다시해보자.

Therefore, using IDENTITY is still the best choice on MySQL, and if you need batching for insert, 
you can use jOOQ for that. Hibernate and jOOQ are a great combo.

결론은 Hibernate만 사용해서 처리하긴 어려우니 jooq를 같이 사용 해라. 둘은 최고의 조합이다. 라고 이야기 한다.

결국 나 역시 JOOQ로 구현 했는데, 짜증나는일이 꽤 있었다.

 

이 부분은 다른 시리즈에서 다뤄보기로 한다.

 

 

Part2에서는?

원래는 하나의 글에서 이야기를 다 풀어보려고 하였으나, 생각 보다 글이 길어지기도 하였다.

그리고 `GeneratedType.SEQUENCE`에 대해서는 꽤 긴 이야기가 있을듯 하여 나눠서 이야기 하려고 한다.

 

Part2에서는 'GeneratedType.SEQUENCE', 'GeneratedType.TABLE' 그리고 결론에 대해 이야기 해보도록 하겠다.

 

 

댓글0