HHH-18136 make it easier to write Generators which delegate to "old" id generation infrastructure

Signed-off-by: Gavin King <gavin@hibernate.org>
This commit is contained in:
Gavin King 2024-05-17 14:22:29 +02:00
parent 030b7946d4
commit e721180435
10 changed files with 207 additions and 32 deletions

View File

@ -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 &lt;param&gt;} elements.
* specified by the user as XML {@code <param>} elements and
* {@link org.hibernate.annotations.Parameter @Parameter}
* annotations.
* <p>
* 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.
* <p>
* 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) {}
}

View File

@ -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.
* <p>
* 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.
*

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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