UUID v7 생성기 추가 - Hibernate IdentifierGenerator 구현
eapim-portal CI (from elink-portal-common) / build (push) Has been cancelled
eapim-portal CI (from elink-portal-common) / build (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,66 @@
|
|||||||
|
package com.eactive.apim.portal.jpa;
|
||||||
|
|
||||||
|
import org.hibernate.HibernateException;
|
||||||
|
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||||
|
import org.hibernate.id.IdentifierGenerator;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.security.SecureRandom;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UUID version 7 (time-ordered, RFC 9562) 생성기.
|
||||||
|
*
|
||||||
|
* <p>상위 48비트에 Unix epoch 밀리초 타임스탬프를 두어 생성 시각 순으로 정렬되므로,
|
||||||
|
* 무작위 v4 대비 인덱스(B-Tree) 삽입 지역성이 좋아 PK 인덱스 단편화를 줄인다.
|
||||||
|
* canonical 36자 문자열로 반환하여 기존 {@code VARCHAR2(36)} 컬럼과 호환된다.</p>
|
||||||
|
*
|
||||||
|
* <p>레이아웃(128비트): unix_ts_ms(48) | ver=0b0111(4) | rand_a(12) | var=0b10(2) | rand_b(62)</p>
|
||||||
|
*
|
||||||
|
* <p>Java 8 호환(java.util.UUID + SecureRandom). Hibernate {@link IdentifierGenerator} 구현이며
|
||||||
|
* 엔티티에서 {@code @GenericGenerator(strategy = "...UuidV7Generator")} 로 지정한다.</p>
|
||||||
|
*/
|
||||||
|
public class UuidV7Generator implements IdentifierGenerator {
|
||||||
|
|
||||||
|
private static final SecureRandom RANDOM = new SecureRandom();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Serializable generate(SharedSessionContractImplementor session, Object object) throws HibernateException {
|
||||||
|
return generateString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** UUID v7 을 canonical 36자 문자열로 생성한다. */
|
||||||
|
public static String generateString() {
|
||||||
|
long timestamp = System.currentTimeMillis();
|
||||||
|
|
||||||
|
byte[] value = new byte[16];
|
||||||
|
|
||||||
|
// 상위 48비트: unix epoch millis (big-endian)
|
||||||
|
value[0] = (byte) ((timestamp >>> 40) & 0xFF);
|
||||||
|
value[1] = (byte) ((timestamp >>> 32) & 0xFF);
|
||||||
|
value[2] = (byte) ((timestamp >>> 24) & 0xFF);
|
||||||
|
value[3] = (byte) ((timestamp >>> 16) & 0xFF);
|
||||||
|
value[4] = (byte) ((timestamp >>> 8) & 0xFF);
|
||||||
|
value[5] = (byte) (timestamp & 0xFF);
|
||||||
|
|
||||||
|
// 나머지 10바이트는 난수로 채운다.
|
||||||
|
byte[] rand = new byte[10];
|
||||||
|
RANDOM.nextBytes(rand);
|
||||||
|
System.arraycopy(rand, 0, value, 6, 10);
|
||||||
|
|
||||||
|
// version 7: value[6] 상위 니블을 0111 로 설정
|
||||||
|
value[6] = (byte) ((value[6] & 0x0F) | 0x70);
|
||||||
|
// variant 10xx: value[8] 상위 2비트를 10 으로 설정
|
||||||
|
value[8] = (byte) ((value[8] & 0x3F) | 0x80);
|
||||||
|
|
||||||
|
long msb = 0L;
|
||||||
|
for (int i = 0; i < 8; i++) {
|
||||||
|
msb = (msb << 8) | (value[i] & 0xFFL);
|
||||||
|
}
|
||||||
|
long lsb = 0L;
|
||||||
|
for (int i = 8; i < 16; i++) {
|
||||||
|
lsb = (lsb << 8) | (value[i] & 0xFFL);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new java.util.UUID(msb, lsb).toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user