[개발일지]/CS 스터디 플랫폼

13장. MySQL에서 되던게 PostgreSQL 에선 왜 안돼? (JPA의 @GeneratedValue 전략 차이)

biz-ninza 2025. 3. 25. 17:08

JPA에서 @GeneratedValue(strategy = GenerationType.IDENTITY)를 사용하면 기본 키(ID)를 자동 증가(AUTO_INCREMENT)하도록 설정된다.
 
MySQL에서는 IDENTITY 전략을 사용하면 JPA가 AUTO_INCREMENT 속성을 추가하며 알아서 작동한다.
PostgreSQL에서는 IDENTITY 전략을 사용하더라도 SERIAL 또는 IDENTITY를 사용해야한다.
 
MySQL만 쓰다가 PostgreSQL 로 넘어오면서 생긴 시행착오와 공부
 
 
 

목차

1. JPA의 기본키 생성전략
2. PostgreSQL 에서 IDENTITY 전략 사용하려면
3. PostgreSQL 에서 SEQUENCE 전략 사용하려면
 
 
 

1. JPA의 기본키 생성전략

 
JPA의 @GeneratedValue 전략은 다음과 같다.

IDENTITY DB의 AUTO_INCREMENT 사용 (MySQL, PostgreSQL SERIAL)
SEQUENCE DB의 Sequence 사용 (nextval(), PostgreSQL, Oracle)
AUTO DB에 맞게 자동 선택
TABLE 키 값을 저장하는 별도 테이블을 사용

 
이는 데이터베이스의 자동 증가 기능(AUTO_INCREMENT, SERIAL, IDENTITY 등)을 직접 활용하는 방식이다.
즉, JPA가 MYSQL 에서는 어플리케이션이 실행될 때 해당 주키에 AUTO_INCREMENT 속성을 부여한다. 
 

// MySQL
INSERT INTO users (phone_number, password, created_at) VALUES ('01012345678', 'password123', NOW());
SELECT LAST_INSERT_ID();  -- 자동 증가된 ID 값을 가져옴

// PostgreSQL
INSERT INTO users (phone_number, password, created_at) VALUES ('01012345678', 'password123', NOW()) RETURNING id;

 
실제 쿼리문을 살펴보면, IDENTITY 전략에서는 JPA는 후에 Create 관련 쿼리가 들어왔을 때 영속성 컨텍스트에 엔티티를 등록하고, DB에 INSERT 쿼리를 먼저실행하여 생성된 ID 값을 가져오며, 다른 전략에서는 JPA가 flush 될때까지 INSERT 를 미루다가 persist() 할 때 INSERT를 실행한다.
 
GeneratedValue 를 실행하면 최종적으로 JPA 가 위와 같은 INSERT 문을 날리게 된다
 

  1. IDENTITY → 간단한 경우 추천
  2. SEQUENCE → 대규모 트랜잭션에서 성능 최적화 가능
  3. AUTO -> 여러 DB를 지원해야하는 경우
  4. TABLE -> 거의 사용하지 않음

 
한편, PostgreSQL 에서는 테이블에 SERIAL을 미리 명시해야 자동 증가된다는 차이가 있다!
 
 
 

2. PostgreSQL 에서 IDENTITY 전략 사용하려면

PostgreSQL에서는 IDENTITY 로 자동증가되는 기본키를 만들때 SERIAL 혹은 IDENTITY를 사용한다.

// MySQL
CREATE TABLE users (
    id BIGINT AUTO_INCREMENT PRIMARY KEY,
);

// PostgreSQL
CREATE TABLE users (
    id SERIAL PRIMARY KEY,  -- MySQL의 AUTO_INCREMENT와 같은 역할
);

// PostgreSQL 10 이상에서는 IDENTITY 도 가능 (표준 SQL 방식의 자동증가 기능이라고함)
CREATE TABLE users (
    id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
);

 
SERIAL : 내부적으로 시퀀스를 생성하여 nextval()을 사용해서 자동 증가
IDENTITY : PostgreSQL 10부터 추가된 표준 SQL 방식의 자동 증가 기능
 
SERIAL 을 넣으면 자동으로 users_id_seq라는 시퀀스를 만들고 id 컬럼의 기본값을 nextval('users_id_seq')로 설정한다
SERIAL은 별도의 시퀀스를 만드는 반면, IDENTITY는 내부적으로 시퀀스를 관리한다
 
즉 SERIAL 은 데이터 타입이 아니라 AUTO_INCREMENT 같은 '속성' 이다.
다만 내부적으로 INTEGER 데이터타입을 포함하고있어서 별도로 타입을 명시하지 않는다. 
(BIGSERIAL 을 사용하면 자동으로 BIGINT 타입이 됨)
 
 
 

3. PostgreSQL 에서 SEQUENCE 전략 사용하려면

 
혹은 기본키 전략을 아예 SEQUENCE 로 진행할 수도 있다.
GenerationType.SEQUENCE는 JPA가 직접 명시적으로 시퀀스를 사용하도록 설정하는 것

@Entity
public class User {
    @Id
    // 시퀀스를 사용하여 ID를 생성
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "user_seq")
    // "users_id_seq" 시퀀스를 사용하도록 설정
    @SequenceGenerator(name = "user_seq", sequenceName = "users_id_seq", allocationSize = 1)
    private Long id;
}

 
위 코드는 PostgreSQL에서 "users_id_seq"라는 시퀀스를 사용하여 ID를 자동 증가시키겠다는 뜻이다.
이 설정을 하면 JPA가 다음과 같은 쿼리를 실행한다: SELECT nextval('users_id_seq');
 

  • 기본적으로 Hibernate는 allocationSize = 50을 사용해서 한 번에 50개씩 미리 가져온다.
  • allocationSize = 1을 설정하면 한 번씩 증가하도록 설정하는 것이다.

 

BIGSERIAL과 BIGINT + SEQUENCE의 차이

id SERIAL PRIMARY KEY 자동 증가하는 INTEGER 타입 컬럼 CREATE SEQUENCE + DEFAULT nextval(...)
id BIGSERIAL PRIMARY KEY 자동 증가하는 BIGINT 타입 컬럼 CREATE SEQUENCE + DEFAULT nextval(...)
id BIGINT PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY PostgreSQL 표준 IDENTITY 컬럼 내부적으로 시퀀스 관리
id BIGINT PRIMARY KEY + SEQUENCE 시퀀스를 명시적으로 생성 후 연결 직접 시퀀스를 관리해야 함

 

 

 

 

DDL-AUTO

  • ddl-auto=create 또는 create-drop이면 JPA가 새 테이블을 만들 때 SERIAL을 자동 생성함.
    • @GeneratedValue(strategy = GenerationType.IDENTITY)를 사용하면 PostgreSQL에서 SERIAL이 포함된 DDL을 생성함.
  • ddl-auto=update는 기존 테이블을 변경하지 않음.
    • 즉, id BIGINT PRIMARY KEY라고 이미 존재하면, JPA가 SERIAL을 추가로 설정하지 않음.
    • 자동 증가 기능이 없는 상태로 남아있음.
    • 변경하려면 수동으로 ALTER TABLE 해야 함.

ALTER COLUMN id SET DEFAULT nextval('users_id_seq'::regclass);은 id 컬럼이 시퀀스를 사용하도록 설정하는 쿼리

users_id_seq라는 시퀀스를 사용하여 id 값을 자동 증가

 

기존 flyway migration .sql 파일에 위의 쿼리문 삽입해서 해결!