Skip to content

Commit d1cb9c3

Browse files
cigalysebersole
authored andcommitted
HHH-18377 Test cases to check monofonicity of generated version 6 & version 7 UUID's
1 parent 4e85302 commit d1cb9c3

File tree

5 files changed

+335
-0
lines changed

5 files changed

+335
-0
lines changed
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/*
2+
* SPDX-License-Identifier: LGPL-2.1-or-later
3+
* Copyright Red Hat Inc. and Hibernate Authors
4+
*/
5+
package org.hibernate.orm.test.id.uuid.rfc9562;
6+
7+
import java.util.UUID;
8+
9+
import org.hibernate.annotations.UuidGenerator;
10+
import org.hibernate.id.uuid.UuidVersion7Strategy;
11+
12+
import jakarta.persistence.Basic;
13+
import jakarta.persistence.Entity;
14+
import jakarta.persistence.Id;
15+
import jakarta.persistence.Table;
16+
17+
@Entity(name = "EntitySeven")
18+
@Table(name = "entity_seven")
19+
public class EntitySeven {
20+
@Id
21+
@UuidGenerator(algorithm = UuidVersion7Strategy.class)
22+
public UUID id;
23+
@Basic
24+
public String name;
25+
26+
private EntitySeven() {
27+
// for Hibernate use
28+
}
29+
30+
public EntitySeven(String name) {
31+
this.name = name;
32+
}
33+
34+
public UUID getId() {
35+
return id;
36+
}
37+
38+
public String getName() {
39+
return name;
40+
}
41+
42+
public void setName(String name) {
43+
this.name = name;
44+
}
45+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/*
2+
* SPDX-License-Identifier: LGPL-2.1-or-later
3+
* Copyright Red Hat Inc. and Hibernate Authors
4+
*/
5+
package org.hibernate.orm.test.id.uuid.rfc9562;
6+
7+
import java.util.UUID;
8+
9+
import org.hibernate.annotations.UuidGenerator;
10+
import org.hibernate.id.uuid.UuidVersion6Strategy;
11+
12+
import jakarta.persistence.Basic;
13+
import jakarta.persistence.Entity;
14+
import jakarta.persistence.GeneratedValue;
15+
import jakarta.persistence.Id;
16+
import jakarta.persistence.Table;
17+
18+
/**
19+
* @author Steve Ebersole
20+
*/
21+
@Table(name = "entity_six")
22+
@Entity
23+
public class EntitySix {
24+
@Id
25+
@GeneratedValue
26+
@UuidGenerator(algorithm = UuidVersion6Strategy.class)
27+
private UUID id;
28+
@Basic
29+
private String name;
30+
31+
protected EntitySix() {
32+
// for Hibernate use
33+
}
34+
35+
public EntitySix(String name) {
36+
this.name = name;
37+
}
38+
39+
public UUID getId() {
40+
return id;
41+
}
42+
43+
public String getName() {
44+
return name;
45+
}
46+
47+
public void setName(String name) {
48+
this.name = name;
49+
}
50+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/*
2+
* SPDX-License-Identifier: LGPL-2.1-or-later
3+
* Copyright Red Hat Inc. and Hibernate Authors
4+
*/
5+
package org.hibernate.orm.test.id.uuid.rfc9562;
6+
7+
import java.util.UUID;
8+
9+
import org.hibernate.annotations.UuidGenerator;
10+
import org.hibernate.id.uuid.UuidVersion7Strategy;
11+
12+
import jakarta.persistence.Basic;
13+
import jakarta.persistence.Entity;
14+
import jakarta.persistence.GeneratedValue;
15+
import jakarta.persistence.Id;
16+
import jakarta.persistence.Table;
17+
18+
@Entity(name = "OtherEntitySeven")
19+
@Table(name = "other_entity_seven")
20+
public class OtherEntitySeven {
21+
@Id
22+
@GeneratedValue
23+
public Long pk;
24+
25+
@UuidGenerator(algorithm = UuidVersion7Strategy.class)
26+
public UUID id;
27+
28+
@Basic
29+
public String name;
30+
31+
private OtherEntitySeven() {
32+
// for Hibernate use
33+
}
34+
35+
public OtherEntitySeven(String name) {
36+
this.name = name;
37+
}
38+
39+
public UUID getId() {
40+
return id;
41+
}
42+
43+
public String getName() {
44+
return name;
45+
}
46+
47+
public void setName(String name) {
48+
this.name = name;
49+
}
50+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/*
2+
* SPDX-License-Identifier: LGPL-2.1-or-later
3+
* Copyright Red Hat Inc. and Hibernate Authors
4+
*/
5+
package org.hibernate.orm.test.id.uuid.rfc9562;
6+
7+
import java.util.UUID;
8+
9+
import org.hibernate.engine.spi.SharedSessionContractImplementor;
10+
import org.hibernate.id.uuid.UuidValueGenerator;
11+
import org.hibernate.id.uuid.UuidVersion6Strategy;
12+
import org.hibernate.id.uuid.UuidVersion7Strategy;
13+
14+
import org.junit.jupiter.api.Test;
15+
16+
import static org.assertj.core.api.Assertions.assertThat;
17+
import static org.mockito.Mockito.mock;
18+
19+
public class UUidV6V7GenetartorTest {
20+
21+
private static final UUID NIL_UUID = new UUID( 0L, 0L );
22+
private static final int ITERATIONS = 1_000_000;
23+
24+
@Test
25+
void testMonotonicityUuid6() {
26+
testMonotonicity( UuidVersion6Strategy.INSTANCE );
27+
}
28+
29+
@Test
30+
void testMonotonicityUuid7() {
31+
testMonotonicity( UuidVersion7Strategy.INSTANCE );
32+
}
33+
34+
private static void testMonotonicity(UuidValueGenerator generator) {
35+
final SharedSessionContractImplementor session = mock( SharedSessionContractImplementor.class );
36+
final UUID[] uuids = new UUID[ITERATIONS + 1];
37+
uuids[0] = NIL_UUID;
38+
for ( int n = 1; n <= ITERATIONS; ++n ) {
39+
uuids[n] = generator.generateUuid( session );
40+
}
41+
42+
for ( var n = 0; n < ITERATIONS; ++n ) {
43+
assertThat( uuids[n + 1].toString() ).isGreaterThan( uuids[n].toString() );
44+
assertThat( uuids[n + 1] ).isGreaterThan( uuids[n] );
45+
}
46+
}
47+
}
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
/*
2+
* SPDX-License-Identifier: LGPL-2.1-or-later
3+
* Copyright Red Hat Inc. and Hibernate Authors
4+
*/
5+
package org.hibernate.orm.test.id.uuid.rfc9562;
6+
7+
import java.time.Instant;
8+
import java.time.LocalDate;
9+
import java.time.ZoneId;
10+
import java.time.temporal.ChronoUnit;
11+
import java.util.UUID;
12+
13+
import org.hibernate.dialect.SybaseDialect;
14+
import org.hibernate.generator.Generator;
15+
import org.hibernate.id.uuid.UuidGenerator;
16+
import org.hibernate.id.uuid.UuidVersion6Strategy;
17+
import org.hibernate.id.uuid.UuidVersion7Strategy;
18+
import org.hibernate.mapping.BasicValue;
19+
import org.hibernate.mapping.Property;
20+
21+
import org.hibernate.testing.orm.junit.DomainModel;
22+
import org.hibernate.testing.orm.junit.DomainModelScope;
23+
import org.hibernate.testing.orm.junit.SessionFactory;
24+
import org.hibernate.testing.orm.junit.SessionFactoryScope;
25+
import org.hibernate.testing.orm.junit.SkipForDialect;
26+
import org.hibernate.testing.util.uuid.IdGeneratorCreationContext;
27+
import org.junit.jupiter.api.AfterEach;
28+
import org.junit.jupiter.api.Test;
29+
30+
import static org.assertj.core.api.Assertions.assertThat;
31+
32+
@SuppressWarnings("JUnitMalformedDeclaration")
33+
@DomainModel(annotatedClasses = {
34+
EntitySeven.class, OtherEntitySeven.class, EntitySix.class
35+
})
36+
@SessionFactory
37+
@SkipForDialect(dialectClass = SybaseDialect.class, matchSubTypes = true,
38+
reason = "Skipped for Sybase to avoid problems with UUIDs potentially ending with a trailing 0 byte")
39+
public class UuidGeneratorAnnotationTests {
40+
@Test
41+
public void verifyUuidV7IdGeneratorModel(final DomainModelScope scope) {
42+
scope.withHierarchy( EntitySeven.class, descriptor -> {
43+
final Property idProperty = descriptor.getIdentifierProperty();
44+
final BasicValue value = (BasicValue) idProperty.getValue();
45+
46+
assertThat( value.getCustomIdGeneratorCreator() ).isNotNull();
47+
final Generator generator = value.getCustomIdGeneratorCreator()
48+
.createGenerator( new IdGeneratorCreationContext(
49+
scope.getDomainModel(),
50+
descriptor
51+
) );
52+
53+
assertThat( generator ).isInstanceOf( UuidGenerator.class );
54+
final UuidGenerator uuidGenerator = (UuidGenerator) generator;
55+
assertThat( uuidGenerator.getValueGenerator() ).isInstanceOf( UuidVersion7Strategy.class );
56+
} );
57+
}
58+
59+
@Test
60+
public void verifyUuidV6IdGeneratorModel(final DomainModelScope scope) {
61+
scope.withHierarchy( EntitySix.class, descriptor -> {
62+
final Property idProperty = descriptor.getIdentifierProperty();
63+
final BasicValue value = (BasicValue) idProperty.getValue();
64+
65+
assertThat( value.getCustomIdGeneratorCreator() ).isNotNull();
66+
final Generator generator = value.getCustomIdGeneratorCreator()
67+
.createGenerator( new IdGeneratorCreationContext(
68+
scope.getDomainModel(),
69+
descriptor
70+
) );
71+
72+
assertThat( generator ).isInstanceOf( UuidGenerator.class );
73+
final UuidGenerator uuidGenerator = (UuidGenerator) generator;
74+
assertThat( uuidGenerator.getValueGenerator() ).isInstanceOf( UuidVersion6Strategy.class );
75+
} );
76+
}
77+
78+
@Test
79+
public void basicUseTest(final SessionFactoryScope scope) {
80+
scope.inTransaction( session -> {
81+
final EntitySeven seven = new EntitySeven( "John Doe" );
82+
session.persist( seven );
83+
session.flush();
84+
assertThat( seven.id ).isNotNull();
85+
assertThat( seven.id.version() ).isEqualTo( 7 );
86+
} );
87+
}
88+
89+
@Test
90+
public void nonPkUseTest(final SessionFactoryScope scope) {
91+
scope.inTransaction( session -> {
92+
final Instant startTime = Instant.now();
93+
94+
final OtherEntitySeven seven = new OtherEntitySeven( "Dave Default" );
95+
session.persist( seven );
96+
session.flush();
97+
98+
final Instant endTime = Instant.now();
99+
assertThat( seven.id ).isNotNull();
100+
assertThat( seven.id.version() ).isEqualTo( 7 );
101+
102+
assertThat( Instant.ofEpochMilli( seven.id.getMostSignificantBits() >> 16 & 0xFFFF_FFFF_FFFFL ) )
103+
.isBetween( startTime.truncatedTo( ChronoUnit.MILLIS ), endTime.truncatedTo( ChronoUnit.MILLIS ) );
104+
} );
105+
}
106+
107+
@Test
108+
void testUuidV6IdGenerator(final SessionFactoryScope sessionFactoryScope) {
109+
sessionFactoryScope.inTransaction( session -> {
110+
final Instant startTime = Instant.now();
111+
112+
final EntitySix six = new EntitySix( "Jane Doe" );
113+
session.persist( six );
114+
assertThat( six.getId() ).isNotNull();
115+
assertThat( six.getId().version() ).isEqualTo( 6 );
116+
117+
session.flush();
118+
final Instant endTime = Instant.now();
119+
assertThat( six.getId() ).isNotNull();
120+
assertThat( six.getId().version() ).isEqualTo( 6 );
121+
assertThat( uuid6Instant( six.getId() ) ).isBetween( startTime, endTime );
122+
} );
123+
}
124+
125+
@AfterEach
126+
void dropTestData(final SessionFactoryScope sessionFactoryScope) {
127+
sessionFactoryScope.inTransaction( session -> {
128+
session.createMutationQuery( "delete EntitySeven" ).executeUpdate();
129+
session.createMutationQuery( "delete OtherEntitySeven" ).executeUpdate();
130+
session.createMutationQuery( "delete EntitySix" ).executeUpdate();
131+
} );
132+
}
133+
134+
public static Instant uuid6Instant(final UUID uuid) {
135+
assert uuid.version() == 6;
136+
137+
final var msb = uuid.getMostSignificantBits();
138+
final var ts = msb >> 4 & 0x0FFF_FFFF_FFFF_F000L | msb & 0x0FFFL;
139+
return LocalDate.of( 1582, 10, 15 ).atStartOfDay( ZoneId.of( "UTC" ) ).toInstant()
140+
.plusSeconds( ts / 10_000_000 ).plusNanos( ts % 10_000_000 * 100 );
141+
}
142+
143+
}

0 commit comments

Comments
 (0)