diff --git a/hibernate-core/src/main/java/org/hibernate/query/sql/internal/NamedNativeQueryMementoImpl.java b/hibernate-core/src/main/java/org/hibernate/query/sql/internal/NamedNativeQueryMementoImpl.java index e3db8e30d8..66e9a2559d 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sql/internal/NamedNativeQueryMementoImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sql/internal/NamedNativeQueryMementoImpl.java @@ -18,7 +18,7 @@ import org.hibernate.query.sql.spi.NamedNativeQueryMemento; import org.hibernate.query.sql.spi.NativeQueryImplementor; /** - * Keeps details of a named native-sql query + * Keeps details of a named native SQL query * * @author Max Andersen * @author Steve Ebersole diff --git a/hibernate-core/src/main/java/org/hibernate/query/sql/spi/NamedNativeQueryMemento.java b/hibernate-core/src/main/java/org/hibernate/query/sql/spi/NamedNativeQueryMemento.java index 6a32c81f51..b7b08c9c31 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sql/spi/NamedNativeQueryMemento.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sql/spi/NamedNativeQueryMemento.java @@ -20,7 +20,7 @@ import org.hibernate.query.spi.QueryImplementor; import org.hibernate.query.sql.internal.NamedNativeQueryMementoImpl; /** - * Descriptor for a named native query in the run-time environment + * Descriptor for a named native query in the runtime environment * * @author Steve Ebersole */ diff --git a/hibernate-core/src/main/java/org/hibernate/relational/SchemaManager.java b/hibernate-core/src/main/java/org/hibernate/relational/SchemaManager.java index 5ade9bf6b9..f72aa48b76 100644 --- a/hibernate-core/src/main/java/org/hibernate/relational/SchemaManager.java +++ b/hibernate-core/src/main/java/org/hibernate/relational/SchemaManager.java @@ -51,7 +51,10 @@ public interface SchemaManager { void validateMappedObjects(); /** - * Truncate the database tables mapped by Hibernate entities. + * Truncate the database tables mapped by Hibernate entities, and + * then re-import initial data from any configured + * {@linkplain org.hibernate.cfg.AvailableSettings#JAKARTA_HBM2DDL_LOAD_SCRIPT_SOURCE + * load script}. *

* Programmatic way to run {@link org.hibernate.tool.schema.spi.SchemaTruncator}. */ diff --git a/hibernate-core/src/main/java/org/hibernate/tool/schema/internal/SchemaTruncatorImpl.java b/hibernate-core/src/main/java/org/hibernate/tool/schema/internal/SchemaTruncatorImpl.java index 6cf7ce57b2..b70f6cead5 100644 --- a/hibernate-core/src/main/java/org/hibernate/tool/schema/internal/SchemaTruncatorImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/tool/schema/internal/SchemaTruncatorImpl.java @@ -12,28 +12,42 @@ import org.hibernate.boot.model.relational.Exportable; import org.hibernate.boot.model.relational.Namespace; import org.hibernate.boot.model.relational.SqlStringGenerationContext; import org.hibernate.boot.model.relational.internal.SqlStringGenerationContextImpl; +import org.hibernate.boot.registry.classloading.spi.ClassLoaderService; +import org.hibernate.cfg.AvailableSettings; import org.hibernate.dialect.Dialect; import org.hibernate.engine.jdbc.internal.FormatStyle; import org.hibernate.engine.jdbc.internal.Formatter; import org.hibernate.internal.util.StringHelper; import org.hibernate.internal.util.collections.CollectionHelper; +import org.hibernate.internal.util.config.ConfigurationHelper; import org.hibernate.mapping.ForeignKey; import org.hibernate.mapping.Table; +import org.hibernate.service.ServiceRegistry; import org.hibernate.tool.schema.internal.exec.GenerationTarget; import org.hibernate.tool.schema.internal.exec.JdbcContext; +import org.hibernate.tool.schema.internal.exec.ScriptSourceInputFromUrl; +import org.hibernate.tool.schema.internal.exec.ScriptSourceInputNonExistentImpl; import org.hibernate.tool.schema.spi.CommandAcceptanceException; import org.hibernate.tool.schema.spi.ContributableMatcher; import org.hibernate.tool.schema.spi.ExecutionOptions; import org.hibernate.tool.schema.spi.SchemaFilter; import org.hibernate.tool.schema.spi.SchemaManagementException; import org.hibernate.tool.schema.spi.SchemaTruncator; +import org.hibernate.tool.schema.spi.ScriptSourceInput; +import org.hibernate.tool.schema.spi.SqlScriptCommandExtractor; import org.hibernate.tool.schema.spi.TargetDescriptor; import org.jboss.logging.Logger; +import java.net.URL; import java.util.ArrayList; import java.util.List; import java.util.Set; +import static org.hibernate.cfg.AvailableSettings.HBM2DDL_CHARSET_NAME; +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.tool.schema.internal.Helper.interpretScriptSourceSetting; + /** * @author Gavin King */ @@ -159,6 +173,10 @@ public class SchemaTruncatorImpl implements SchemaTruncator { enableConstraints( namespace, metadata, formatter, options, sqlStringGenerationContext, contributableInclusionFilter, targets ); } + + final SqlScriptCommandExtractor commandExtractor = tool.getServiceRegistry().getService( SqlScriptCommandExtractor.class ); + final boolean format = Helper.interpretFormattingEnabled( options.getConfigurationValues() ); + applyImportSources( options, commandExtractor, format, dialect, targets ); } private void disableConstraints( @@ -294,4 +312,77 @@ public class SchemaTruncatorImpl implements SchemaTruncator { } } } + + //Woooooo, massive copy/paste from SchemaCreatorImpl! + + private void applyImportSources( + ExecutionOptions options, + SqlScriptCommandExtractor commandExtractor, + boolean format, + Dialect dialect, + GenerationTarget... targets) { + final ServiceRegistry serviceRegistry = tool.getServiceRegistry(); + final ClassLoaderService classLoaderService = serviceRegistry.getService( ClassLoaderService.class ); + + // I have had problems applying the formatter to these imported statements. + // and legacy SchemaExport did not format them, so doing same here + //final Formatter formatter = format ? DDLFormatterImpl.INSTANCE : FormatStyle.NONE.getFormatter(); + final Formatter formatter = FormatStyle.NONE.getFormatter(); + + Object importScriptSetting = options.getConfigurationValues().get( HBM2DDL_LOAD_SCRIPT_SOURCE ); + if ( importScriptSetting == null ) { + importScriptSetting = options.getConfigurationValues().get( JAKARTA_HBM2DDL_LOAD_SCRIPT_SOURCE ); + } + String charsetName = (String) options.getConfigurationValues().get( HBM2DDL_CHARSET_NAME ); + + if ( importScriptSetting != null ) { + final ScriptSourceInput importScriptInput = interpretScriptSourceSetting( importScriptSetting, classLoaderService, charsetName ); + final List commands = importScriptInput.extract( + reader -> commandExtractor.extractCommands( reader, dialect ) + ); + for ( int i = 0; i < commands.size(); i++ ) { + applySqlString( commands.get( i ), formatter, options, targets ); + } + } + + final String importFiles = ConfigurationHelper.getString( + AvailableSettings.HBM2DDL_IMPORT_FILES, + options.getConfigurationValues(), + SchemaCreatorImpl.DEFAULT_IMPORT_FILE + ); + + for ( String currentFile : importFiles.split( "," ) ) { + final String resourceName = currentFile.trim(); + if ( resourceName.isEmpty() ) { + //skip empty resource names + continue; + } + final ScriptSourceInput importScriptInput = interpretLegacyImportScriptSetting( resourceName, classLoaderService, charsetName ); + final List commands = importScriptInput.extract( + reader -> commandExtractor.extractCommands( reader, dialect ) + ); + for ( int i = 0; i < commands.size(); i++ ) { + applySqlString( commands.get( i ), formatter, options, targets ); + } + } + } + + private ScriptSourceInput interpretLegacyImportScriptSetting( + String resourceName, + ClassLoaderService classLoaderService, + String charsetName) { + try { + final URL resourceUrl = classLoaderService.locateResource( resourceName ); + if ( resourceUrl == null ) { + return ScriptSourceInputNonExistentImpl.INSTANCE; + } + else { + return new ScriptSourceInputFromUrl( resourceUrl, charsetName ); + } + } + catch (Exception e) { + throw new SchemaManagementException( "Error resolving legacy import resource : " + resourceName, e ); + } + } + } diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/catalog/SchemaManagerOracleTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/schemamanager/SchemaManagerDefaultSchemaTest.java similarity index 85% rename from hibernate-core/src/test/java/org/hibernate/orm/test/catalog/SchemaManagerOracleTest.java rename to hibernate-core/src/test/java/org/hibernate/orm/test/schemamanager/SchemaManagerDefaultSchemaTest.java index a7cacfe473..2cc1a446f1 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/catalog/SchemaManagerOracleTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/schemamanager/SchemaManagerDefaultSchemaTest.java @@ -4,13 +4,12 @@ * License: GNU Lesser General Public License (LGPL), version 2.1 or later. * See the lgpl.txt file in the root directory or . */ -package org.hibernate.orm.test.catalog; +package org.hibernate.orm.test.schemamanager; import jakarta.persistence.CascadeType; import jakarta.persistence.Entity; import jakarta.persistence.Id; import jakarta.persistence.ManyToOne; -import org.hibernate.dialect.PostgreSQLDialect; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.testing.orm.junit.DialectFeatureChecks; @@ -18,7 +17,6 @@ import org.hibernate.testing.orm.junit.DomainModel; import org.hibernate.testing.orm.junit.RequiresDialectFeature; import org.hibernate.testing.orm.junit.SessionFactory; import org.hibernate.testing.orm.junit.SessionFactoryScope; -import org.hibernate.testing.orm.junit.SkipForDialect; import org.hibernate.tool.schema.spi.SchemaManagementException; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -30,11 +28,11 @@ import static org.junit.jupiter.api.Assertions.fail; /** * @author Gavin King */ -@DomainModel(annotatedClasses = {SchemaManagerOracleTest.Book.class, SchemaManagerOracleTest.Author.class}) +@DomainModel(annotatedClasses = {SchemaManagerDefaultSchemaTest.Book.class, SchemaManagerDefaultSchemaTest.Author.class}) @SessionFactory(exportSchema = false) -@SkipForDialect(dialectClass = PostgreSQLDialect.class, reason = "doesn't work in the CI") +//@SkipForDialect(dialectClass = PostgreSQLDialect.class, reason = "doesn't work in the CI") @RequiresDialectFeature(feature= DialectFeatureChecks.SupportsTruncateTable.class) -public class SchemaManagerOracleTest { +public class SchemaManagerDefaultSchemaTest { @BeforeEach public void clean(SessionFactoryScope scope) { @@ -45,7 +43,7 @@ public class SchemaManagerOracleTest { return s.createQuery("select count(*) from BookForTesting", Long.class).getSingleResult(); } - @Test public void test0(SessionFactoryScope scope) { + @Test public void testExportValidateTruncateDrop(SessionFactoryScope scope) { SessionFactoryImplementor factory = scope.getSessionFactory(); factory.getSchemaManager().exportMappedObjects(true); scope.inTransaction( s -> s.persist( new Book() ) ); diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/catalog/SchemaManagerTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/schemamanager/SchemaManagerExplicitSchemaTest.java similarity index 85% rename from hibernate-core/src/test/java/org/hibernate/orm/test/catalog/SchemaManagerTest.java rename to hibernate-core/src/test/java/org/hibernate/orm/test/schemamanager/SchemaManagerExplicitSchemaTest.java index 932dc4f721..9564e92077 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/catalog/SchemaManagerTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/schemamanager/SchemaManagerExplicitSchemaTest.java @@ -4,13 +4,12 @@ * License: GNU Lesser General Public License (LGPL), version 2.1 or later. * See the lgpl.txt file in the root directory or . */ -package org.hibernate.orm.test.catalog; +package org.hibernate.orm.test.schemamanager; import jakarta.persistence.CascadeType; import jakarta.persistence.Entity; import jakarta.persistence.Id; import jakarta.persistence.ManyToOne; -import org.hibernate.cfg.AvailableSettings; import org.hibernate.dialect.OracleDialect; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionImplementor; @@ -26,6 +25,7 @@ import org.hibernate.tool.schema.spi.SchemaManagementException; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import static org.hibernate.cfg.AvailableSettings.DEFAULT_SCHEMA; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; @@ -33,12 +33,12 @@ import static org.junit.jupiter.api.Assertions.fail; /** * @author Gavin King */ -@DomainModel(annotatedClasses = {SchemaManagerTest.Book.class, SchemaManagerTest.Author.class}) +@DomainModel(annotatedClasses = {SchemaManagerExplicitSchemaTest.Book.class, SchemaManagerExplicitSchemaTest.Author.class}) @SessionFactory(exportSchema = false) -@ServiceRegistry(settings = @Setting(name = AvailableSettings.DEFAULT_SCHEMA, value = "schema_manager_test")) +@ServiceRegistry(settings = @Setting(name = DEFAULT_SCHEMA, value = "schema_manager_test")) @SkipForDialect(dialectClass = OracleDialect.class, reason = "Oracle tests run in the DBO schema") @RequiresDialectFeature(feature= DialectFeatureChecks.SupportsTruncateTable.class) -public class SchemaManagerTest { +public class SchemaManagerExplicitSchemaTest { @BeforeEach public void clean(SessionFactoryScope scope) { @@ -49,7 +49,7 @@ public class SchemaManagerTest { return s.createQuery("select count(*) from BookForTesting", Long.class).getSingleResult(); } - @Test public void test0(SessionFactoryScope scope) { + @Test public void testExportValidateTruncateDrop(SessionFactoryScope scope) { SessionFactoryImplementor factory = scope.getSessionFactory(); factory.getSchemaManager().exportMappedObjects(true); scope.inTransaction( s -> s.persist( new Book() ) ); @@ -69,7 +69,7 @@ public class SchemaManagerTest { @Entity(name="BookForTesting") static class Book { - @Id String isbn = "xyz123"; + @Id String isbn = "9781932394153"; String title = "Hibernate in Action"; @ManyToOne(cascade = CascadeType.PERSIST) Author author = new Author(); diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/schemamanager/SchemaManagerLoadScriptTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/schemamanager/SchemaManagerLoadScriptTest.java new file mode 100644 index 0000000000..e96776ee11 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/schemamanager/SchemaManagerLoadScriptTest.java @@ -0,0 +1,81 @@ +/* + * 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 . + */ +package org.hibernate.orm.test.schemamanager; + +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.JoinTable; +import jakarta.persistence.ManyToMany; +import jakarta.persistence.Table; +import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.engine.spi.SessionImplementor; +import org.hibernate.testing.orm.junit.DialectFeatureChecks; +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.RequiresDialectFeature; +import org.hibernate.testing.orm.junit.ServiceRegistry; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.hibernate.testing.orm.junit.Setting; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.util.Set; + +import static org.hibernate.cfg.AvailableSettings.JAKARTA_HBM2DDL_LOAD_SCRIPT_SOURCE; +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * @author Gavin King + */ +@DomainModel(annotatedClasses = {SchemaManagerLoadScriptTest.Book.class, SchemaManagerLoadScriptTest.Author.class}) +@SessionFactory(exportSchema = false) +@ServiceRegistry(settings = @Setting(name = JAKARTA_HBM2DDL_LOAD_SCRIPT_SOURCE, + value = "org/hibernate/orm/test/schemamanager/data.sql")) +@RequiresDialectFeature(feature= DialectFeatureChecks.SupportsTruncateTable.class) +public class SchemaManagerLoadScriptTest { + + @BeforeEach + public void clean(SessionFactoryScope scope) { + scope.getSessionFactory().getSchemaManager().dropMappedObjects(true); + } + + private Long countBooks(SessionImplementor s) { + return s.createQuery("select count(*) from Book", Long.class).getSingleResult(); + } + + private Long countAuthors(SessionImplementor s) { + return s.createQuery("select count(*) from Author", Long.class).getSingleResult(); + } + + @Test + public void testExportValidateTruncateDrop(SessionFactoryScope scope) { + SessionFactoryImplementor factory = scope.getSessionFactory(); + factory.getSchemaManager().exportMappedObjects(true); + factory.getSchemaManager().validateMappedObjects(); + Author author = new Author(); author.name = "Steve Ebersole"; + scope.inTransaction( s -> s.persist(author) ); + scope.inTransaction( s -> assertEquals( 1, countBooks(s) ) ); + scope.inTransaction( s -> assertEquals( 3, countAuthors(s) ) ); + factory.getSchemaManager().truncateMappedObjects(); + scope.inTransaction( s -> assertEquals( 2, countAuthors(s) ) ); + factory.getSchemaManager().dropMappedObjects(true); + } + + @Entity(name="Book") @Table(name="Books") + static class Book { + @Id + String isbn; + String title; + } + + @Entity(name="Author") @Table(name="Authors") + static class Author { + @Id String name; + @ManyToMany @JoinTable(name = "BooksByAuthor") + public Set books; + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/schemamanager/data.sql b/hibernate-core/src/test/java/org/hibernate/orm/test/schemamanager/data.sql new file mode 100644 index 0000000000..f59cdd1e73 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/schemamanager/data.sql @@ -0,0 +1,5 @@ +insert into Books (isbn, title) values ('9781932394887','Java Persistence with Hibernate') +insert into Authors (name) values ('Gavin King') +insert into Authors (name) values ('Christian Bauer') +insert into BooksByAuthor (books_isbn, Author_name) values ('9781932394887','Gavin King') +insert into BooksByAuthor (books_isbn, Author_name) values ('9781932394887','Christian Bauer')