HHH-15763 - Allow Dialect to specify fallback SchemaManagementTool

This commit is contained in:
Steve Ebersole 2022-11-25 23:37:53 -06:00
parent 07de23d283
commit cff02f6726
5 changed files with 221 additions and 10 deletions

View File

@ -38,6 +38,7 @@ import java.util.TimeZone;
import java.util.UUID; import java.util.UUID;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import org.hibernate.Incubating;
import org.hibernate.LockMode; import org.hibernate.LockMode;
import org.hibernate.LockOptions; import org.hibernate.LockOptions;
import org.hibernate.NotYetImplementedFor6Exception; import org.hibernate.NotYetImplementedFor6Exception;
@ -128,6 +129,7 @@ import org.hibernate.query.sqm.mutation.spi.SqmMultiTableInsertStrategy;
import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy; import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy;
import org.hibernate.query.sqm.sql.SqmTranslatorFactory; import org.hibernate.query.sqm.sql.SqmTranslatorFactory;
import org.hibernate.service.ServiceRegistry; import org.hibernate.service.ServiceRegistry;
import org.hibernate.service.spi.ServiceRegistryImplementor;
import org.hibernate.sql.ForUpdateFragment; import org.hibernate.sql.ForUpdateFragment;
import org.hibernate.sql.ast.SqlAstNodeRenderingMode; import org.hibernate.sql.ast.SqlAstNodeRenderingMode;
import org.hibernate.sql.ast.SqlAstTranslatorFactory; import org.hibernate.sql.ast.SqlAstTranslatorFactory;
@ -137,6 +139,7 @@ import org.hibernate.sql.ast.spi.StringBuilderSqlAppender;
import org.hibernate.tool.schema.extract.internal.SequenceInformationExtractorLegacyImpl; import org.hibernate.tool.schema.extract.internal.SequenceInformationExtractorLegacyImpl;
import org.hibernate.tool.schema.extract.internal.SequenceInformationExtractorNoOpImpl; import org.hibernate.tool.schema.extract.internal.SequenceInformationExtractorNoOpImpl;
import org.hibernate.tool.schema.extract.spi.SequenceInformationExtractor; import org.hibernate.tool.schema.extract.spi.SequenceInformationExtractor;
import org.hibernate.tool.schema.internal.HibernateSchemaManagementTool;
import org.hibernate.tool.schema.internal.StandardAuxiliaryDatabaseObjectExporter; import org.hibernate.tool.schema.internal.StandardAuxiliaryDatabaseObjectExporter;
import org.hibernate.tool.schema.internal.StandardForeignKeyExporter; import org.hibernate.tool.schema.internal.StandardForeignKeyExporter;
import org.hibernate.tool.schema.internal.StandardIndexExporter; import org.hibernate.tool.schema.internal.StandardIndexExporter;
@ -146,6 +149,7 @@ import org.hibernate.tool.schema.internal.StandardTableExporter;
import org.hibernate.tool.schema.internal.StandardUniqueKeyExporter; import org.hibernate.tool.schema.internal.StandardUniqueKeyExporter;
import org.hibernate.tool.schema.spi.Cleaner; import org.hibernate.tool.schema.spi.Cleaner;
import org.hibernate.tool.schema.spi.Exporter; import org.hibernate.tool.schema.spi.Exporter;
import org.hibernate.tool.schema.spi.SchemaManagementTool;
import org.hibernate.type.BasicType; import org.hibernate.type.BasicType;
import org.hibernate.type.BasicTypeRegistry; import org.hibernate.type.BasicTypeRegistry;
import org.hibernate.type.SqlTypes; import org.hibernate.type.SqlTypes;
@ -4378,4 +4382,16 @@ public abstract class Dialect implements ConversionContext {
public TimeZoneSupport getTimeZoneSupport() { public TimeZoneSupport getTimeZoneSupport() {
return TimeZoneSupport.NONE; return TimeZoneSupport.NONE;
} }
/**
* The SchemaManagementTool to use if none explicitly specified.
* <p/>
* Allows Dialects to override how schema tooling works by default
*/
@Incubating
public SchemaManagementTool getFallbackSchemaManagementTool(
Map<String, Object> configurationValues,
ServiceRegistryImplementor registry) {
return new HibernateSchemaManagementTool();
}
} }

View File

