JPA에는
@Entity
public class Users{
@Id
private long id;
}
@Id 어노테이션을 통해 기본키로 설정할 필드를 지정할 수 있습니다.
여기서 기본키 값을 생성해주는
@Entity
public class Users{
@Id
@GeneratedValue
private long id;
}
어노테이션이 있는데 여기서 사용할 수 있는 전략이 4가지 있습니다.
1. Auto
@Entity
public class Users {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private long id;
}
AUTO 전략은 JPA에서 기본 키를 자동으로 생성할 때 사용되는 기본 전략입니다. 이 전략은 JPA 구현체(예: Hibernate)가 사용하는 데이터베이스에 맞춰 가장 적합한 방식으로 기본 키를 생성하도록 합니다.
AUTO 전략의 동작 방식
- @GeneratedValue(strategy = GenerationType.AUTO)를 사용하면, JPA는 데이터베이스에 적합한 기본 키 생성 전략을 자동으로 선택합니다.
- Hibernate를 사용한다면, 기본적으로 SequenceStyleGenerator를 통해 키를 생성합니다.
- 시퀀스(Sequence)를 지원하는 데이터베이스에서는 시퀀스를 사용합니다.
- **시퀀스를 지원하지 않는 데이터베이스(MySQL 등)**에서는 키 생성용 테이블을 생성하여 사용합니다.
- 만약, 특정 전략을 지정하지 않고 @GeneratedValue만 사용하면 GenerationType.AUTO가 기본값으로 적용됩니다.
2.IDENTITY
@Entity
public class Users {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
}
- IDENTITY 전략은 말 그대로 데이터베이스의 “IDENTITY 컬럼” 기능을 이용해 기본 키를 생성하는 방식입니다.
- MySQL의 AUTO_INCREMENT, MSSQL의 IDENTITY 같은 기능을 사용하죠. 데이터베이스가 자동으로 증가되는 숫자를 직접 관리하는 개념입니다.
- 개발자가 별도로 시퀀스(Sequence)나 테이블을 설정하지 않아도 되고, 데이터를 INSERT하는 시점에 DB가 직접 ID를 결정합니다.
정리하자면, IDENTITY를 사용하면 데이터베이스가 알아서 자동 증가값을 만들어 주는 전략이기 때문에, 손쉽게 사용할 수 있습니다. 다만 DB 별로 IDENTITY 기능 지원 여부와 동작 방식이 다를 수 있으니, 사용하는 DB가 IDENTITY를 지원하는지 미리 확인하는 게 좋습니다.
3.SEQUENCE
@Entity
@SequenceGenerator(
name = "USER_SEQ_GENERATOR",
sequenceName = "USER_SEQ", // 데이터베이스 시퀀스 이름
initialValue = 1, // 시퀀스 초기값
allocationSize = 1 // 증가값
)
public class Users {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE,
generator = "USER_SEQ_GENERATOR")
private long id;
}
- SEQUENCE 전략은 데이터베이스가 제공하는 “시퀀스(Sequence) 오브젝트”를 이용해 기본 키를 생성합니다.
- 오라클(Oracle), PostgreSQL 등에서 시퀀스를 지원하고 있죠.
- 위의 예시처럼 @SequenceGenerator를 활용해 어떤 시퀀스를 쓸 것인지 지정해줄 수 있습니다. initialValue, allocationSize 같은 속성으로 초기값이나 한 번에 증가할 양을 세세하게 조정할 수 있어요.
쉽게 말해, “숫자를 1씩 자동으로 올려주는 기계(시퀀스)를 하나 더 만들어 놓고, 거기서 새로 저장할 데이터의 ID를 뽑아 쓰는 방식”이라고 생각하면 됩니다.
4. TABLE
@Entity
@TableGenerator(
name = "USER_TABLE_GENERATOR",
table = "MY_SEQUENCES", // 키 생성 정보를 저장할 테이블 이름
pkColumnName = "SEQ_NAME", // 시퀀스 이름을 저장할 컬럼
valueColumnName = "SEQ_NUMBER", // 시퀀스 값(다음에 사용할 번호)을 저장할 컬럼
pkColumnValue = "USER_SEQ", // 해당 엔티티에 사용할 시퀀스 이름
initialValue = 1,
allocationSize = 1
)
public class Users {
@Id
@GeneratedValue(strategy = GenerationType.TABLE,
generator = "USER_TABLE_GENERATOR")
private long id;
}
- TABLE 전략은 별도의 “키 생성 전용 테이블”을 만들고, 그 테이블에서 현재 사용할 수 있는 다음 키 값을 조회하여 ID를 생성하는 방식입니다.
- 어떤 DB든지 테이블만 만들 수 있으면 쓸 수 있으니 호환성 면에서 유리할 수 있습니다.
- 하지만 매번 테이블을 조회해야 하므로 다른 전략(IDENTITY, SEQUENCE)에 비해 성능 면에서 약간 손해가 있을 수 있습니다.
“TABLE 전략”은 작은 노트(테이블)에 “현재 다음으로 쓸 번호”를 적어두고, 새로운 데이터가 생길 때마다 그 노트에서 다음 번호를 꺼내 써서 ID를 발급한다고 보시면 됩니다.
4. 개발 중 겪은 문제와 해결 과정
JPA를 처음 사용하면서 개발을 진행하던 중, 로컬 환경에서 MySQL을 사용하며 모든 엔티티의 기본 키 자동 생성 전략을 AUTO로 설정했었습니다.
@Entity
public class Users {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private long id;
}
그런데, 개발 중 MySQL 스키마에 생성된 테이블들을 확인하다 보니, 예상치 못하게 seq 테이블이 추가로 생성되어 있는 것을 발견했습니다.
예를 들어, users라는 테이블을 생성했을 때, users_seq라는 테이블이 함께 만들어진 것을 볼 수 있었습니다.
users
users_seq
이 상황에서 “왜 이런 테이블이 생성되는 거지?” 하는 의문이 들었고, 그 이유를 알아보기 위해 조사를 시작했습니다.
문제의 원인
JPA에서는 기본 키 생성 전략이 AUTO로 설정된 경우, Hibernate가 내부적으로 SequenceStyleGenerator라는 클래스를 사용합니다.
이 클래스는 다음과 같은 방식으로 작동합니다.
- 데이터베이스가 시퀀스(Sequence)를 지원하는 경우, 시퀀스를 이용해 기본 키를 생성합니다.
- 데이터베이스가 시퀀스를 지원하지 않을 경우, 테이블(Table) 방식으로 기본 키를 생성합니다.
MySQL은 시퀀스를 지원하지 않기 때문에, JPA는 테이블 방식을 사용하여 기본 키를 생성하려고 합니다.
이를 위해 **seq 테이블(키 생성 전용 테이블)**이 자동으로 생성된 것입니다.
결국, Hibernate가 MySQL의 제약(시퀀스 미지원)을 인지하고 테이블 방식으로 기본 키를 생성하도록 동작했던 것입니다.
해결 방법
이 문제를 해결하기 위해 JPA의 기본 키 자동 생성 전략을 MySQL 환경에 적합한 IDENTITY로 변경했습니다.
@Entity
public class Users {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
}
- IDENTITY 전략은 MySQL의 AUTO_INCREMENT를 활용해 기본 키를 생성합니다.
- 따라서, 추가적인 seq 테이블이 생성되지 않고, users 테이블 자체의 id 컬럼에서 자동으로 ID 값이 관리됩니다.
결과와 배운 점
기본 키 생성 전략을 IDENTITY로 변경한 뒤, 더 이상 불필요한 seq 테이블이 생성되지 않았습니다.
이 과정을 통해 JPA에서의 기본 키 생성 전략이 데이터베이스 환경에 따라 달라질 수 있다는 점을 확실히 이해하게 되었습니다.
'BackEnd > JPA' 카테고리의 다른 글
SQL 중심 개발의 한계와 JPA 도입의 필요성 (1) | 2025.06.10 |
---|---|
주소 문자열을 지역 엔티티에 정확히 매핑하기 (feat. 중복 이름 처리) (0) | 2025.05.12 |
양방향 연관관계에서 편의 메서드를 사용하는 이유 (0) | 2025.04.28 |
JPA 엔티티 설계 쉽게 이해하기: Region 클래스 리팩터링 사례 (0) | 2025.04.23 |
JPA @Id 필드, primitive(int) vs Wrapper(Integer) (0) | 2025.04.21 |