✏️ @GeneratedValue
JPA에서 ID를 자동 생성하는 방법에는 4가지 전략이 있다.
각 전략은 @GeneratedValue(strategy = GenerationType.XXX)로 지정할 수 있다.
📌 1. AUTO (기본값)
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
✅ 특징
- 기본값으로 사용됨 (명시하지 않으면 AUTO가 적용됨).
- 데이터베이스에 따라 적절한 전략을 자동 선택.
- 보통 MySQL은 IDENTITY, H2나 Oracle은 SEQUENCE로 자동 선택됨.
🛠 예제: MySQL vs H2
데이터베이스 | AUTO 전략이 선택하는 기본값 |
MySQL, MariaDB | IDENTITY (AUTO_INCREMENT 사용) |
H2, Oracle | SEQUENCE (hibernate_sequence 자동 생성) |
⚠️ 주의
- 개발 환경과 운영 환경의 데이터베이스가 다르면 AUTO 전략이 다른 방식으로 동작할 수 있음.
- 운영 환경에서 예상과 다르게 SEQUENCE가 적용되면 hibernate_sequence 테이블이 자동 생성될 수 있음.
- 명확한 ID 전략을 원하면 IDENTITY, SEQUENCE, TABLE을 직접 설정하는 것이 좋음.
📌 2. IDENTITY (AUTO_INCREMENT)
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
✅ 특징
- 데이터베이스가 ID 값을 자동 생성 (AUTO_INCREMENT 사용).
- INSERT 실행 후 DB가 생성한 ID 값을 가져옴.
- MySQL, MariaDB에서 주로 사용.
🔹 동작 방식
- persist() 호출 → 즉시 INSERT 실행
- DB가 AUTO_INCREMENT로 ID 값을 생성
- Hibernate가 생성된 ID를 조회해서 엔티티에 설정
💡 트랜잭션이 끝나기 전에 INSERT 실행됨(즉시 반영).
🛠 예제
Member member = new Member();
member.setName("John");
em.persist(member); // 여기서 바로 INSERT 실행됨
🔹 실행되는 SQL
INSERT INTO member (name) VALUES ('John');
SELECT LAST_INSERT_ID();
- SELECT LAST_INSERT_ID()로 방금 생성된 ID를 가져옴
⚠️ 주의
- JPA의 persist()를 호출하면 즉시 INSERT가 실행됨 → flush()가 필요 없음.
- 배치 처리(벌크 INSERT)에 불리함 (한 번에 여러 개 INSERT가 어려움).
- ID 값을 DB에서 직접 생성하기 때문에 영속성 컨텍스트에서 ID를 미리 알 수 없음.
📌 3. SEQUENCE (시퀀스 객체 사용)
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
@SequenceGenerator(
name = "member_seq_generator",
sequenceName = "member_seq", // 매핑할 시퀀스 이름
allocationSize = 1 // 기본값 50, 보통 1로 설정
)
private Long id;
✅ 특징
- DB의 시퀀스를 사용하여 ID를 미리 가져옴.
- @SequenceGenerator를 사용하여 원하는 시퀀스를 설정 가능.
- Oracle, PostgreSQL에서 주로 사용.
🔹 동작 방식
- persist() 호출
- Hibernate가 NEXTVAL을 호출해서 미리 ID 값을 가져옴
- 엔티티에 ID를 설정한 후 INSERT 실행 (트랜잭션 커밋 시점)
💡 persist() 시점에서는 INSERT가 실행되지 않고, ID만 미리 할당됨!
🛠 예제
Member member = new Member();
member.setName("Alice");
em.persist(member); // 여기서는 INSERT 실행 안 됨 (ID만 미리 가져옴)
🔹 실행되는 SQL
SELECT NEXTVAL('member_seq'); -- ID 미리 가져오기
INSERT INTO member (id, name) VALUES (1, 'Alice'); -- 트랜잭션 커밋 시점에 실행됨
⚠️ 주의
- allocationSize = 50이 기본값이므로, JPA가 50개씩 미리 ID를 가져와서 캐싱함.
- 동시성 처리에 유리 (IDENTITY보다 성능이 좋음).
- MySQL에서는 시퀀스를 지원하지 않으므로 사용 불가능.
📌 4. TABLE (키 생성 전용 테이블 사용)
@Id
@GeneratedValue(strategy = GenerationType.TABLE)
@TableGenerator(
name = "member_table_generator",
table = "id_table", // ID를 저장할 테이블 이름
pkColumnName = "entity", // 엔티티 이름을 저장하는 컬럼
valueColumnName = "next_id", // 다음 ID 값을 저장하는 컬럼
pkColumnValue = "member", // 엔티티 구분값
allocationSize = 1
)
private Long id;
✅ 특징
- 별도의 테이블을 만들어서 ID 값을 관리 (DBMS에 상관없이 사용 가능).
- 모든 DB에서 동작하지만 성능이 가장 느림.
🔹 동작 방식
- persist() 호출
- ID를 관리하는 테이블 (id_table)에서 다음 ID 값을 조회
- 새로운 ID 값을 member 테이블에 INSERT
💡 JPA가 직접 ID를 관리하므로, 모든 DB에서 동작 가능.
🛠 예제
SELECT next_id FROM id_table WHERE entity = 'member'; -- 다음 ID 값 가져오기
UPDATE id_table SET next_id = next_id + 1 WHERE entity = 'member'; -- ID 값 증가
INSERT INTO member (id, name) VALUES (1, 'Bob'); -- 실제 데이터 저장
⚠️ 주의
- 성능이 가장 느림 (매번 ID 조회 및 업데이트가 필요함).
- 보통 SEQUENCE를 지원하지 않는 DB에서 사용하지만, 권장되지 않음.
'JPA' 카테고리의 다른 글
[JPA] 💼 PersistentBag (0) | 2025.03.30 |
---|---|
[Spring Data JPA] 나머지 기능들 (0) | 2025.01.19 |
[Spring Data JPA] 구현체 분석 (0) | 2025.01.18 |
[Spring Data JPA] WEB 확장 기능, 페이지 1처리 코드 구현 (0) | 2025.01.18 |
[Spring Data JPA] Auditing (0) | 2025.01.18 |