@ -58,11 +58,11 @@ import static org.hibernate.cfg.AvailableSettings.DIALECT_DB_NAME;
import static org.hibernate.cfg.AvailableSettings.DIALECT_DB_VERSION; import static org.hibernate.cfg.AvailableSettings.DIALECT_DB_VERSION;
import static org.hibernate.cfg.AvailableSettings.HBM2DDL_CONNECTION; import static org.hibernate.cfg.AvailableSettings.HBM2DDL_CONNECTION;
import static org.hibernate.cfg.AvailableSettings.HBM2DDL_DELIMITER; import static org.hibernate.cfg.AvailableSettings.HBM2DDL_DELIMITER;
import static org.hibernate.cfg.AvailableSettings.JAKARTA_HBM2DDL_CONNECTION;
import static org.hibernate.cfg.AvailableSettings.JAKARTA_HBM2DDL_DB_MAJOR_VERSION; import static org.hibernate.cfg.AvailableSettings.JAKARTA_HBM2DDL_DB_MAJOR_VERSION;
import static org.hibernate.cfg.AvailableSettings.JAKARTA_HBM2DDL_DB_MINOR_VERSION; import static org.hibernate.cfg.AvailableSettings.JAKARTA_HBM2DDL_DB_MINOR_VERSION;
import static org.hibernate.cfg.AvailableSettings.JAKARTA_HBM2DDL_DB_VERSION;
import static org.hibernate.cfg.AvailableSettings.JAKARTA_HBM2DDL_CONNECTION;
import static org.hibernate.cfg.AvailableSettings.JAKARTA_HBM2DDL_DB_NAME; import static org.hibernate.cfg.AvailableSettings.JAKARTA_HBM2DDL_DB_NAME;
import static org.hibernate.cfg.AvailableSettings.JAKARTA_HBM2DDL_DB_VERSION;
import static org.hibernate.internal.log.DeprecationLogger.DEPRECATION_LOGGER; import static org.hibernate.internal.log.DeprecationLogger.DEPRECATION_LOGGER;
import static org.hibernate.internal.util.NullnessHelper.coalesceSuppliedValues; import static org.hibernate.internal.util.NullnessHelper.coalesceSuppliedValues;
@ -77,6 +77,9 @@ public class HibernateSchemaManagementTool implements SchemaManagementTool, Serv
private ServiceRegistry serviceRegistry; private ServiceRegistry serviceRegistry;
private GenerationTarget customTarget; private GenerationTarget customTarget;
public HibernateSchemaManagementTool() {
}
@Override @Override
public void injectServices(ServiceRegistryImplementor serviceRegistry) { public void injectServices(ServiceRegistryImplementor serviceRegistry) {
this.serviceRegistry = serviceRegistry; this.serviceRegistry = serviceRegistry;
@ -146,10 +149,11 @@ public class HibernateSchemaManagementTool implements SchemaManagementTool, Serv
return customTarget; return customTarget;
} }
GenerationTarget[] buildGenerationTargets( @Override
public GenerationTarget[] buildGenerationTargets(
TargetDescriptor targetDescriptor, TargetDescriptor targetDescriptor,
JdbcContext jdbcContext, JdbcContext jdbcContext,
Map<String,Object> options, Map<String, Object> options,
boolean needsAutoCommit) { boolean needsAutoCommit) {
final String scriptDelimiter = ConfigurationHelper.getString( HBM2DDL_DELIMITER, options, ";" ); final String scriptDelimiter = ConfigurationHelper.getString( HBM2DDL_DELIMITER, options, ";" );
@ -158,7 +162,7 @@ public class HibernateSchemaManagementTool implements SchemaManagementTool, Serv
int index = 0; int index = 0;
if ( targetDescriptor.getTargetTypes().contains( TargetType.STDOUT ) ) { if ( targetDescriptor.getTargetTypes().contains( TargetType.STDOUT ) ) {
targets[index] = new GenerationTargetToStdout( scriptDelimiter ); targets[index] = buildStdoutTarget( scriptDelimiter );
index++; index++;
} }
@ -166,13 +170,13 @@ public class HibernateSchemaManagementTool implements SchemaManagementTool, Serv
if ( targetDescriptor.getScriptTargetOutput() == null ) { if ( targetDescriptor.getScriptTargetOutput() == null ) {
throw new SchemaManagementException( "Writing to script was requested, but no script file was specified" ); throw new SchemaManagementException( "Writing to script was requested, but no script file was specified" );
} }
targets[index] = new GenerationTargetToScript( targetDescriptor.getScriptTargetOutput(), scriptDelimiter ); targets[index] = buildScriptTarget( targetDescriptor, scriptDelimiter );
index++; index++;
} }
if ( targetDescriptor.getTargetTypes().contains( TargetType.DATABASE ) ) { if ( targetDescriptor.getTargetTypes().contains( TargetType.DATABASE ) ) {
targets[index] = customTarget == null targets[index] = customTarget == null
? new GenerationTargetToDatabase( getDdlTransactionIsolator( jdbcContext ), true, needsAutoCommit ) ? buildDatabaseTarget( jdbcContext, needsAutoCommit )
: customTarget; : customTarget;
index++; index++;
} }
@ -180,6 +184,18 @@ public class HibernateSchemaManagementTool implements SchemaManagementTool, Serv
return targets; return targets;
} }
protected GenerationTarget buildStdoutTarget(String scriptDelimiter) {
return new GenerationTargetToStdout( scriptDelimiter );
}
protected GenerationTarget buildScriptTarget(TargetDescriptor targetDescriptor, String scriptDelimiter) {
return new GenerationTargetToScript( targetDescriptor.getScriptTargetOutput(), scriptDelimiter );
}
protected GenerationTarget buildDatabaseTarget(JdbcContext jdbcContext, boolean needsAutoCommit) {
return new GenerationTargetToDatabase( getDdlTransactionIsolator( jdbcContext ), true, needsAutoCommit );
}
GenerationTarget[] buildGenerationTargets( GenerationTarget[] buildGenerationTargets(
TargetDescriptor targetDescriptor, TargetDescriptor targetDescriptor,
DdlTransactionIsolator ddlTransactionIsolator, DdlTransactionIsolator ddlTransactionIsolator,
@ -191,7 +207,7 @@ public class HibernateSchemaManagementTool implements SchemaManagementTool, Serv
int index = 0; int index = 0;
if ( targetDescriptor.getTargetTypes().contains( TargetType.STDOUT ) ) { if ( targetDescriptor.getTargetTypes().contains( TargetType.STDOUT ) ) {
targets[index] = new GenerationTargetToStdout( scriptDelimiter ); targets[index] = buildStdoutTarget( scriptDelimiter );
index++; index++;
} }
@ -199,7 +215,7 @@ public class HibernateSchemaManagementTool implements SchemaManagementTool, Serv
if ( targetDescriptor.getScriptTargetOutput() == null ) { if ( targetDescriptor.getScriptTargetOutput() == null ) {
throw new SchemaManagementException( "Writing to script was requested, but no script file was specified" ); throw new SchemaManagementException( "Writing to script was requested, but no script file was specified" );
} }
targets[index] = new GenerationTargetToScript( targetDescriptor.getScriptTargetOutput(), scriptDelimiter ); targets[index] = buildScriptTarget( targetDescriptor, scriptDelimiter );
index++; index++;
} }

