-
Notifications
You must be signed in to change notification settings - Fork 3.5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
HHH-18377 Test cases to check monofonicity of generated version 6 & v…
…ersion 7 UUID's
- Loading branch information
Showing
6 changed files
with
335 additions
and
1 deletion.
There are no files selected for viewing
45 changes: 45 additions & 0 deletions
45
hibernate-core/src/test/java/org/hibernate/orm/test/id/uuid/rfc9562/EntitySeven.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; | ||
} | ||
} |
50 changes: 50 additions & 0 deletions
50
hibernate-core/src/test/java/org/hibernate/orm/test/id/uuid/rfc9562/EntitySix.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; | ||
} | ||
} |
50 changes: 50 additions & 0 deletions
50
hibernate-core/src/test/java/org/hibernate/orm/test/id/uuid/rfc9562/OtherEntitySeven.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; | ||
} | ||
} |
47 changes: 47 additions & 0 deletions
47
...ate-core/src/test/java/org/hibernate/orm/test/id/uuid/rfc9562/UUidV6V7GenetartorTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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] ); | ||
} | ||
} | ||
} |
143 changes: 143 additions & 0 deletions
143
...re/src/test/java/org/hibernate/orm/test/id/uuid/rfc9562/UuidGeneratorAnnotationTests.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 ); | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters