diff --git a/hibernate-core/src/main/java/org/hibernate/annotations/UuidGenerator.java b/hibernate-core/src/main/java/org/hibernate/annotations/UuidGenerator.java
index 25d73166fd09..38fabec3a26e 100644
--- a/hibernate-core/src/main/java/org/hibernate/annotations/UuidGenerator.java
+++ b/hibernate-core/src/main/java/org/hibernate/annotations/UuidGenerator.java
@@ -9,6 +9,8 @@
import java.util.UUID;
import org.hibernate.Incubating;
+import org.hibernate.id.uuid.UuidVersion6Strategy;
+import org.hibernate.id.uuid.UuidVersion7Strategy;
import org.hibernate.id.uuid.UuidValueGenerator;
import static java.lang.annotation.ElementType.FIELD;
@@ -52,7 +54,19 @@ enum Style {
* @implNote Can be a bottleneck, since synchronization is used when
* incrementing an internal counter as part of the algorithm.
*/
- TIME
+ TIME,
+ /**
+ * Use a time-based generation strategy consistent with RFC 4122
+ * version 6.
+ * @see UuidVersion6Strategy
+ */
+ VERSION_6,
+ /**
+ * Use a time-based generation strategy consistent with RFC 4122
+ * version 7.
+ * @see UuidVersion7Strategy
+ */
+ VERSION_7
}
/**
diff --git a/hibernate-core/src/main/java/org/hibernate/id/uuid/UuidGenerator.java b/hibernate-core/src/main/java/org/hibernate/id/uuid/UuidGenerator.java
index 380606c390e1..92b4465b7be1 100644
--- a/hibernate-core/src/main/java/org/hibernate/id/uuid/UuidGenerator.java
+++ b/hibernate-core/src/main/java/org/hibernate/id/uuid/UuidGenerator.java
@@ -23,6 +23,8 @@
import static org.hibernate.annotations.UuidGenerator.Style.AUTO;
import static org.hibernate.annotations.UuidGenerator.Style.TIME;
+import static org.hibernate.annotations.UuidGenerator.Style.VERSION_6;
+import static org.hibernate.annotations.UuidGenerator.Style.VERSION_7;
import static org.hibernate.generator.EventTypeSets.INSERT_ONLY;
import static org.hibernate.internal.util.ReflectHelper.getPropertyType;
@@ -78,9 +80,15 @@ private static UuidValueGenerator determineValueGenerator(
}
return instantiateCustomGenerator( config.algorithm() );
}
- else if ( config.style() == TIME ) {
+ if ( config.style() == TIME ) {
return new CustomVersionOneStrategy();
}
+ else if ( config.style() == VERSION_6 ) {
+ return UuidVersion6Strategy.INSTANCE;
+ }
+ else if ( config.style() == VERSION_7 ) {
+ return UuidVersion7Strategy.INSTANCE;
+ }
}
return StandardRandomStrategy.INSTANCE;
diff --git a/hibernate-core/src/main/java/org/hibernate/id/uuid/UuidVersion6Strategy.java b/hibernate-core/src/main/java/org/hibernate/id/uuid/UuidVersion6Strategy.java
new file mode 100644
index 000000000000..1e629d7a5bdb
--- /dev/null
+++ b/hibernate-core/src/main/java/org/hibernate/id/uuid/UuidVersion6Strategy.java
@@ -0,0 +1,130 @@
+/*
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ * Copyright Red Hat Inc. and Hibernate Authors
+ */
+package org.hibernate.id.uuid;
+
+import java.security.SecureRandom;
+import java.time.Duration;
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.ZoneId;
+import java.util.UUID;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+
+import org.hibernate.engine.spi.SharedSessionContractImplementor;
+import org.hibernate.id.UUIDGenerationStrategy;
+
+/**
+ * Implements UUID Version 6 generation strategy as defined by the RFC 9562.
+ *
+ *
+ * - 32 bits - the most significant 32 bits of the 60-bit starting timestamp.
+ * - 16 bits - the middle 16 bits of the 60-bit starting timestamp.
+ * - 4 bits - version field, set to 0b0110 (6).
+ * - 12 bits - the least significant 12 bits from the 60-bit starting timestamp.
+ * - 2 bits - variant field, set to 0b10.
+ * - 14 bits - the clock sequence, resets to 0 when timestamp changes.
+ * - 48 bits - pseudorandom data to provide uniqueness.
+ *
+ *
+ * @author Cedomir Igaly
+ */
+public class UuidVersion6Strategy implements UUIDGenerationStrategy, UuidValueGenerator {
+
+ private static final Instant EPOCH_1582;
+
+ static {
+ EPOCH_1582 = LocalDate.of( 1582, 10, 15 )
+ .atStartOfDay( ZoneId.of( "UTC" ) )
+ .toInstant();
+ }
+
+ private static class Holder {
+
+ static final SecureRandom numberGenerator = new SecureRandom();
+ }
+
+ public static final UuidVersion6Strategy INSTANCE = new UuidVersion6Strategy();
+
+ private final Lock lock = new ReentrantLock( true );
+
+ private long currentTimestamp;
+
+ private final AtomicLong clockSequence = new AtomicLong( 0 );
+
+ public UuidVersion6Strategy() {
+ this( getCurrentTimestamp(), 0 );
+ }
+
+ public UuidVersion6Strategy(final long currentTimestamp, final long clockSequence) {
+ this.currentTimestamp = currentTimestamp;
+ this.clockSequence.set( clockSequence );
+ }
+
+ /**
+ * A variant 6
+ */
+ @Override
+ public int getGeneratedVersion() {
+ // UUIDv6 is a field-compatible version of UUIDv1, reordered for improved DB locality
+ return 6;
+ }
+
+ /**
+ * Delegates to {@link #generateUuid}
+ */
+ @Override
+ public UUID generateUUID(SharedSessionContractImplementor session) {
+ return generateUuid( session );
+ }
+
+
+ /**
+ * @param session session
+ *
+ * @return UUID version 6
+ * @see UuidValueGenerator#generateUuid(SharedSessionContractImplementor)
+ */
+ @Override
+ public UUID generateUuid(SharedSessionContractImplementor session) {
+ final long currentTimestamp = getCurrentTimestamp();
+
+ return new UUID(
+ // MSB bits 0-47 - most significant 32 bits of the 60-bit starting timestamp
+ currentTimestamp << 4 & 0xFFFF_FFFF_FFFF_0000L
+ // MSB bits 48-51 - version = 6
+ | 0x6000L
+ // MSB bits 52-63 - least significant 12 bits from the 60-bit starting timestamp
+ | currentTimestamp & 0x0FFFL,
+ // LSB bits 0-1 - variant = 4
+ 0x8000_0000_0000_0000L
+ // LSB bits 2-15 - clock sequence
+ | ( getSequence( currentTimestamp ) & 0x3FFFL ) << 48
+ // LSB bits 16-63 - pseudorandom data
+ | Holder.numberGenerator.nextLong() & 0xFFFF_FFFF_FFFFL
+ );
+ }
+
+
+ private long getSequence(final long currentTimestamp) {
+ lock.lock();
+ try {
+ if ( this.currentTimestamp > currentTimestamp ) {
+ this.currentTimestamp = currentTimestamp;
+ clockSequence.set( 0 );
+ }
+ }
+ finally {
+ lock.unlock();
+ }
+ return clockSequence.getAndIncrement();
+ }
+
+ private static long getCurrentTimestamp() {
+ final Duration duration = Duration.between( EPOCH_1582, Instant.now() );
+ return duration.toSeconds() * 10_000_000 + duration.toNanosPart() / 100;
+ }
+}
diff --git a/hibernate-core/src/main/java/org/hibernate/id/uuid/UuidVersion7Strategy.java b/hibernate-core/src/main/java/org/hibernate/id/uuid/UuidVersion7Strategy.java
new file mode 100644
index 000000000000..6f51c8f48f6c
--- /dev/null
+++ b/hibernate-core/src/main/java/org/hibernate/id/uuid/UuidVersion7Strategy.java
@@ -0,0 +1,131 @@
+/*
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ * Copyright Red Hat Inc. and Hibernate Authors
+ */
+package org.hibernate.id.uuid;
+
+import java.security.SecureRandom;
+import java.time.Duration;
+import java.time.Instant;
+import java.util.UUID;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+
+import org.hibernate.engine.spi.SharedSessionContractImplementor;
+import org.hibernate.id.UUIDGenerationStrategy;
+
+import static java.time.Instant.EPOCH;
+import static java.time.temporal.ChronoUnit.MILLIS;
+
+/**
+ * Implements UUID Version 7 generation strategy as defined by the RFC 9562.
+ *
+ *
+ * - 48 bits - 48-bit big-endian unsigned number of the Unix Epoch timestamp in milliseconds.
+ * - 4 bits - version field, set to 0b0111 (7).
+ * -
+ * 12 bits - sub-milliseconds part of timestamp (resolution approximately 1/4 of millisecond)
+ * to guarantee additional monotonicity.
+ *
+ * - 2 bits - variant field, set to 0b10.
+ * - 14 bits - counter to guarantee additional monotonicity, resets to 0 when timestamp changes.
+ * - 48 bits - pseudorandom data to provide uniqueness.
+ *
+ *
+ * @author Cedomir Igaly
+ */
+public class UuidVersion7Strategy implements UUIDGenerationStrategy, UuidValueGenerator {
+
+ public static final UuidVersion7Strategy INSTANCE = new UuidVersion7Strategy();
+
+ private static class Holder {
+
+ static final SecureRandom numberGenerator = new SecureRandom();
+ }
+
+ private final Lock lock = new ReentrantLock( true );
+
+ private Duration currentTimestamp;
+
+ private final AtomicLong clockSequence;
+
+ public UuidVersion7Strategy() {
+ this( getCurrentTimestamp(), 0 );
+ }
+
+ public UuidVersion7Strategy(final Duration currentTimestamp, final long clockSequence) {
+ this.currentTimestamp = currentTimestamp;
+ this.clockSequence = new AtomicLong( clockSequence );
+ }
+
+ /**
+ * A variant 7
+ */
+ @Override
+ public int getGeneratedVersion() {
+ /*
+ * UUIDv7 features a time-ordered value field derived from the widely implemented and well-
+ * known Unix Epoch timestamp source, the number of milliseconds since midnight 1 Jan 1970 UTC,
+ * leap seconds excluded.
+ */
+ return 7;
+ }
+
+ /**
+ * Delegates to {@link #generateUuid}
+ */
+ @Override
+ public UUID generateUUID(SharedSessionContractImplementor session) {
+ return generateUuid( session );
+ }
+
+ /**
+ * @param session session
+ *
+ * @return UUID version 7
+ * @see UuidValueGenerator#generateUuid(SharedSessionContractImplementor)
+ */
+ @Override
+ public UUID generateUuid(SharedSessionContractImplementor session) {
+ final Duration currentTimestamp = getCurrentTimestamp();
+
+ final long seq = getSequence( currentTimestamp );
+
+ final long millis = currentTimestamp.getSeconds() * 1000 + currentTimestamp.getNano() / 1_000_000;
+ final long nanosPart = Math.round( ( currentTimestamp.getNano() % 1_000_000L ) * 0.004096 );
+
+ return new UUID(
+ // MSB bits 0-47 - 48-bit big-endian unsigned number of the Unix Epoch timestamp in milliseconds
+ millis << 16 & 0xFFFF_FFFF_FFFF_0000L
+ // MSB bits 48-51 - version = 7
+ | 0x7000L
+ // MSB bits 52-63 - sub-milliseconds part of timestamp
+ | nanosPart & 0xFFFL,
+ // LSB bits 0-1 - variant = 4
+ 0x8000_0000_0000_0000L
+ // LSB bits 2-15 - counter
+ | ( seq & 0x3FFFL ) << 48
+ // LSB bits 16-63 - pseudorandom data
+ | Holder.numberGenerator.nextLong() & 0xFFFF_FFFF_FFFFL
+ );
+ }
+
+ private long getSequence(final Duration currentTimestamp) {
+ lock.lock();
+ try {
+ if ( !this.currentTimestamp.equals( currentTimestamp ) ) {
+ this.currentTimestamp = currentTimestamp;
+ clockSequence.set( 0 );
+ }
+ }
+ finally {
+ lock.unlock();
+ }
+ return clockSequence.getAndIncrement();
+ }
+
+ private static Duration getCurrentTimestamp() {
+ return Duration.between( EPOCH, Instant.now() ).truncatedTo( MILLIS );
+ }
+}
diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/id/uuid/rfc9562/EntitySeven.java b/hibernate-core/src/test/java/org/hibernate/orm/test/id/uuid/rfc9562/EntitySeven.java
new file mode 100644
index 000000000000..4a9a9c7b71df
--- /dev/null
+++ b/hibernate-core/src/test/java/org/hibernate/orm/test/id/uuid/rfc9562/EntitySeven.java
@@ -0,0 +1,45 @@
+/*
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ * Copyright Red Hat Inc. and Hibernate Authors
+ */
+package org.hibernate.orm.test.id.uuid.rfc9562;
+
+import java.util.UUID;
+
+import org.hibernate.annotations.UuidGenerator;
+import org.hibernate.id.uuid.UuidVersion7Strategy;
+
+import jakarta.persistence.Basic;
+import jakarta.persistence.Entity;
+import jakarta.persistence.Id;
+import jakarta.persistence.Table;
+
+@Entity(name = "EntitySeven")
+@Table(name = "entity_seven")
+public class EntitySeven {
+ @Id
+ @UuidGenerator(algorithm = UuidVersion7Strategy.class)
+ public UUID id;
+ @Basic
+ public String name;
+
+ private EntitySeven() {
+ // for Hibernate use
+ }
+
+ public EntitySeven(String name) {
+ this.name = name;
+ }
+
+ public UUID getId() {
+ return id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+}
diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/id/uuid/rfc9562/EntitySix.java b/hibernate-core/src/test/java/org/hibernate/orm/test/id/uuid/rfc9562/EntitySix.java
new file mode 100644
index 000000000000..adbd07b577a1
--- /dev/null
+++ b/hibernate-core/src/test/java/org/hibernate/orm/test/id/uuid/rfc9562/EntitySix.java
@@ -0,0 +1,50 @@
+/*
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ * Copyright Red Hat Inc. and Hibernate Authors
+ */
+package org.hibernate.orm.test.id.uuid.rfc9562;
+
+import java.util.UUID;
+
+import org.hibernate.annotations.UuidGenerator;
+import org.hibernate.id.uuid.UuidVersion6Strategy;
+
+import jakarta.persistence.Basic;
+import jakarta.persistence.Entity;
+import jakarta.persistence.GeneratedValue;
+import jakarta.persistence.Id;
+import jakarta.persistence.Table;
+
+/**
+ * @author Steve Ebersole
+ */
+@Table(name = "entity_six")
+@Entity
+public class EntitySix {
+ @Id
+ @GeneratedValue
+ @UuidGenerator(algorithm = UuidVersion6Strategy.class)
+ private UUID id;
+ @Basic
+ private String name;
+
+ protected EntitySix() {
+ // for Hibernate use
+ }
+
+ public EntitySix(String name) {
+ this.name = name;
+ }
+
+ public UUID getId() {
+ return id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+}
diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/id/uuid/rfc9562/OtherEntitySeven.java b/hibernate-core/src/test/java/org/hibernate/orm/test/id/uuid/rfc9562/OtherEntitySeven.java
new file mode 100644
index 000000000000..1726e1d51c8c
--- /dev/null
+++ b/hibernate-core/src/test/java/org/hibernate/orm/test/id/uuid/rfc9562/OtherEntitySeven.java
@@ -0,0 +1,50 @@
+/*
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ * Copyright Red Hat Inc. and Hibernate Authors
+ */
+package org.hibernate.orm.test.id.uuid.rfc9562;
+
+import java.util.UUID;
+
+import org.hibernate.annotations.UuidGenerator;
+import org.hibernate.id.uuid.UuidVersion7Strategy;
+
+import jakarta.persistence.Basic;
+import jakarta.persistence.Entity;
+import jakarta.persistence.GeneratedValue;
+import jakarta.persistence.Id;
+import jakarta.persistence.Table;
+
+@Entity(name = "OtherEntitySeven")
+@Table(name = "other_entity_seven")
+public class OtherEntitySeven {
+ @Id
+ @GeneratedValue
+ public Long pk;
+
+ @UuidGenerator(algorithm = UuidVersion7Strategy.class)
+ public UUID id;
+
+ @Basic
+ public String name;
+
+ private OtherEntitySeven() {
+ // for Hibernate use
+ }
+
+ public OtherEntitySeven(String name) {
+ this.name = name;
+ }
+
+ public UUID getId() {
+ return id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+}
diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/id/uuid/rfc9562/UUidV6V7GenetartorTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/id/uuid/rfc9562/UUidV6V7GenetartorTest.java
new file mode 100644
index 000000000000..678bc00b8a75
--- /dev/null
+++ b/hibernate-core/src/test/java/org/hibernate/orm/test/id/uuid/rfc9562/UUidV6V7GenetartorTest.java
@@ -0,0 +1,47 @@
+/*
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ * Copyright Red Hat Inc. and Hibernate Authors
+ */
+package org.hibernate.orm.test.id.uuid.rfc9562;
+
+import java.util.UUID;
+
+import org.hibernate.engine.spi.SharedSessionContractImplementor;
+import org.hibernate.id.uuid.UuidValueGenerator;
+import org.hibernate.id.uuid.UuidVersion6Strategy;
+import org.hibernate.id.uuid.UuidVersion7Strategy;
+
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+
+public class UUidV6V7GenetartorTest {
+
+ private static final UUID NIL_UUID = new UUID( 0L, 0L );
+ private static final int ITERATIONS = 1_000_000;
+
+ @Test
+ void testMonotonicityUuid6() {
+ testMonotonicity( UuidVersion6Strategy.INSTANCE );
+ }
+
+ @Test
+ void testMonotonicityUuid7() {
+ testMonotonicity( UuidVersion7Strategy.INSTANCE );
+ }
+
+ private static void testMonotonicity(UuidValueGenerator generator) {
+ final SharedSessionContractImplementor session = mock( SharedSessionContractImplementor.class );
+ final UUID[] uuids = new UUID[ITERATIONS + 1];
+ uuids[0] = NIL_UUID;
+ for ( int n = 1; n <= ITERATIONS; ++n ) {
+ uuids[n] = generator.generateUuid( session );
+ }
+
+ for ( var n = 0; n < ITERATIONS; ++n ) {
+ assertThat( uuids[n + 1].toString() ).isGreaterThan( uuids[n].toString() );
+ assertThat( uuids[n + 1] ).isGreaterThan( uuids[n] );
+ }
+ }
+}
diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/id/uuid/rfc9562/UuidGeneratorAnnotationTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/id/uuid/rfc9562/UuidGeneratorAnnotationTests.java
new file mode 100644
index 000000000000..58314d6740b2
--- /dev/null
+++ b/hibernate-core/src/test/java/org/hibernate/orm/test/id/uuid/rfc9562/UuidGeneratorAnnotationTests.java
@@ -0,0 +1,143 @@
+/*
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ * Copyright Red Hat Inc. and Hibernate Authors
+ */
+package org.hibernate.orm.test.id.uuid.rfc9562;
+
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.ZoneId;
+import java.time.temporal.ChronoUnit;
+import java.util.UUID;
+
+import org.hibernate.dialect.SybaseDialect;
+import org.hibernate.generator.Generator;
+import org.hibernate.id.uuid.UuidGenerator;
+import org.hibernate.id.uuid.UuidVersion6Strategy;
+import org.hibernate.id.uuid.UuidVersion7Strategy;
+import org.hibernate.mapping.BasicValue;
+import org.hibernate.mapping.Property;
+
+import org.hibernate.testing.orm.junit.DomainModel;
+import org.hibernate.testing.orm.junit.DomainModelScope;
+import org.hibernate.testing.orm.junit.SessionFactory;
+import org.hibernate.testing.orm.junit.SessionFactoryScope;
+import org.hibernate.testing.orm.junit.SkipForDialect;
+import org.hibernate.testing.util.uuid.IdGeneratorCreationContext;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+@SuppressWarnings("JUnitMalformedDeclaration")
+@DomainModel(annotatedClasses = {
+ EntitySeven.class, OtherEntitySeven.class, EntitySix.class
+})
+@SessionFactory
+@SkipForDialect(dialectClass = SybaseDialect.class, matchSubTypes = true,
+ reason = "Skipped for Sybase to avoid problems with UUIDs potentially ending with a trailing 0 byte")
+public class UuidGeneratorAnnotationTests {
+ @Test
+ public void verifyUuidV7IdGeneratorModel(final DomainModelScope scope) {
+ scope.withHierarchy( EntitySeven.class, descriptor -> {
+ final Property idProperty = descriptor.getIdentifierProperty();
+ final BasicValue value = (BasicValue) idProperty.getValue();
+
+ assertThat( value.getCustomIdGeneratorCreator() ).isNotNull();
+ final Generator generator = value.getCustomIdGeneratorCreator()
+ .createGenerator( new IdGeneratorCreationContext(
+ scope.getDomainModel(),
+ descriptor
+ ) );
+
+ assertThat( generator ).isInstanceOf( UuidGenerator.class );
+ final UuidGenerator uuidGenerator = (UuidGenerator) generator;
+ assertThat( uuidGenerator.getValueGenerator() ).isInstanceOf( UuidVersion7Strategy.class );
+ } );
+ }
+
+ @Test
+ public void verifyUuidV6IdGeneratorModel(final DomainModelScope scope) {
+ scope.withHierarchy( EntitySix.class, descriptor -> {
+ final Property idProperty = descriptor.getIdentifierProperty();
+ final BasicValue value = (BasicValue) idProperty.getValue();
+
+ assertThat( value.getCustomIdGeneratorCreator() ).isNotNull();
+ final Generator generator = value.getCustomIdGeneratorCreator()
+ .createGenerator( new IdGeneratorCreationContext(
+ scope.getDomainModel(),
+ descriptor
+ ) );
+
+ assertThat( generator ).isInstanceOf( UuidGenerator.class );
+ final UuidGenerator uuidGenerator = (UuidGenerator) generator;
+ assertThat( uuidGenerator.getValueGenerator() ).isInstanceOf( UuidVersion6Strategy.class );
+ } );
+ }
+
+ @Test
+ public void basicUseTest(final SessionFactoryScope scope) {
+ scope.inTransaction( session -> {
+ final EntitySeven seven = new EntitySeven( "John Doe" );
+ session.persist( seven );
+ session.flush();
+ assertThat( seven.id ).isNotNull();
+ assertThat( seven.id.version() ).isEqualTo( 7 );
+ } );
+ }
+
+ @Test
+ public void nonPkUseTest(final SessionFactoryScope scope) {
+ scope.inTransaction( session -> {
+ final Instant startTime = Instant.now();
+
+ final OtherEntitySeven seven = new OtherEntitySeven( "Dave Default" );
+ session.persist( seven );
+ session.flush();
+
+ final Instant endTime = Instant.now();
+ assertThat( seven.id ).isNotNull();
+ assertThat( seven.id.version() ).isEqualTo( 7 );
+
+ assertThat( Instant.ofEpochMilli( seven.id.getMostSignificantBits() >> 16 & 0xFFFF_FFFF_FFFFL ) )
+ .isBetween( startTime.truncatedTo( ChronoUnit.MILLIS ), endTime.truncatedTo( ChronoUnit.MILLIS ) );
+ } );
+ }
+
+ @Test
+ void testUuidV6IdGenerator(final SessionFactoryScope sessionFactoryScope) {
+ sessionFactoryScope.inTransaction( session -> {
+ final Instant startTime = Instant.now();
+
+ final EntitySix six = new EntitySix( "Jane Doe" );
+ session.persist( six );
+ assertThat( six.getId() ).isNotNull();
+ assertThat( six.getId().version() ).isEqualTo( 6 );
+
+ session.flush();
+ final Instant endTime = Instant.now();
+ assertThat( six.getId() ).isNotNull();
+ assertThat( six.getId().version() ).isEqualTo( 6 );
+ assertThat( uuid6Instant( six.getId() ) ).isBetween( startTime, endTime );
+ } );
+ }
+
+ @AfterEach
+ void dropTestData(final SessionFactoryScope sessionFactoryScope) {
+ sessionFactoryScope.inTransaction( session -> {
+ session.createMutationQuery( "delete EntitySeven" ).executeUpdate();
+ session.createMutationQuery( "delete OtherEntitySeven" ).executeUpdate();
+ session.createMutationQuery( "delete EntitySix" ).executeUpdate();
+ } );
+ }
+
+ public static Instant uuid6Instant(final UUID uuid) {
+ assert uuid.version() == 6;
+
+ final var msb = uuid.getMostSignificantBits();
+ final var ts = msb >> 4 & 0x0FFF_FFFF_FFFF_F000L | msb & 0x0FFFL;
+ return LocalDate.of( 1582, 10, 15 ).atStartOfDay( ZoneId.of( "UTC" ) ).toInstant()
+ .plusSeconds( ts / 10_000_000 ).plusNanos( ts % 10_000_000 * 100 );
+ }
+
+}
diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/FunctionTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/FunctionTests.java
index 4602e2267df2..7f36bed33e62 100644
--- a/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/FunctionTests.java
+++ b/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/FunctionTests.java
@@ -69,7 +69,6 @@
import static org.hibernate.testing.orm.domain.gambit.EntityOfBasics.Gender.FEMALE;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertInstanceOf;
import static org.junit.jupiter.api.Assertions.fail;
import static org.junit.jupiter.api.Assertions.assertThrows;