diff --git a/hibernate-core/src/main/java/org/hibernate/annotations/UuidGenerator.java b/hibernate-core/src/main/java/org/hibernate/annotations/UuidGenerator.java
new file mode 100644
index 0000000000..1b32a6c216
--- /dev/null
+++ b/hibernate-core/src/main/java/org/hibernate/annotations/UuidGenerator.java
@@ -0,0 +1,45 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * License: GNU Lesser General Public License (LGPL), version 2.1 or later
+ * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
+ */
+package org.hibernate.annotations;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.util.UUID;
+
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+
+@IdGeneratorType( org.hibernate.id.uuid.UuidGenerator.class )
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ FIELD, METHOD })
+public @interface UuidGenerator {
+
+ enum Style {
+ /**
+ * Defaults to {@link #RANDOM}
+ */
+ AUTO,
+ /**
+ * Uses {@link UUID#randomUUID()} to generate values
+ */
+ RANDOM,
+ /**
+ * Applies a time-based generation strategy consistent with IETF RFC 4122. Uses
+ * IP address rather than mac address.
+ *
+ * NOTE : Can be a bottleneck due to the need to synchronize in order to increment an
+ * internal count as part of the algorithm.
+ */
+ TIME
+ }
+
+ /**
+ * Which style of generation should be used
+ */
+ Style style() default Style.AUTO;
+}
diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/AnnotationBinder.java b/hibernate-core/src/main/java/org/hibernate/cfg/AnnotationBinder.java
index c62f501df0..8c8ab60314 100644
--- a/hibernate-core/src/main/java/org/hibernate/cfg/AnnotationBinder.java
+++ b/hibernate-core/src/main/java/org/hibernate/cfg/AnnotationBinder.java
@@ -8,6 +8,7 @@ package org.hibernate.cfg;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Member;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -116,6 +117,7 @@ import org.hibernate.mapping.BasicValue;
import org.hibernate.mapping.Component;
import org.hibernate.mapping.Constraint;
import org.hibernate.mapping.DependantValue;
+import org.hibernate.mapping.IdentifierGeneratorCreator;
import org.hibernate.mapping.Join;
import org.hibernate.mapping.JoinedSubclass;
import org.hibernate.mapping.KeyValue;
@@ -2545,38 +2547,16 @@ public final class AnnotationBinder {
XClass entityXClass = inferredData.getClassOrElement();
XProperty idXProperty = inferredData.getProperty();
- //manage composite related metadata
- //guess if its a component and find id data access (property, field etc)
- final boolean isComponent = entityXClass.isAnnotationPresent( Embeddable.class )
- || idXProperty.isAnnotationPresent( EmbeddedId.class );
-
final Annotation generatorAnnotation = HCANNHelper.findContainingAnnotation( idXProperty, IdGeneratorType.class, buildingContext );
if ( generatorAnnotation != null ) {
- final IdGeneratorType idGeneratorType = generatorAnnotation.annotationType().getAnnotation( IdGeneratorType.class );
- assert idGeneratorType != null;
-
- idValue.setCustomIdGeneratorCreator( (context) -> {
- final Class extends IdentifierGenerator> generatorClass = idGeneratorType.value();
- try {
- return generatorClass
- .getConstructor( generatorAnnotation.annotationType(), CustomIdGeneratorCreationContext.class )
- .newInstance( generatorAnnotation, context );
- }
- catch (NoSuchMethodException e) {
- throw new HibernateException(
- "Unable to find appropriate constructor for @IdGeneratorType handling : " + generatorClass.getName(),
- e
- );
- }
- catch (InvocationTargetException | InstantiationException | IllegalAccessException e) {
- throw new HibernateException(
- "Unable to invoke constructor for @IdGeneratorType handling : " + generatorClass.getName(),
- e
- );
- }
- } );
+ idValue.setCustomIdGeneratorCreator( new CustomIdGeneratorCreator( generatorAnnotation, idXProperty ) );
}
else {
+ //manage composite related metadata
+ //guess if its a component and find id data access (property, field etc)
+ final boolean isComponent = entityXClass.isAnnotationPresent( Embeddable.class )
+ || idXProperty.isAnnotationPresent( EmbeddedId.class );
+
GeneratedValue generatedValue = idXProperty.getAnnotation( GeneratedValue.class );
String generatorType = generatedValue != null
@@ -3751,4 +3731,39 @@ public final class AnnotationBinder {
LOG.ignoreNotFoundWithFetchTypeLazy( entity, association );
}
}
+
+ private static class CustomIdGeneratorCreator implements IdentifierGeneratorCreator {
+ private final Annotation generatorAnnotation;
+ private final Member underlyingMember;
+
+ public CustomIdGeneratorCreator(Annotation generatorAnnotation, XProperty idXProperty) {
+ this.generatorAnnotation = generatorAnnotation;
+ this.underlyingMember = HCANNHelper.getUnderlyingMember( idXProperty );
+ }
+
+ @Override
+ public IdentifierGenerator createGenerator(CustomIdGeneratorCreationContext context) {
+ final IdGeneratorType idGeneratorType = generatorAnnotation.annotationType().getAnnotation( IdGeneratorType.class );
+ assert idGeneratorType != null;
+
+ final Class extends IdentifierGenerator> generatorClass = idGeneratorType.value();
+ try {
+ return generatorClass
+ .getConstructor( generatorAnnotation.annotationType(), Member.class, CustomIdGeneratorCreationContext.class )
+ .newInstance( generatorAnnotation, underlyingMember, context );
+ }
+ catch (NoSuchMethodException e) {
+ throw new HibernateException(
+ "Unable to find appropriate constructor for @IdGeneratorType handling : " + generatorClass.getName(),
+ e
+ );
+ }
+ catch (InvocationTargetException | InstantiationException | IllegalAccessException e) {
+ throw new HibernateException(
+ "Unable to invoke constructor for @IdGeneratorType handling : " + generatorClass.getName(),
+ e
+ );
+ }
+ }
+ }
}
diff --git a/hibernate-core/src/main/java/org/hibernate/id/UUIDGenerationStrategy.java b/hibernate-core/src/main/java/org/hibernate/id/UUIDGenerationStrategy.java
index 06450b89d1..5fd32bfc6f 100644
--- a/hibernate-core/src/main/java/org/hibernate/id/UUIDGenerationStrategy.java
+++ b/hibernate-core/src/main/java/org/hibernate/id/UUIDGenerationStrategy.java
@@ -14,8 +14,9 @@ import org.hibernate.engine.spi.SharedSessionContractImplementor;
/**
* A strategy for generating a variant 2 {@link UUID} value.
*
- * @author Steve Ebersole
+ * @deprecated see {@link UUIDGenerator}
*/
+@Deprecated
public interface UUIDGenerationStrategy extends Serializable {
/**
* Which variant, according to IETF RFC 4122, of UUID does this strategy generate? RFC 4122 defines
diff --git a/hibernate-core/src/main/java/org/hibernate/id/UUIDGenerator.java b/hibernate-core/src/main/java/org/hibernate/id/UUIDGenerator.java
index 0fd0c3443d..be17a2ec5c 100644
--- a/hibernate-core/src/main/java/org/hibernate/id/UUIDGenerator.java
+++ b/hibernate-core/src/main/java/org/hibernate/id/UUIDGenerator.java
@@ -36,8 +36,10 @@ import org.hibernate.type.descriptor.java.UUIDJavaTypeDescriptor;
*
{@link org.hibernate.id.uuid.CustomVersionOneStrategy}
*
*
- * @author Steve Ebersole
+ * @deprecated (since 6.0) - use {@link org.hibernate.id.uuid.UuidGenerator} and
+ * {@link org.hibernate.annotations.UuidGenerator} instead
*/
+@Deprecated
public class UUIDGenerator implements IdentifierGenerator {
private static final CoreMessageLogger LOG = CoreLogging.messageLogger( UUIDGenerator.class );
diff --git a/hibernate-core/src/main/java/org/hibernate/id/uuid/CustomVersionOneStrategy.java b/hibernate-core/src/main/java/org/hibernate/id/uuid/CustomVersionOneStrategy.java
index 4293b5cc0b..e8a5f2444e 100644
--- a/hibernate-core/src/main/java/org/hibernate/id/uuid/CustomVersionOneStrategy.java
+++ b/hibernate-core/src/main/java/org/hibernate/id/uuid/CustomVersionOneStrategy.java
@@ -23,7 +23,7 @@ import org.hibernate.internal.util.BytesHelper;
*
* @author Steve Ebersole
*/
-public class CustomVersionOneStrategy implements UUIDGenerationStrategy {
+public class CustomVersionOneStrategy implements UUIDGenerationStrategy, UuidGenerator.ValueGenerator {
@Override
public int getGeneratedVersion() {
return 1;
@@ -44,12 +44,18 @@ public class CustomVersionOneStrategy implements UUIDGenerationStrategy {
mostSignificantBits = BytesHelper.asLong( hiBits );
}
+
@Override
- public UUID generateUUID(SharedSessionContractImplementor session) {
+ public UUID generateUuid(SharedSessionContractImplementor session) {
long leastSignificantBits = generateLeastSignificantBits( System.currentTimeMillis() );
return new UUID( mostSignificantBits, leastSignificantBits );
}
+ @Override
+ public UUID generateUUID(SharedSessionContractImplementor session) {
+ return generateUuid( session );
+ }
+
public long getMostSignificantBits() {
return mostSignificantBits;
}
diff --git a/hibernate-core/src/main/java/org/hibernate/id/uuid/StandardRandomStrategy.java b/hibernate-core/src/main/java/org/hibernate/id/uuid/StandardRandomStrategy.java
index c4ba650b39..24536568db 100644
--- a/hibernate-core/src/main/java/org/hibernate/id/uuid/StandardRandomStrategy.java
+++ b/hibernate-core/src/main/java/org/hibernate/id/uuid/StandardRandomStrategy.java
@@ -16,7 +16,7 @@ import org.hibernate.id.UUIDGenerationStrategy;
*
* @author Steve Ebersole
*/
-public class StandardRandomStrategy implements UUIDGenerationStrategy {
+public class StandardRandomStrategy implements UUIDGenerationStrategy, UuidGenerator.ValueGenerator {
public static final StandardRandomStrategy INSTANCE = new StandardRandomStrategy();
/**
@@ -33,6 +33,11 @@ public class StandardRandomStrategy implements UUIDGenerationStrategy {
*/
@Override
public UUID generateUUID(SharedSessionContractImplementor session) {
+ return generateUuid( session );
+ }
+
+ @Override
+ public UUID generateUuid(SharedSessionContractImplementor session) {
return UUID.randomUUID();
}
}
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
new file mode 100644
index 0000000000..57ed7fa1ff
--- /dev/null
+++ b/hibernate-core/src/main/java/org/hibernate/id/uuid/UuidGenerator.java
@@ -0,0 +1,72 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * License: GNU Lesser General Public License (LGPL), version 2.1 or later
+ * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
+ */
+package org.hibernate.id.uuid;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Member;
+import java.lang.reflect.Method;
+import java.util.UUID;
+
+import org.hibernate.HibernateException;
+import org.hibernate.engine.spi.SharedSessionContractImplementor;
+import org.hibernate.id.IdentifierGenerator;
+import org.hibernate.id.factory.spi.CustomIdGeneratorCreationContext;
+import org.hibernate.type.descriptor.java.UUIDJavaTypeDescriptor;
+import org.hibernate.type.descriptor.java.UUIDJavaTypeDescriptor.ValueTransformer;
+
+import static org.hibernate.annotations.UuidGenerator.Style.TIME;
+
+/**
+ * UUID-based IdentifierGenerator
+ *
+ * @see org.hibernate.annotations.UuidGenerator
+ */
+public class UuidGenerator implements IdentifierGenerator {
+ interface ValueGenerator {
+ UUID generateUuid(SharedSessionContractImplementor session);
+ }
+
+ private final ValueGenerator generator;
+ private final ValueTransformer valueTransformer;
+
+ public UuidGenerator(
+ org.hibernate.annotations.UuidGenerator config,
+ Member idMember,
+ CustomIdGeneratorCreationContext creationContext) {
+ if ( config.style() == TIME ) {
+ generator = new CustomVersionOneStrategy();
+ }
+ else {
+ generator = StandardRandomStrategy.INSTANCE;
+ }
+
+ final Class> propertyType;
+ if ( idMember instanceof Method ) {
+ propertyType = ( (Method) idMember ).getReturnType();
+ }
+ else {
+ propertyType = ( (Field) idMember ).getType();
+ }
+
+ if ( UUID.class.isAssignableFrom( propertyType ) ) {
+ valueTransformer = UUIDJavaTypeDescriptor.PassThroughTransformer.INSTANCE;
+ }
+ else if ( String.class.isAssignableFrom( propertyType ) ) {
+ valueTransformer = UUIDJavaTypeDescriptor.ToStringTransformer.INSTANCE;
+ }
+ else if ( byte[].class.isAssignableFrom( propertyType ) ) {
+ valueTransformer = UUIDJavaTypeDescriptor.ToBytesTransformer.INSTANCE;
+ }
+ else {
+ throw new HibernateException( "Unanticipated return type [" + propertyType.getName() + "] for UUID conversion" );
+ }
+ }
+
+ public Object generate(SharedSessionContractImplementor session, Object object) throws HibernateException {
+ return valueTransformer.transform( generator.generateUuid( session ) );
+ }
+}
diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/id/custom/CustomGeneratorTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/id/custom/CustomGeneratorTests.java
index 879122fc34..7d9d5a2657 100644
--- a/hibernate-core/src/test/java/org/hibernate/orm/test/id/custom/CustomGeneratorTests.java
+++ b/hibernate-core/src/test/java/org/hibernate/orm/test/id/custom/CustomGeneratorTests.java
@@ -35,8 +35,12 @@ public class CustomGeneratorTests {
@Test
public void basicUseTest(SessionFactoryScope scope) {
+ assertThat( SimpleSequenceGenerator.generationCount ).isEqualTo( 0 );
+
scope.inTransaction( (session) -> {
session.persist( new TheEntity( "steve" ) );
} );
+
+ assertThat( SimpleSequenceGenerator.generationCount ).isEqualTo( 1 );
}
}
diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/id/custom/SimpleSequenceGenerator.java b/hibernate-core/src/test/java/org/hibernate/orm/test/id/custom/SimpleSequenceGenerator.java
index 3771d5534f..ee2318e31d 100644
--- a/hibernate-core/src/test/java/org/hibernate/orm/test/id/custom/SimpleSequenceGenerator.java
+++ b/hibernate-core/src/test/java/org/hibernate/orm/test/id/custom/SimpleSequenceGenerator.java
@@ -6,6 +6,7 @@
*/
package org.hibernate.orm.test.id.custom;
+import java.lang.reflect.Member;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
@@ -17,11 +18,16 @@ import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.id.IdentifierGenerator;
import org.hibernate.id.factory.spi.CustomIdGeneratorCreationContext;
+/**
+ * An example custom generator.
+ */
public class SimpleSequenceGenerator implements IdentifierGenerator {
+ public static int generationCount = 0;
+
private final Identifier sequenceName;
private final String sqlSelectFrag;
- public SimpleSequenceGenerator(Sequence config, CustomIdGeneratorCreationContext context) {
+ public SimpleSequenceGenerator(Sequence config, Member annotatedMember, CustomIdGeneratorCreationContext context) {
final String name = config.name();
// ignore the other config for now...
@@ -50,6 +56,7 @@ public class SimpleSequenceGenerator implements IdentifierGenerator {
@Override
public Object generate(SharedSessionContractImplementor session, Object object) {
+ generationCount++;
try {
final PreparedStatement st = session.getJdbcCoordinator().getStatementPreparer().prepareStatement( sqlSelectFrag );
try {
diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/id/uuid/annotation/TheEntity.java b/hibernate-core/src/test/java/org/hibernate/orm/test/id/uuid/annotation/TheEntity.java
new file mode 100644
index 0000000000..621ebd2d0c
--- /dev/null
+++ b/hibernate-core/src/test/java/org/hibernate/orm/test/id/uuid/annotation/TheEntity.java
@@ -0,0 +1,46 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * License: GNU Lesser General Public License (LGPL), version 2.1 or later
+ * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
+ */
+package org.hibernate.orm.test.id.uuid.annotation;
+
+import java.util.UUID;
+
+import org.hibernate.annotations.UuidGenerator;
+
+import jakarta.persistence.Entity;
+import jakarta.persistence.Table;
+import jakarta.persistence.Id;
+import jakarta.persistence.Basic;
+
+@Entity(name = "TheEntity")
+@Table(name = "TheEntity")
+public class TheEntity {
+ @Id
+ @UuidGenerator
+ public UUID id;
+ @Basic
+ public String name;
+
+ private TheEntity() {
+ // for Hibernate use
+ }
+
+ public TheEntity(String name) {
+ this.name = name;
+ }
+
+ public UUID getId() {
+ return id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+}
\ No newline at end of file
diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/id/uuid/annotation/UuidGeneratorAnnotationTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/id/uuid/annotation/UuidGeneratorAnnotationTests.java
new file mode 100644
index 0000000000..bd41bee061
--- /dev/null
+++ b/hibernate-core/src/test/java/org/hibernate/orm/test/id/uuid/annotation/UuidGeneratorAnnotationTests.java
@@ -0,0 +1,42 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * License: GNU Lesser General Public License (LGPL), version 2.1 or later
+ * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
+ */
+package org.hibernate.orm.test.id.uuid.annotation;
+
+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.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+@DomainModel( annotatedClasses = TheEntity.class )
+@SessionFactory
+public class UuidGeneratorAnnotationTests {
+ @Test
+ public void verifyModel(DomainModelScope scope) {
+ scope.withHierarchy( TheEntity.class, (descriptor) -> {
+ final Property idProperty = descriptor.getIdentifierProperty();
+ final BasicValue value = (BasicValue) idProperty.getValue();
+
+ assertThat( value.getCustomIdGeneratorCreator() ).isNotNull();
+
+ final String strategy = value.getIdentifierGeneratorStrategy();
+ assertThat( strategy ).isEqualTo( "assigned" );
+ } );
+ }
+
+ @Test
+ public void basicUseTest(SessionFactoryScope scope) {
+ scope.inTransaction( (session) -> {
+ session.persist( new TheEntity( "steve" ) );
+ } );
+ }
+}