나는 지금까지 PK값을 생성할 때 JPA의 @GeneratedValue=strategy = GenerationType.IDENTITY 설정을 통해 Auto Increment를 사용해왔다.
그러나 이번에 회사에서 PK 무결성 에러와 관련해서, 로그 분석을 하던 중에 Oracle DB의 테이블의 PK 값을 Sequence를 사용해 증가해주는 로그를 발견하게 되었다.
결론적으로는 Sequence의 기본 값을 수정하여 문제를 해결했긴 했지만 Sequence란 무엇인지에 대해서, 그리고 JPA에서의 Sequence와 Identity 전략에 대해서도 알아보고자 한다.
그렇다면 먼저 Sequence가 무엇인지에 대해서 먼저 알아보아야 한다.
Sequence란 데이터베이스에서 일련 번호(PK)를 자동으로 생성하기 위해 사용되는 객체를 의미한다.
조금 더 풀어서 이야기하면, 자동으로 순차적으로 증가하는 순번을 반환하는 객체이다.
Oracle에서 Sequence를 다루는 문법들을 이야기하면서, 정확히 어떻게 쓰이는지 살펴보자.
아래는 Oracle의 Sequence를 생성하는 코드이다.
CREATE SEQUENCE CUSTOMER_SEQ INCREMENT BY 1 MINVALUE 1 MAXVALUE 9999999999999999999999999999 NOCYCLE CACHE 20;
CREATE SEQUENCE CUSTOMER_SEQ ← CUSTOMER_SEQ 시퀀스를 생성한다.INCREMENT BY 1 ← 증감 숫자 1MINVALUE 1 MAXVALUE 9999999999999999999999999999 ← 최솟값과 최댓값 설정NOCYCLE ← 최대값에 도달하면 시퀀스 중지CACHE ← 매모리에 시퀀스 값 20을 미리 할당아래는 Oracle의 Sequence에서 다음 순번 값을 자동으로 반환받는 코드이다.
SELECT SCUSTOMER.nextval FROM dual;
자동으로 순차적으로 증가하는 순번을 반환 받을 수 있다는 것이다.JPA에서의 Sequence 전략에 대해서 알아보기 위해 PK에 값을 할당하는 두 방식에 대해서 알아보자.
이는 기본 키 생성을 DB에 위임하는 전략이다.
이는 AutoIncrement기능을 제공해서 기본 키 값을 자동으로 생성하는 DBMS인 MySQL, MariaDB 등에서 사용된다.
Indetity 전략은 먼저 Entity를 DB에 저장한 후에 식별자를 조회해서 Entity의 식별자로 할당하는 전략이다.
@Id
@GeneratedValue=strategy = GenerationType.IDENTITY)
private long id;
이는 JPA에서 쓰기 지연 방식이 동작되지 않는다.
쓰기 지연 방식이란 한 트랜잭션 안에서 이뤄지는 Update나 Save의 쿼리를 쓰기지연 저장소에 가지고 있다가 트랜잭션이 커밋되는 순간 한번에 DB에 날리는 것을 의미하는데, 이 때 Entity가 영속 상태이어야 한다.
그러나 영속 상태가 되려면 식별자가 반드시 필요한데, 이 전략은 DB에 저장해야만 식별자를 구할 수 있으므로 쓰기 지연 방식이 동작되지 않고, 개별 쿼리로 커밋된다.
Sequence 전략은 위에서 설명한 DB Sequence를 사용해서 기본 키를 할당하는 전략이며, 당연하지만 DB에서 Sequence를 생성한 후에 사용할 수 있다.
import javax.persistence.*;
@Entity
@Table(name = "your_table_name")
public class YourEntity {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "your_sequence_generator")
@SequenceGenerator(name = "your_sequence_generator", sequenceName = "your_sequence_name", allocationSize = 1)
private Long id;
}
이는 JPA에서 em.persist() 를 호출하기전에 DB Sequence를 먼저 조회하고, 그 후에 조회된 식별자를 Entity에 할당 한 후에 Entity를 영속 상태로 저장한다.
그리고 그 후에 Transaction을 Commit하여서 Flush가 발생할 때 해당 Entity를 DB에 저장한다.
그래서 위 Identity 전략과 다르게 쓰기 지연 저장소 방식이 동작할 수 있다.