View File

@ -11,6 +11,7 @@ import java.util.Map;
import org.hibernate.boot.registry.StandardServiceInitiator; import org.hibernate.boot.registry.StandardServiceInitiator;
import org.hibernate.boot.registry.selector.spi.StrategySelector; import org.hibernate.boot.registry.selector.spi.StrategySelector;
import org.hibernate.cfg.AvailableSettings; import org.hibernate.cfg.AvailableSettings;
import org.hibernate.engine.jdbc.spi.JdbcServices;
import org.hibernate.service.spi.ServiceRegistryImplementor; import org.hibernate.service.spi.ServiceRegistryImplementor;
import org.hibernate.tool.schema.spi.SchemaManagementTool; import org.hibernate.tool.schema.spi.SchemaManagementTool;
@ -24,7 +25,9 @@ public class SchemaManagementToolInitiator implements StandardServiceInitiator<S
final Object setting = configurationValues.get( AvailableSettings.SCHEMA_MANAGEMENT_TOOL ); final Object setting = configurationValues.get( AvailableSettings.SCHEMA_MANAGEMENT_TOOL );
SchemaManagementTool tool = registry.getService( StrategySelector.class ).resolveStrategy( SchemaManagementTool.class, setting ); SchemaManagementTool tool = registry.getService( StrategySelector.class ).resolveStrategy( SchemaManagementTool.class, setting );
if ( tool == null ) { if ( tool == null ) {
tool = new HibernateSchemaManagementTool(); tool = registry.getService( JdbcServices.class )
.getDialect()
.getFallbackSchemaManagementTool( configurationValues, registry );
} }
return tool; return tool;

View File

@ -11,6 +11,7 @@ import java.util.Map;
import org.hibernate.Incubating; import org.hibernate.Incubating;
import org.hibernate.service.Service; import org.hibernate.service.Service;
import org.hibernate.tool.schema.internal.exec.GenerationTarget; import org.hibernate.tool.schema.internal.exec.GenerationTarget;
import org.hibernate.tool.schema.internal.exec.JdbcContext;
/** /**
* Contract for schema management tool integration. * Contract for schema management tool integration.
@ -35,4 +36,14 @@ public interface SchemaManagementTool extends Service {
void setCustomDatabaseGenerationTarget(GenerationTarget generationTarget); void setCustomDatabaseGenerationTarget(GenerationTarget generationTarget);
ExtractionTool getExtractionTool(); ExtractionTool getExtractionTool();
/**
* Resolves the {@linkplain GenerationTarget targets} to which to
* send the DDL commands based on configuration
*/
GenerationTarget[] buildGenerationTargets(
TargetDescriptor targetDescriptor,
JdbcContext jdbcContext,
Map<String, Object> options,
boolean needsAutoCommit);
} }

