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:
parent
ce4f22f400
commit
c5ad9e129e
|
@ -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;
|
||||
}
|
|
@ -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
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 );
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 ) );
|
||||
}
|
||||
}
|
|
@ -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 );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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" ) );
|
||||
} );
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue