HHH-14497 - Drop legacy id-generator settings;

HHH-14718 - Drop deprecated generator implementations;
HHH-14959 - Drop IdentifierGeneratorFactory as a Service;
HHH-14960 - Add @GeneratorType for better custom generator config
This commit is contained in:
Steve Ebersole 2021-12-08 00:22:52 -06:00
parent ce4f22f400
commit c5ad9e129e
11 changed files with 279 additions and 34 deletions

View File

@ -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;
}

View File

@ -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
);
}
}
}
}

View File

@ -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

View File

@ -36,8 +36,10 @@ import org.hibernate.type.descriptor.java.UUIDJavaTypeDescriptor;
* <li>{@link org.hibernate.id.uuid.CustomVersionOneStrategy}</li>
* </ul>
*
* @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 );

View File

@ -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;
}

View File

@ -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();
}
}

View File

@ -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 ) );
}
}

View File

@ -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 );
}
}

View File

@ -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 {

View File

@ -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;
}
}

View File

@ -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" ) );
} );
}
}