View File

@ -0,0 +1,165 @@
/*
* 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.schematools;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
import java.util.Map;
import org.hibernate.boot.Metadata;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.registry.StandardServiceRegistry;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.dialect.Dialect;
import org.hibernate.engine.config.spi.ConfigurationService;
import org.hibernate.orm.test.tool.schema.ExecutionOptionsTestImpl;
import org.hibernate.service.spi.ServiceRegistryImplementor;
import org.hibernate.tool.schema.SourceType;
import org.hibernate.tool.schema.TargetType;
import org.hibernate.tool.schema.internal.HibernateSchemaManagementTool;
import org.hibernate.tool.schema.internal.exec.GenerationTarget;
import org.hibernate.tool.schema.internal.exec.JdbcContext;
import org.hibernate.tool.schema.spi.SchemaCreator;
import org.hibernate.tool.schema.spi.SchemaManagementTool;
import org.hibernate.tool.schema.spi.ScriptSourceInput;
import org.hibernate.tool.schema.spi.ScriptTargetOutput;
import org.hibernate.tool.schema.spi.SourceDescriptor;
import org.hibernate.tool.schema.spi.TargetDescriptor;
import org.hibernate.testing.orm.junit.ServiceRegistryScope;
import org.junit.jupiter.api.Test;
import jakarta.persistence.Basic;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link org.hibernate.dialect.Dialect#getFallbackSchemaManagementTool}
*
* @author Steve Ebersole
*/
public class FallbackSchemaManagementToolTests {
@Test
public void testFallbackToolIsPickedUp() {
ServiceRegistryScope.using(
() -> {
return new StandardServiceRegistryBuilder()
.applySetting( AvailableSettings.DIALECT, CustomDialect.class.getName() )
.build();
},
(registryScope) -> {
final StandardServiceRegistry registry = registryScope.getRegistry();
final Metadata metadata = new MetadataSources( registry )
.addAnnotatedClass( SimpleEntity.class )
.buildMetadata();
final HibernateSchemaManagementTool tool = (HibernateSchemaManagementTool) registry.getService( SchemaManagementTool.class );
final Map<String, Object> settings = registry.getService( ConfigurationService.class ).getSettings();
final SchemaCreator schemaCreator = tool.getSchemaCreator( settings );
schemaCreator.doCreation(
metadata,
new ExecutionOptionsTestImpl(),
contributed -> true,
new SourceDescriptor() {
@Override
public SourceType getSourceType() {
return SourceType.METADATA;
}
@Override
public ScriptSourceInput getScriptSourceInput() {
return null;
}
},
new TargetDescriptor() {
@Override
public EnumSet<TargetType> getTargetTypes() {
return EnumSet.of( TargetType.DATABASE );
}
@Override
public ScriptTargetOutput getScriptTargetOutput() {
return null;
}
}
);
assertThat( CollectingGenerationTarget.commands ).hasSize( 1 );
}
);
}
private static class CollectingGenerationTarget implements GenerationTarget {
public static final List<String> commands = new ArrayList<>();
public CollectingGenerationTarget(JdbcContext jdbcContext, boolean needsAutoCommit) {
}
@Override
public void prepare() {
commands.clear();
}
@Override
public void accept(String command) {
commands.add( command );
}
@Override
public void release() {
}
}
public static class SchemaManagementToolImpl extends HibernateSchemaManagementTool {
@Override
protected GenerationTarget buildDatabaseTarget(JdbcContext jdbcContext, boolean needsAutoCommit) {
return new CollectingGenerationTarget( jdbcContext, needsAutoCommit );
}
}
public static class CustomDialect extends Dialect {
@Override
public SchemaManagementTool getFallbackSchemaManagementTool(Map<String, Object> configurationValues, ServiceRegistryImplementor registry) {
return new SchemaManagementToolImpl();
}
}
@Entity( name = "SimpleEntity" )
@Table( name = "SimpleEntity" )
public static class SimpleEntity {
@Id
private Integer id;
@Basic
private String name;
private SimpleEntity() {
// for use by Hibernate
}
public SimpleEntity(Integer id, String name) {
this.id = id;
this.name = name;
}
public Integer getId() {
return id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
}