JPA 및 Hibernate 내에서 엔티티에 대한 식별자(Id)를 생성하는 다양한 옵션에 대해 정리한다.
식별자는 엔티티 기본 키를 모델링하고 특정 엔터티를 고유하게 식별하는 데 사용된다. 기본 키는 데이터베이스 내의 행을 고유하게 식별한다. 이론적으로 식별자는 기본 키와 일치할 필요가 없다. 식별자는 각 행을 고유하게 식별하는 열에 매핑되기만 하면 되기 때문이다.
- 여기서는 기본 키와 식별자(Id) 는 일부로 같은 의미로 사용한다.
기본키와(pk) 식별자(Id)는 다음 제약조건이 제한된다
UNIQUE
- 값은 각 행을 고유하게 식별해야 한다.NOT NULL
- 값은 null일 수 없다. 복합 ID (복합 키)의 경우 어떤 부분도 null이 될 수 없다.IMMUTABLE
- 한 번 삽입된 값은 절대 변경할 수 없고, 변경되면 안된다..
식별자는 단일 값(단순) 또는 여러 값(복합)일 수 있다. 모든 엔터티는 식별자를 정의해야 한다.
Generated Identifiers - 기본생성된 식별자
Hibernate는 간단한 식별자가 생성되는 기능을 @GeneratedValue
를 통해 제공한다.
식별자 속성에 어노테이션을 추가하면 된다. Hibernate는 엔터티가 persistence 되면 식별자 값을 생성한다.
- javax.persistence.
GeneratedValue
어노테이션 : 식별자 값이 generated한다는 것을 나타낸다. - javax.persistence.
GenerationType
어노테이션 : 식별자 값이 생성되는 방법을 지정한다.
값이 생성되는 방법은 4가지 주요 키 생성 전략(strategy)가 있다.
hibernate6 에서는 GenerationType.UUID가 추가되어 5가지가 있다.
AUTO
- persistence provider(Jpa 구현체) 가 자동으로 특정 데이터베이스에 대해 적절한 생성 전략 결정- IdGeneratorStrategyInterpreter를 사용했지만, hibernate6부터 deprecated 되었다.
- hibernate6부터 IdentityGenerationTypeStrategy로 대체되었다
IDENTITY
- persistence provider(Jpa 구현체) 가 기본키 생성을 데이터베이스에게 위임한다.- MySQL의 경우 AUTO_INCREMENT를 사용하여 기본키를 생성한다.
- hibernate6부터 IdentityGenerationTypeStrategy로 사용된다.
SEQUENCE
- persistence provider(Jpa 구현체)가 데이터베이스 시퀀스를 사용하여 엔터티에 대한 기본 키를 할당한다.SequenceStyleGenerator
클래스를 이용한다.
TABLE
- persistence provider(Jpa 구현체)가 데이터베이스에 키 생성 전용 테이블을 만들고 이를 사용하여 기본키를 할당한다UUID
(hibernate6 에서부터 지원) : RFC 4122 Universally Unique IDentifier를 생성하여 기본키 할당
미리 정의되어 있는 Hibernate Strategy
식별자를 생성하는 데 사용할 수 있는 Hibernate에서 제공하는 많은 일반 Generator가 구현되어있다. 모든 Generator는 org.hibernate.id.IdentifierGenerator
인터페이스를 구현한다.
@Entity
public class MyEntity {
@Id
@GeneratedValue(generator = "uuid")
@GenericGenerator(
name = "uuid",
strategy = "org.hibernate.id.UUIDGenerator",
parameters = {
@Parameter(
name = "uuid_gen_strategy_class",
value = "org.hibernate.id.uuid.CustomVersionOneStrategy"
)
}
)
public UUID id;
IdentifierGeneratorFactory 클래스에 저장되어 있는 Hibernate Shortcuts(generator)의 의미는 다음과 같다.
- 하이버네이트 5.x대와 6.x 버전대에 지정되어있는 generator들이 다르다.
하이버네이트 5.x대 generator
DefaultIdentifierGeneratorFactory 클래스
public class DefaultIdentifierGeneratorFactory
implements MutableIdentifierGeneratorFactory, Serializable, ServiceRegistryAwareService {
public DefaultIdentifierGeneratorFactory(boolean ignoreBeanContainer) {
this.ignoreBeanContainer = ignoreBeanContainer;
register( "uuid2", UUIDGenerator.class );
register( "guid", GUIDGenerator.class ); // can be done with UUIDGenerator + strategy
register( "uuid", UUIDHexGenerator.class ); // "deprecated" for new use
register( "uuid.hex", UUIDHexGenerator.class ); // uuid.hex is deprecated
register( "assigned", Assigned.class );
register( "identity", IdentityGenerator.class );
register( "select", SelectGenerator.class );
register( "sequence", SequenceStyleGenerator.class );
register( "seqhilo", SequenceHiLoGenerator.class );
register( "increment", IncrementGenerator.class );
register( "foreign", ForeignGenerator.class );
register( "sequence-identity", SequenceIdentityGenerator.class );
register( "enhanced-sequence", SequenceStyleGenerator.class );
register( "enhanced-table", TableGenerator.class );
}
...
}
increment
- 다른 프로세스가 동일한 테이블에 데이터를 삽입하지 않는 경우에만 고유한 long, short 또는 int 유형의 식별자를 생성identity
- DB2, MySQL, MS SQL Server, Sybase 및 HypersonicSQL의 ID 열(AUTO_INCREMENT))을 지원sequence
- DB2, PostgreSQL, Oracle, SAP DB, McKoi의 시퀀스 또는 Interbase의 생성기를 사용.hilo
- hi 값의 소스로 테이블과 열이 주어진 경우 hi/lo 알고리즘을 사용하여 long, short 또는 int 유형의 식별자를 효율적으로 생성.seqhilo
- 지정된 데이터베이스 시퀀스가 주어지면 hi/lo 알고리즘을 사용하여 long, short 또는 int 유형의 식별자를 효율적으로 생성uuid
- 사용자 지정 알고리즘을 기반으로 128비트 UUID를 생성uuid2
- IETF RFC 4122 호환(버전 2) 128비트 UUID를 생성guid
- MS SQL Server 및 MySQL에서 데이터베이스 생성 GUID 문자열을 사용native
- 기본 데이터베이스의 기능에 따라 ID, 시퀀스 또는 hilo를 선택assigned
- save()가 호출되기 전에 응용 프로그램이 entity에 식별자를 할당할 수 있다.select
- 고유한 키로 행을 선택하고 기본 키 값을 검색하여 데이터베이스 트리거에 의해 할당된 기본 키를 검색foreign
- 연결된 다른 개체의 식별자를 사용sequence-identity
- 실제 값 생성을 위해 데이터베이스 시퀀스를 활용하는 특수 시퀀스 생성 전략
하이버네이트 6.x대 generator
org.hibernate.id.factory.internal
패키지의 StandardIdentifierGeneratorFactory에서 알 수 있다.
register( "uuid2", UUIDGenerator.class );
// can be done with UuidGenerator + strategy
register( "guid", GUIDGenerator.class );
register( "uuid", UUIDHexGenerator.class ); // "deprecated" for new use
register( "uuid.hex", UUIDHexGenerator.class ); // uuid.hex is deprecated
register( "assigned", Assigned.class );
register( "identity", IdentityGenerator.class );
register( "select", SelectGenerator.class );
register( "sequence", SequenceStyleGenerator.class );
register( "increment", IncrementGenerator.class );
register( "foreign", ForeignGenerator.class );
register( "enhanced-sequence", SequenceStyleGenerator.class );
register( "enhanced-table", TableGenerator.class );
uuid2
- IETF RFC 4122 호환(버전 2) 128비트 UUID를 생성guid
- MS SQL Server 및 MySQL에서 데이터베이스 생성 GUID 문자열을 사용uuid
- 사용자 지정 알고리즘을 기반으로 128비트 UUID를 생성uuid.hex
- 길이가 32인 문자열을 반환하는 UUIDGeneratorassigned
- save()가 호출되기 전에 응용 프로그램이 entity에 식별자를 할당할 수 있다.identity
- 기본 키로 사용되는 ANSI-SQL IDENTITY 열과 함께 사용하기 위한 generater- DB2, MySQL, MS SQL Server, Sybase 및 HypersonicSQL의 ID 열(AUTO_INCREMENT))을 지원
select
- 고유한 키로 행을 선택하고 기본 키 값을 검색하여 데이터베이스 트리거에 의해 할당된 기본 키를 검색- 방금 삽입한 행을 선택하여 데이터베이스에서 할당한 식별자 값을 결정, 하나의 매핑 매개변수가 필요
sequence
- DB2, PostgreSQL, Oracle, SAP DB, McKoi의 시퀀스 또는 Interbase의 생성기를 사용.increment
- 다른 프로세스가 동일한 테이블에 데이터를 삽입하지 않는 경우에만 고유한 long, short 또는 int 유형의 식별자를 생성foreign
- 연결된 다른 개체의 식별자를 사용enhanced-sequence
: 실제 값 생성을 위해 데이터베이스 시퀀스를 활용하는 특수 시퀀스 생성 전략enhanced-table
: Table Generator 사용.
기본 Custom Generator들
- AutoGenerationTypeStrategy
- IdentityGenerationTypeStrategy
- SequenceGenerationTypeStrategy
- TableGenerationTypeStrategy
- UUIDGenerationTypeStrategy
UUID 전략
springboot 3.0 and hibernate 6.x대에서는 GenerationType.UUID라는 타입을 지원한다.
- 아쉽게도 2.7.x대에는 GenerationType에 UUID라는 Type이 없다.
- Hibernate 5에서 도입된 UUIDGenerator 전략은 간단하게 선언할 수 있다.
다음과 같이 3가지로 지정해서 사용할 수 있으며, String 타입 ,UUID 타입을 모두 지원
한다.
- @GenerateValue만 사용
- @GenericGenerator(strategy = "uuid2") 사용
- @GeneratedValue(strategy = GenerationType.UUID) 사용 (springboot 3.0 & hibernate6 에서만 지원)
UUID의 컬럼 타입은 MySQL 기준으로 BINARY(16)을 사용해야 한다 (A single UUID needs 16 bytes.)
그렇지 않으면 조회하지 못할 수도 있다저장할 때 남는 길이는 오른쪽으로 패딩 처리하여 저장하기 때문이다.
1. GenerateValue만 사용
@Entity
public class UUIDEntity {
@Id
@GeneratedValue
private UUID id;
// ...
}
// repository
public interface UUIDRepository extends JpaRepository<UUIDEntity, UUID> { }
- 이 방법은 필드 타입이 String인 경우 UUID를 생성해주지 않고 에러를 생성한다.
- org.springframework.orm.jpa.JpaSystemException: Unknown integral data type for ids : java.lang.String
2. @GenericGenerator(strategy = "uuid2") 사용
hibernate에서 UuidGenerator
클래스를 이용해서 키값을 생성해서 사용할 수 있다.
- org.hibernate.id.uuid 패키지
@Entity
@Getter
@NoArgsConstructor
public class UUIDEntity {
@Id
@GeneratedValue(generator = "uuid")
@GenericGenerator(name = "uuid", strategy = "uuid2")
private UUID id;
}
//repository
public interface UUIDRepository extends JpaRepository<UUIDEntity, UUID> { }
----------------------------------------------------------------
// 또는
@Entity
public class UUIDEntity {
@Id
@GeneratedValue(generator = "uuid")
@GenericGenerator(name = "uuid", strategy = "uuid2")
private String id;
}
//repository
public interface UUIDRepository extends JpaRepository<UUIDEntity, String> { }
- ddl-auto 를 사용하면 db 컬럼 타입은 지정된 필드의 타입(uuid 또는 String) 으로 지정된다.
- 필드 타입이 uuid인 경우 db 컬럼 타입은 uuid. String인 경우 varchar
3. @GeneratedValue(strategy = GenerationType.UUID) 사용 - springboot 3.0 & hibernate 6.x대
@Entity
public class UUIDEntity {
@Id
@GeneratedValue(strategy = GenerationType.UUID)
private UUID id; // java.util.uuid
}
// repository
public interface UUIDRepository extends JpaRepository<UUIDEntity, UUID> { }
----------------------------------------------------------------
// 또는
@Entity
public class UUIDEntity {
@Id
@GeneratedValue(strategy = GenerationType.UUID)
private String id;
}
//repository
public interface UUIDRepository extends JpaRepository<UUIDEntity, String> { }
- ddl-auto 를 사용하면 db 컬럼 타입은 지정된 필드의 타입(uuid 또는 String) 으로 지정된다.
- 필드 타입이 uuid인 경우 db 컬럼 타입은 uuid. String인 경우 varchar
springboot 3.0 & hibernate 6.x대
에서만 지원하는GenerationType.UUID
다른 전략은
org.hibernate.id.factory.internal
패키지의 StandardIdentifierGeneratorFactory에서 알 수 있다.
Custom Id 생성 전략
'Spring > JPA' 카테고리의 다른 글
Jpa Insert, update delete 시 select query를 통한 먼저 조회하지 않는 방법.md (1) | 2023.01.18 |
---|---|
Jpa Id 생성전략에 따른 동작 방식 (0) | 2023.01.18 |
Jpa Hibernate Custom Id Generator (1) | 2023.01.18 |
Jpa ArrayType. PostgreSQL Array Type을 저장하는 방법 - Hibernate Types, List, Array (0) | 2023.01.05 |
Cannot call sendError() after the response has been committed - 순환 참조 문제 With JPA Entity (0) | 2022.12.23 |