From e72118043599e838a2f024bce9829fa59f4ced03 Mon Sep 17 00:00:00 2001 From: Gavin King Date: Fri, 17 May 2024 14:22:29 +0200 Subject: [PATCH] HHH-18136 make it easier to write Generators which delegate to "old" id generation infrastructure Signed-off-by: Gavin King --- .../java/org/hibernate/id/Configurable.java | 22 +++- .../org/hibernate/id/IdentifierGenerator.java | 12 -- .../spi/CustomIdGeneratorCreationContext.java | 4 + .../internal/SessionFactoryImpl.java | 5 +- .../java/org/hibernate/mapping/Component.java | 6 +- .../org/hibernate/mapping/SimpleValue.java | 42 +++++-- .../AbstractCollectionPersister.java | 5 +- .../idgen/userdefined/NativeGenerator.java | 108 ++++++++++++++++++ .../userdefined/NativeGeneratorTest.java | 21 ++++ .../orm/test/idgen/userdefined/NativeId.java | 14 +++ 10 files changed, 207 insertions(+), 32 deletions(-) create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/idgen/userdefined/NativeGenerator.java create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/idgen/userdefined/NativeGeneratorTest.java create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/idgen/userdefined/NativeId.java diff --git a/hibernate-core/src/main/java/org/hibernate/id/Configurable.java b/hibernate-core/src/main/java/org/hibernate/id/Configurable.java index d927d7ffda..16fad4e850 100644 --- a/hibernate-core/src/main/java/org/hibernate/id/Configurable.java +++ b/hibernate-core/src/main/java/org/hibernate/id/Configurable.java @@ -9,6 +9,8 @@ package org.hibernate.id; import java.util.Properties; import org.hibernate.MappingException; +import org.hibernate.boot.model.relational.Database; +import org.hibernate.boot.model.relational.SqlStringGenerationContext; import org.hibernate.service.ServiceRegistry; import org.hibernate.type.Type; @@ -21,12 +23,30 @@ import org.hibernate.type.Type; public interface Configurable { /** * Configure this instance, given the value of parameters - * specified by the user as {@code <param>} elements. + * specified by the user as XML {@code } elements and + * {@link org.hibernate.annotations.Parameter @Parameter} + * annotations. + *

* This method is called just once, following instantiation. + * If this instance also implements {@code ExportableProducer}, + * then this method is always called before + * {@link org.hibernate.boot.model.relational.ExportableProducer#registerExportables(Database)}, * * @param type The id property type descriptor * @param params param values, keyed by parameter name * @param serviceRegistry Access to service that may be needed. */ void configure(Type type, Properties params, ServiceRegistry serviceRegistry) throws MappingException; + + /** + * Initializes this instance, pre-generating SQL if necessary. + *

+ * If this instance also implements {@code ExportableProducer}, + * then this method is always called after + * {@link org.hibernate.boot.model.relational.ExportableProducer#registerExportables(Database)}, + * and before first use. + * + * @param context A context to help generate SQL strings + */ + default void initialize(SqlStringGenerationContext context) {} } diff --git a/hibernate-core/src/main/java/org/hibernate/id/IdentifierGenerator.java b/hibernate-core/src/main/java/org/hibernate/id/IdentifierGenerator.java index d33de987d9..19fb89068b 100644 --- a/hibernate-core/src/main/java/org/hibernate/id/IdentifierGenerator.java +++ b/hibernate-core/src/main/java/org/hibernate/id/IdentifierGenerator.java @@ -113,18 +113,6 @@ public interface IdentifierGenerator extends BeforeExecutionGenerator, Exportabl @Override default void registerExportables(Database database) {} - /** - * Initializes this instance, in particular pre-generates - * SQL as necessary. - *

- * This method is called after - * {@link #registerExportables(Database)}, - * and before first use. - * - * @param context A context to help generate SQL strings - */ - default void initialize(SqlStringGenerationContext context) {} - /** * Generate a new identifier. * diff --git a/hibernate-core/src/main/java/org/hibernate/id/factory/spi/CustomIdGeneratorCreationContext.java b/hibernate-core/src/main/java/org/hibernate/id/factory/spi/CustomIdGeneratorCreationContext.java index 99647dd8f1..f7096fcf75 100644 --- a/hibernate-core/src/main/java/org/hibernate/id/factory/spi/CustomIdGeneratorCreationContext.java +++ b/hibernate-core/src/main/java/org/hibernate/id/factory/spi/CustomIdGeneratorCreationContext.java @@ -15,4 +15,8 @@ import org.hibernate.generator.GeneratorCreationContext; public interface CustomIdGeneratorCreationContext extends GeneratorCreationContext { IdentifierGeneratorFactory getIdentifierGeneratorFactory(); RootClass getRootClass(); + + // we could add these if it helps integrate old infrastructure +// Properties getParameters(); +// SqlStringGenerationContext getSqlStringGenerationContext(); } diff --git a/hibernate-core/src/main/java/org/hibernate/internal/SessionFactoryImpl.java b/hibernate-core/src/main/java/org/hibernate/internal/SessionFactoryImpl.java index 7c4a376195..4420b3e672 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/SessionFactoryImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/SessionFactoryImpl.java @@ -68,6 +68,7 @@ import org.hibernate.engine.transaction.jta.platform.spi.JtaPlatform; import org.hibernate.event.spi.EventEngine; import org.hibernate.generator.Generator; import org.hibernate.graph.spi.RootGraphImplementor; +import org.hibernate.id.Configurable; import org.hibernate.id.IdentifierGenerator; import org.hibernate.id.factory.IdentifierGeneratorFactory; import org.hibernate.integrator.spi.Integrator; @@ -466,8 +467,8 @@ public class SessionFactoryImpl extends QueryParameterBindingTypeResolverImpl im jdbcServices.getJdbcEnvironment().getDialect(), (RootClass) model ); - if ( generator instanceof IdentifierGenerator ) { - final IdentifierGenerator identifierGenerator = (IdentifierGenerator) generator; + if ( generator instanceof Configurable ) { + final Configurable identifierGenerator = (Configurable) generator; identifierGenerator.initialize( sqlStringGenerationContext ); } if ( generator.allowAssignedIdentifiers() ) { diff --git a/hibernate-core/src/main/java/org/hibernate/mapping/Component.java b/hibernate-core/src/main/java/org/hibernate/mapping/Component.java index 409fb3b461..8409b78be5 100644 --- a/hibernate-core/src/main/java/org/hibernate/mapping/Component.java +++ b/hibernate-core/src/main/java/org/hibernate/mapping/Component.java @@ -34,8 +34,8 @@ import org.hibernate.dialect.Dialect; import org.hibernate.engine.spi.Mapping; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.id.CompositeNestedGeneratedValueGenerator; +import org.hibernate.id.Configurable; import org.hibernate.id.IdentifierGenerationException; -import org.hibernate.id.IdentifierGenerator; import org.hibernate.id.factory.IdentifierGeneratorFactory; import org.hibernate.internal.util.ReflectHelper; import org.hibernate.internal.util.collections.ArrayHelper; @@ -784,8 +784,8 @@ public class Component extends SimpleValue implements MetaAttributable, Sortable @Override public void initialize(SqlStringGenerationContext context) { - if ( subgenerator instanceof IdentifierGenerator ) { - ( (IdentifierGenerator) subgenerator).initialize( context ); + if ( subgenerator instanceof Configurable) { + ( (Configurable) subgenerator).initialize( context ); } } } diff --git a/hibernate-core/src/main/java/org/hibernate/mapping/SimpleValue.java b/hibernate-core/src/main/java/org/hibernate/mapping/SimpleValue.java index ddc2abdb99..81088fba0d 100644 --- a/hibernate-core/src/main/java/org/hibernate/mapping/SimpleValue.java +++ b/hibernate-core/src/main/java/org/hibernate/mapping/SimpleValue.java @@ -405,19 +405,20 @@ public abstract class SimpleValue implements KeyValue { IdentifierGeneratorFactory identifierGeneratorFactory, Dialect dialect, RootClass rootClass) throws MappingException { - if ( generator != null ) { - return generator; - } - else if ( customIdGeneratorCreator != null ) { - generator = customIdGeneratorCreator.createGenerator( - new IdGeneratorCreationContext( identifierGeneratorFactory, null, null, rootClass ) - ); - return generator; - } - else { - generator = createLegacyIdentifierGenerator(this, identifierGeneratorFactory, dialect, null, null, rootClass ); - return generator; + getTable().setIdentifierValue( this ); + + if ( generator == null ) { + if ( customIdGeneratorCreator != null ) { + generator = customIdGeneratorCreator.createGenerator( + new IdGeneratorCreationContext( identifierGeneratorFactory, null, null, rootClass ) + ); + } + else { + generator = createLegacyIdentifierGenerator(this, identifierGeneratorFactory, dialect, null, null, rootClass ); + } } + + return generator; } public boolean isUpdateable() { @@ -1134,5 +1135,22 @@ public abstract class SimpleValue implements KeyValue { public Property getProperty() { return rootClass.getIdentifierProperty(); } + + // we could add these if it helps integrate old infrastructure +// @Override +// public Properties getParameters() { +// final Value value = getProperty().getValue(); +// if ( !value.isSimpleValue() ) { +// throw new IllegalStateException( "not a simple-valued property" ); +// } +// final Dialect dialect = getDatabase().getDialect(); +// return collectParameters( (SimpleValue) value, dialect, defaultCatalog, defaultSchema, rootClass ); +// } +// +// @Override +// public SqlStringGenerationContext getSqlStringGenerationContext() { +// final Database database = getDatabase(); +// return fromExplicit( database.getJdbcEnvironment(), database, defaultCatalog, defaultSchema ); +// } } } diff --git a/hibernate-core/src/main/java/org/hibernate/persister/collection/AbstractCollectionPersister.java b/hibernate-core/src/main/java/org/hibernate/persister/collection/AbstractCollectionPersister.java index a12dcb731e..4d27053b14 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/collection/AbstractCollectionPersister.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/collection/AbstractCollectionPersister.java @@ -52,6 +52,7 @@ import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.engine.spi.SubselectFetch; import org.hibernate.generator.BeforeExecutionGenerator; import org.hibernate.generator.Generator; +import org.hibernate.id.Configurable; import org.hibernate.id.IdentifierGenerator; import org.hibernate.internal.FilterAliasGenerator; import org.hibernate.internal.FilterHelper; @@ -605,8 +606,8 @@ public abstract class AbstractCollectionPersister if ( generator.generatedOnExecution() ) { throw new MappingException("must be an BeforeExecutionGenerator"); //TODO fix message } - if ( generator instanceof IdentifierGenerator ) { - ( (IdentifierGenerator) generator ).initialize( context.getSqlStringGenerationContext() ); + if ( generator instanceof Configurable ) { + ( (Configurable) generator ).initialize( context.getSqlStringGenerationContext() ); } return (BeforeExecutionGenerator) generator; } diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/idgen/userdefined/NativeGenerator.java b/hibernate-core/src/test/java/org/hibernate/orm/test/idgen/userdefined/NativeGenerator.java new file mode 100644 index 0000000000..2c0501c9cd --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/idgen/userdefined/NativeGenerator.java @@ -0,0 +1,108 @@ +package org.hibernate.orm.test.idgen.userdefined; + +import org.hibernate.boot.model.relational.Database; +import org.hibernate.boot.model.relational.ExportableProducer; +import org.hibernate.boot.model.relational.SqlStringGenerationContext; +import org.hibernate.dialect.Dialect; +import org.hibernate.engine.spi.SharedSessionContractImplementor; +import org.hibernate.generator.BeforeExecutionGenerator; +import org.hibernate.generator.EventType; +import org.hibernate.generator.Generator; +import org.hibernate.generator.OnExecutionGenerator; +import org.hibernate.id.Configurable; +import org.hibernate.id.PostInsertIdentityPersister; +import org.hibernate.id.factory.IdentifierGeneratorFactory; +import org.hibernate.id.factory.spi.CustomIdGeneratorCreationContext; +import org.hibernate.id.insert.InsertGeneratedIdentifierDelegate; +import org.hibernate.mapping.SimpleValue; +import org.hibernate.service.ServiceRegistry; +import org.hibernate.type.Type; + +import java.lang.reflect.Member; +import java.util.EnumSet; +import java.util.Properties; + +public class NativeGenerator + implements OnExecutionGenerator, BeforeExecutionGenerator, Configurable, ExportableProducer { + + private final IdentifierGeneratorFactory factory; + private final String strategy; + + private Generator generator; + + public NativeGenerator(NativeId nativeId, Member member, CustomIdGeneratorCreationContext creationContext) { + factory = creationContext.getIdentifierGeneratorFactory(); + strategy = creationContext.getDatabase().getDialect().getNativeIdentifierGeneratorStrategy(); + SimpleValue value = (SimpleValue) creationContext.getProperty().getValue(); + value.setIdentifierGeneratorStrategy(strategy); + } + + @Override + public EnumSet getEventTypes() { + return generator.getEventTypes(); + } + + @Override + public boolean generatedOnExecution() { + return generator.generatedOnExecution(); + } + + @Override + public void configure(Type type, Properties parameters, ServiceRegistry serviceRegistry) { + generator = factory.createIdentifierGenerator(strategy, type, parameters); + //TODO: should use this instead of the deprecated method, but see HHH-18135 +// GenerationType generationType; +// switch (strategy) { +// case "identity": +// generationType = GenerationType.IDENTITY; +// break; +// case "sequence": +// generationType = GenerationType.SEQUENCE; +// break; +// default: +// throw new AssertionFailure("unrecognized strategy"); +// } +// generator = +// factory.createIdentifierGenerator( generationType, strategy, strategy, type.getJavaTypeDescriptor(), +// parameters, (a, b) -> null ); + } + + @Override + public void registerExportables(Database database) { + if ( generator instanceof ExportableProducer ) { + ((ExportableProducer) generator).registerExportables(database); + } + } + + @Override + public void initialize(SqlStringGenerationContext context) { + if ( generator instanceof Configurable ) { + ((Configurable) generator).initialize(context); + } + } + + @Override + public Object generate(SharedSessionContractImplementor session, Object owner, Object currentValue, EventType eventType) { + return ((BeforeExecutionGenerator) generator).generate(session, owner, currentValue, eventType); + } + + @Override + public boolean referenceColumnsInSql(Dialect dialect) { + return ((OnExecutionGenerator) generator).referenceColumnsInSql(dialect); + } + + @Override + public boolean writePropertyValue() { + return ((OnExecutionGenerator) generator).writePropertyValue(); + } + + @Override + public String[] getReferencedColumnValues(Dialect dialect) { + return ((OnExecutionGenerator) generator).getReferencedColumnValues(dialect); + } + + @Override + public InsertGeneratedIdentifierDelegate getGeneratedIdentifierDelegate(PostInsertIdentityPersister persister) { + return ((OnExecutionGenerator) generator).getGeneratedIdentifierDelegate(persister); + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/idgen/userdefined/NativeGeneratorTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/idgen/userdefined/NativeGeneratorTest.java new file mode 100644 index 0000000000..79e7819fe9 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/idgen/userdefined/NativeGeneratorTest.java @@ -0,0 +1,21 @@ +package org.hibernate.orm.test.idgen.userdefined; + +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.junit.jupiter.api.Test; + +@SessionFactory +@DomainModel(annotatedClasses = NativeGeneratorTest.NativeEntity.class) +public class NativeGeneratorTest { + @Test void test(SessionFactoryScope scope) { + scope.inTransaction(s -> s.persist(new NativeEntity())); + } + @Entity + public static class NativeEntity { + @Id @NativeId + long id; + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/idgen/userdefined/NativeId.java b/hibernate-core/src/test/java/org/hibernate/orm/test/idgen/userdefined/NativeId.java new file mode 100644 index 0000000000..337a67f27c --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/idgen/userdefined/NativeId.java @@ -0,0 +1,14 @@ +package org.hibernate.orm.test.idgen.userdefined; + +import org.hibernate.annotations.IdGeneratorType; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.FIELD) +@Retention(RetentionPolicy.RUNTIME) +@IdGeneratorType(NativeGenerator.class) +public @interface NativeId { +}