JPA @GeneratedValue: SEQUENCE vs IDENTITY 비교

이지니·2025년 10월 12일

TIL

목록 보기
2/11

회원 엔티티에서 @GeneratedValue로 기본키를 자동 생성하기로 함.
그런데 회원가입 시 아직 insert되기 이전에 createdBy 등록을 위해 userId가 필요한 상황이 발생함.

👉 이 글에서 값 생성 시점을 기준으로 SEQUENCEIDENTITY를 비교합니다.

1. IDENTITY 전략

값 생성 시점

  • DB INSERT 시점에 생성
  • em.persist() 호출 시 즉시 INSERT 쿼리가 실행됨
  • DB의 AUTO_INCREMENT(MySQL), SERIAL(PostgreSQL) 등을 활용

동작 방식

@Entity
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long userId;
    
    private String username;
}
User user = new User();
user.setUsername("홍길동");

em.persist(user);  // 이 시점에 즉시 INSERT 쿼리 실행
System.out.println(user.getUserId());  // INSERT 전에는 null

장점

  • 설정이 간단함
  • DB의 네이티브 기능 활용

단점

  • 쓰기 지연(Write-Behind) 불가능: persist() 시점에 즉시 INSERT 실행
  • 배치 INSERT 불가능: 성능 최적화 제한
  • 트랜잭션 커밋 전에 ID를 알 수 없음: persist() 전에는 ID가 null

2. SEQUENCE 전략

값 생성 시점

  • em.persist() 호출 시점에 생성
  • Hibernate가 DB 시퀀스(nextval)를 호출하여 ID를 미리 할당
  • 실제 INSERT는 트랜잭션 커밋 시점에 실행

동작 방식

@Entity
@SequenceGenerator(
    name = "user_seq_generator",
    sequenceName = "user_seq",
    initialValue = 1,
    allocationSize = 50
)
public class User {
    @Id
    @GeneratedValue(
        strategy = GenerationType.SEQUENCE,
        generator = "user_seq_generator"
    )
    private Long userId;
    
    private String username;
}
User user = new User();
user.setUsername("홍길동");

em.persist(user);  // Sequence 조회 → ID 할당 (INSERT는 아직 실행 안 됨)
System.out.println(user.getUserId());  // ID 조회 가능 (DB INSERT 전)

// 트랜잭션 커밋 시점에 실제 INSERT 실행

장점

  • INSERT 전에 ID 확보 가능: 영속성 컨텍스트에 저장하기 전에 ID를 알 수 있음
  • 쓰기 지연 가능: 트랜잭션 커밋 시점까지 INSERT 쿼리 지연
  • 배치 INSERT 가능: 여러 엔티티를 한 번에 INSERT하여 성능 향상
  • allocationSize 최적화: 시퀀스를 미리 여러 개 할당받아 DB 호출 최소화

단점

  • Sequence를 지원하는 DB에서만 사용 가능 (Oracle, PostgreSQL, H2 등)
  • 추가적인 Sequence 객체 관리 필요

3. 실제 사용 사례: INSERT 전 ID가 필요한 경우

문제 상황

회원 생성 시 userId를 다른 로직에서 사용해야 하는데, 아직 DB에 INSERT되기 전인 경우:

@Service
@Transactional
public class UserService {
    
    public Long createUser(UserCreateRequest request) {
        User user = new User();
        user.setUsername(request.getUsername());
        
        em.persist(user);
        
        // ❌ IDENTITY 사용 시: 이 시점에 이미 INSERT 실행됨
        // ✅ SEQUENCE 사용 시: INSERT는 아직 실행 안 됨, ID만 할당됨
        
        // userId를 createdBy에 추가
        user.setCreatedBy(user.getUserId());  // ID 필요!
        sendWelcomeEmail(user.getUserId());   // ID 필요!
        
        return user.getUserId();
        // 트랜잭션 커밋 시점에 실제 INSERT 실행 (SEQUENCE)
    }
}

SEQUENCE를 선택한 이유

  1. INSERT 전에 userId를 확보할 수 있어서 다른 로직에서 활용 가능
  2. 연관 엔티티 생성 시 userId를 외래 키로 사용 가능
  3. 쓰기 지연을 통한 성능 최적화
  4. 배치 처리 가능

4. 비교 표

구분IDENTITYSEQUENCE
ID 생성 시점DB INSERT 시persist() 호출 시
INSERT 실행 시점persist() 호출 즉시트랜잭션 커밋 시
INSERT 전 ID 확보❌ 불가능✅ 가능
쓰기 지연❌ 불가능✅ 가능
배치 INSERT❌ 불가능✅ 가능
지원 DBMySQL, PostgreSQL 등Oracle, PostgreSQL, H2 등
성능 최적화제한적allocationSize 활용 가능

5. SEQUENCE 실무 팁

allocationSize 최적화 (SEQUENCE)

@SequenceGenerator(
    name = "user_seq_generator",
    sequenceName = "user_seq",
    allocationSize = 50  // 한 번에 50개씩 미리 할당
)
  • Sequence를 한 번 호출할 때 50개의 ID를 메모리에 할당
  • 50개의 엔티티를 생성할 때까지 DB 호출 없이 ID 할당 가능
  • 다만 애플리케이션 재시작 시 번호가 건너뛰는 현상이 있을 수 있음
  • 성능 향상 효과가 큰 최적화 방법

시퀀스 예시 (PostgreSQL)

CREATE SEQUENCE user_seq INCREMENT BY 1 START WITH 1;
CREATE TABLE users (
  user_id BIGINT PRIMARY KEY,
  email VARCHAR(255)
);

Hibernate 설정

spring.jpa.properties.hibernate.jdbc.batch_size=50
spring.jpa.properties.hibernate.order_inserts=true
spring.jpa.properties.hibernate.order_updates=true

결론

INSERT 전에 ID가 필요한 경우에는 SEQUENCE 전략이 필수입니다. 회원 생성 시 userId를 다른 로직에서 사용해야 하는 상황이라면, SEQUENCE 전략을 통해 persist() 호출만으로 ID를 확보하고, 실제 INSERT는 트랜잭션 커밋 시점까지 지연시킬 수 있습니다. 이는 성능 최적화와 함께 비즈니스 로직의 유연성을 제공합니다.

profile
화이팅

0개의 댓글