diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/SchemaToolingSettings.java b/hibernate-core/src/main/java/org/hibernate/cfg/SchemaToolingSettings.java index 86f7a9153076..39c52f577131 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/SchemaToolingSettings.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/SchemaToolingSettings.java @@ -391,6 +391,14 @@ public interface SchemaToolingSettings { @Deprecated String HBM2DDL_IMPORT_FILES = "hibernate.hbm2ddl.import_files"; + /** + * Specifies if the default {@code /import.sql} script file should not be executed + * when {@link #HBM2DDL_IMPORT_FILES} is not specified and {@value #HBM2DDL_AUTO} is set to {@code create} or {@code create-drop}. + * + * The default value is {@code false}. + */ + String HBM2DDL_SKIP_DEFAULT_IMPORT_FILE = "hibernate.hbm2ddl.skip_default_import_file"; + /** * Specifies whether to automatically create also the database schema/catalog. * The default is false. diff --git a/hibernate-core/src/main/java/org/hibernate/tool/schema/internal/SchemaCreatorImpl.java b/hibernate-core/src/main/java/org/hibernate/tool/schema/internal/SchemaCreatorImpl.java index 78ad8d519003..9afc9185e961 100644 --- a/hibernate-core/src/main/java/org/hibernate/tool/schema/internal/SchemaCreatorImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/tool/schema/internal/SchemaCreatorImpl.java @@ -60,7 +60,9 @@ import static org.hibernate.cfg.AvailableSettings.HBM2DDL_IMPORT_FILES; import static org.hibernate.cfg.AvailableSettings.HBM2DDL_LOAD_SCRIPT_SOURCE; import static org.hibernate.cfg.AvailableSettings.JAKARTA_HBM2DDL_LOAD_SCRIPT_SOURCE; +import static org.hibernate.cfg.SchemaToolingSettings.HBM2DDL_SKIP_DEFAULT_IMPORT_FILE; import static org.hibernate.internal.util.collections.CollectionHelper.setOfSize; +import static org.hibernate.internal.util.config.ConfigurationHelper.getBoolean; import static org.hibernate.internal.util.config.ConfigurationHelper.getString; import static org.hibernate.tool.schema.internal.Helper.applyScript; import static org.hibernate.tool.schema.internal.Helper.applySqlStrings; @@ -597,11 +599,24 @@ private void applyImportSources( commandExtractor, dialect, formatter, - hasDefaultImportFileScriptBeenExecuted ? "" : DEFAULT_IMPORT_FILE, + hasDefaultImportFileScriptBeenExecuted ? "" : getDefaultImportFile( options ), targets ); } + private String getDefaultImportFile(ExecutionOptions options) { + if ( skipDefaultFileImport( options ) ) { + return ""; + } + else { + return DEFAULT_IMPORT_FILE; + } + } + + private static boolean skipDefaultFileImport(ExecutionOptions options) { + return getBoolean( HBM2DDL_SKIP_DEFAULT_IMPORT_FILE, options.getConfigurationValues(), false ); + } + /** * In principle, we should format the commands in the import script if the * {@code format} parameter is {@code true}, and since it's supposed to be @@ -642,16 +657,19 @@ private boolean applyImportScript( formatter, targets ); - return containsDefaultImportFile( importScriptInput ); + return containsDefaultImportFile( importScriptInput, options ); } else { return false; } } - private boolean containsDefaultImportFile(ScriptSourceInput importScriptInput) { + private boolean containsDefaultImportFile(ScriptSourceInput importScriptInput,ExecutionOptions options ) { + if ( skipDefaultFileImport( options ) ) { + return false; + } final URL defaultImportFileUrl = getClassLoaderService().locateResource( DEFAULT_IMPORT_FILE ); - return defaultImportFileUrl != null && importScriptInput.containsScript(defaultImportFileUrl); + return defaultImportFileUrl != null && importScriptInput.containsScript( defaultImportFileUrl ); } /** diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/tool/schema/SkipDefaultImportFileExecutionTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/tool/schema/SkipDefaultImportFileExecutionTest.java new file mode 100644 index 000000000000..ba384e57e62d --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/tool/schema/SkipDefaultImportFileExecutionTest.java @@ -0,0 +1,243 @@ +/* + * SPDX-License-Identifier: LGPL-2.1-or-later + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.orm.test.tool.schema; + +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import org.hibernate.boot.Metadata; +import org.hibernate.boot.MetadataSources; +import org.hibernate.boot.registry.BootstrapServiceRegistryBuilder; +import org.hibernate.boot.registry.StandardServiceRegistry; +import org.hibernate.boot.registry.classloading.spi.ClassLoaderService; +import org.hibernate.bytecode.enhance.spi.Enhancer; +import org.hibernate.testing.util.ServiceRegistryUtil; +import org.hibernate.tool.schema.SourceType; +import org.hibernate.tool.schema.TargetType; +import org.hibernate.tool.schema.internal.SchemaCreatorImpl; +import org.hibernate.tool.schema.spi.CommandAcceptanceException; +import org.hibernate.tool.schema.spi.ContributableMatcher; +import org.hibernate.tool.schema.spi.ExceptionHandler; +import org.hibernate.tool.schema.spi.ExecutionOptions; +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.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URL; +import java.net.URLClassLoader; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.EnumSet; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; +import static org.hibernate.cfg.SchemaToolingSettings.HBM2DDL_SKIP_DEFAULT_IMPORT_FILE; +import static org.hibernate.tool.schema.internal.SchemaCreatorImpl.DEFAULT_IMPORT_FILE; + +public class SkipDefaultImportFileExecutionTest { + private File defaultImportFile; + private StandardServiceRegistry serviceRegistry; + private static final String COMMAND = "INSERT INTO TEST_ENTITY (id, name) values (1,'name')"; + + + @BeforeEach + public void setUp() throws Exception { + defaultImportFile = createDefaultImportFile( "import.sql" ); + serviceRegistry = ServiceRegistryUtil.serviceRegistryBuilder( + new BootstrapServiceRegistryBuilder().applyClassLoader( + toClassLoader( defaultImportFile.getParentFile() ) ).build() + ).build(); + } + + @AfterEach + public void tearDown() { + serviceRegistry.close(); + if ( defaultImportFile.exists() ) { + try { + Files.delete( defaultImportFile.toPath() ); + } + catch (IOException e) { + throw new RuntimeException( e ); + } + } + } + + @Test + public void testImportScriptIsNotExecutedWhitSkipDefaultImportFileSetting() { + assertThat( serviceRegistry.getService( ClassLoaderService.class ) + .locateResource( DEFAULT_IMPORT_FILE ) ).isNotNull(); + + TargetDescriptorImpl targetDescriptor = TargetDescriptorImpl.INSTANCE; + + createSchema( targetDescriptor ); + + TestScriptTargetOutput targetOutput = (TestScriptTargetOutput) targetDescriptor.getScriptTargetOutput(); + + assertNoInsertCommandsFromDeaultImportFileHasBeenExecuted( targetOutput ); + } + + private static void assertNoInsertCommandsFromDeaultImportFileHasBeenExecuted(TestScriptTargetOutput targetOutput) { + assertThat( targetOutput.getInsertCommands().size() ).isEqualTo( 0 ) + .as( "Despite setting hibernate.hbm2ddl.skip_default_import_file to true the default import.sql file insert command hs been executed" ); + } + + private void createSchema(TargetDescriptorImpl targetDescriptor) { + + final Metadata mappings = buildMappings( serviceRegistry ); + + final SchemaCreatorImpl schemaCreator = new SchemaCreatorImpl( serviceRegistry ); + + final Map<String, Object> options = new HashMap<>(); + options.put( HBM2DDL_SKIP_DEFAULT_IMPORT_FILE, "true" ); + schemaCreator.doCreation( + mappings, + new ExecutionOptionsTestImpl( options ), + ContributableMatcher.ALL, + SourceDescriptorImpl.INSTANCE, + targetDescriptor + ); + } + + private static File createDefaultImportFile(@SuppressWarnings("SameParameterValue") String fileName) + throws Exception { + final Path tmp = Files.createTempDirectory( "default_import" ); + final File file = new File( tmp.toString() + File.separator + fileName ); + + try (final FileWriter myWriter = new FileWriter( file )) { + myWriter.write( COMMAND ); + } + + return file; + } + + private static ClassLoader toClassLoader(File classesDir) { + final URI classesDirUri = classesDir.toURI(); + try { + final URL url = classesDirUri.toURL(); + return new URLClassLoader( new URL[] {url}, Enhancer.class.getClassLoader() ); + } + catch (MalformedURLException e) { + throw new RuntimeException( "Unable to resolve classpath entry to URL : " + classesDir.getAbsolutePath(), + e ); + } + } + + private Metadata buildMappings(StandardServiceRegistry registry) { + return new MetadataSources( registry ) + .addAnnotatedClass( TestEntity.class ) + .buildMetadata(); + } + + @Entity(name = "TestEntity") + @Table(name = "TEST_ENTITY") + public static class TestEntity { + @Id + private long id; + + private String name; + } + + public class ExecutionOptionsTestImpl implements ExecutionOptions, ExceptionHandler { + Map<String, Object> configValues; + + public ExecutionOptionsTestImpl(Map<String, Object> configValues) { + this.configValues = configValues; + } + + @Override + public Map<String, Object> getConfigurationValues() { + return configValues; + } + + @Override + public boolean shouldManageNamespaces() { + return true; + } + + @Override + public ExceptionHandler getExceptionHandler() { + return this; + } + + @Override + public void handleException(CommandAcceptanceException exception) { + throw exception; + } + } + + private static class SourceDescriptorImpl implements SourceDescriptor { + /** + * Singleton access + */ + public static final SourceDescriptorImpl INSTANCE = new SourceDescriptorImpl(); + + @Override + public SourceType getSourceType() { + return SourceType.METADATA; + } + + @Override + public ScriptSourceInput getScriptSourceInput() { + return null; + } + } + + private static class TargetDescriptorImpl implements TargetDescriptor { + /** + * Singleton access + */ + public static final TargetDescriptorImpl INSTANCE = new TargetDescriptorImpl(); + + @Override + public EnumSet<TargetType> getTargetTypes() { + return EnumSet.of( TargetType.SCRIPT ); + } + + @Override + public ScriptTargetOutput getScriptTargetOutput() { + return TestScriptTargetOutput.INSTANCE; + } + } + + private static class TestScriptTargetOutput implements ScriptTargetOutput { + + public static final TestScriptTargetOutput INSTANCE = new TestScriptTargetOutput(); + + List<String> insertCommands = new ArrayList<>(); + + @Override + public void prepare() { + + } + + @Override + public void accept(String command) { + if ( command.toLowerCase().startsWith( "insert" ) ) { + insertCommands.add( command ); + } + } + + @Override + public void release() { + + } + + public List<String> getInsertCommands() { + return insertCommands; + } + } +}