SchemaTruncator should reimport the load script after truncating

This commit is contained in:
Gavin 2022-11-17 01:00:55 +01:00 committed by Gavin King
parent bc4554f86e
commit 479aa10e2f
8 changed files with 195 additions and 17 deletions

View File

@ -18,7 +18,7 @@ import org.hibernate.query.sql.spi.NamedNativeQueryMemento;
import org.hibernate.query.sql.spi.NativeQueryImplementor; 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 Max Andersen
* @author Steve Ebersole * @author Steve Ebersole

View File

@ -20,7 +20,7 @@ import org.hibernate.query.spi.QueryImplementor;
import org.hibernate.query.sql.internal.NamedNativeQueryMementoImpl; 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 * @author Steve Ebersole
*/ */

View File

@ -51,7 +51,10 @@ public interface SchemaManager {
void validateMappedObjects(); 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}.
* <p> * <p>
* Programmatic way to run {@link org.hibernate.tool.schema.spi.SchemaTruncator}. * Programmatic way to run {@link org.hibernate.tool.schema.spi.SchemaTruncator}.
*/ */

View File

@ -12,28 +12,42 @@ import org.hibernate.boot.model.relational.Exportable;
import org.hibernate.boot.model.relational.Namespace; import org.hibernate.boot.model.relational.Namespace;
import org.hibernate.boot.model.relational.SqlStringGenerationContext; import org.hibernate.boot.model.relational.SqlStringGenerationContext;
import org.hibernate.boot.model.relational.internal.SqlStringGenerationContextImpl; 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.dialect.Dialect;
import org.hibernate.engine.jdbc.internal.FormatStyle; import org.hibernate.engine.jdbc.internal.FormatStyle;
import org.hibernate.engine.jdbc.internal.Formatter; import org.hibernate.engine.jdbc.internal.Formatter;
import org.hibernate.internal.util.StringHelper; import org.hibernate.internal.util.StringHelper;
import org.hibernate.internal.util.collections.CollectionHelper; import org.hibernate.internal.util.collections.CollectionHelper;
import org.hibernate.internal.util.config.ConfigurationHelper;
import org.hibernate.mapping.ForeignKey; import org.hibernate.mapping.ForeignKey;
import org.hibernate.mapping.Table; 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.GenerationTarget;
import org.hibernate.tool.schema.internal.exec.JdbcContext; 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.CommandAcceptanceException;
import org.hibernate.tool.schema.spi.ContributableMatcher; import org.hibernate.tool.schema.spi.ContributableMatcher;
import org.hibernate.tool.schema.spi.ExecutionOptions; import org.hibernate.tool.schema.spi.ExecutionOptions;
import org.hibernate.tool.schema.spi.SchemaFilter; import org.hibernate.tool.schema.spi.SchemaFilter;
import org.hibernate.tool.schema.spi.SchemaManagementException; import org.hibernate.tool.schema.spi.SchemaManagementException;
import org.hibernate.tool.schema.spi.SchemaTruncator; 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.hibernate.tool.schema.spi.TargetDescriptor;
import org.jboss.logging.Logger; import org.jboss.logging.Logger;
import java.net.URL;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Set; 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 * @author Gavin King
*/ */
@ -159,6 +173,10 @@ public class SchemaTruncatorImpl implements SchemaTruncator {
enableConstraints( namespace, metadata, formatter, options, sqlStringGenerationContext, enableConstraints( namespace, metadata, formatter, options, sqlStringGenerationContext,
contributableInclusionFilter, targets ); 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( 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<String> 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<String> 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 );
}
}
} }

View File

@ -4,13 +4,12 @@
* License: GNU Lesser General Public License (LGPL), version 2.1 or later. * 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>. * See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/ */
package org.hibernate.orm.test.catalog; package org.hibernate.orm.test.schemamanager;
import jakarta.persistence.CascadeType; import jakarta.persistence.CascadeType;
import jakarta.persistence.Entity; import jakarta.persistence.Entity;
import jakarta.persistence.Id; import jakarta.persistence.Id;
import jakarta.persistence.ManyToOne; import jakarta.persistence.ManyToOne;
import org.hibernate.dialect.PostgreSQLDialect;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.testing.orm.junit.DialectFeatureChecks; 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.RequiresDialectFeature;
import org.hibernate.testing.orm.junit.SessionFactory; import org.hibernate.testing.orm.junit.SessionFactory;
import org.hibernate.testing.orm.junit.SessionFactoryScope; import org.hibernate.testing.orm.junit.SessionFactoryScope;
import org.hibernate.testing.orm.junit.SkipForDialect;
import org.hibernate.tool.schema.spi.SchemaManagementException; import org.hibernate.tool.schema.spi.SchemaManagementException;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@ -30,11 +28,11 @@ import static org.junit.jupiter.api.Assertions.fail;
/** /**
* @author Gavin King * @author Gavin King
*/ */
@DomainModel(annotatedClasses = {SchemaManagerOracleTest.Book.class, SchemaManagerOracleTest.Author.class}) @DomainModel(annotatedClasses = {SchemaManagerDefaultSchemaTest.Book.class, SchemaManagerDefaultSchemaTest.Author.class})
@SessionFactory(exportSchema = false) @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) @RequiresDialectFeature(feature= DialectFeatureChecks.SupportsTruncateTable.class)
public class SchemaManagerOracleTest { public class SchemaManagerDefaultSchemaTest {
@BeforeEach @BeforeEach
public void clean(SessionFactoryScope scope) { public void clean(SessionFactoryScope scope) {
@ -45,7 +43,7 @@ public class SchemaManagerOracleTest {
return s.createQuery("select count(*) from BookForTesting", Long.class).getSingleResult(); 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(); SessionFactoryImplementor factory = scope.getSessionFactory();
factory.getSchemaManager().exportMappedObjects(true); factory.getSchemaManager().exportMappedObjects(true);
scope.inTransaction( s -> s.persist( new Book() ) ); scope.inTransaction( s -> s.persist( new Book() ) );

View File

@ -4,13 +4,12 @@
* License: GNU Lesser General Public License (LGPL), version 2.1 or later. * 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>. * See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/ */
package org.hibernate.orm.test.catalog; package org.hibernate.orm.test.schemamanager;
import jakarta.persistence.CascadeType; import jakarta.persistence.CascadeType;
import jakarta.persistence.Entity; import jakarta.persistence.Entity;
import jakarta.persistence.Id; import jakarta.persistence.Id;
import jakarta.persistence.ManyToOne; import jakarta.persistence.ManyToOne;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.dialect.OracleDialect; import org.hibernate.dialect.OracleDialect;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SessionImplementor; 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.BeforeEach;
import org.junit.jupiter.api.Test; 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.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail; import static org.junit.jupiter.api.Assertions.fail;
@ -33,12 +33,12 @@ import static org.junit.jupiter.api.Assertions.fail;
/** /**
* @author Gavin King * @author Gavin King
*/ */
@DomainModel(annotatedClasses = {SchemaManagerTest.Book.class, SchemaManagerTest.Author.class}) @DomainModel(annotatedClasses = {SchemaManagerExplicitSchemaTest.Book.class, SchemaManagerExplicitSchemaTest.Author.class})
@SessionFactory(exportSchema = false) @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") @SkipForDialect(dialectClass = OracleDialect.class, reason = "Oracle tests run in the DBO schema")
@RequiresDialectFeature(feature= DialectFeatureChecks.SupportsTruncateTable.class) @RequiresDialectFeature(feature= DialectFeatureChecks.SupportsTruncateTable.class)
public class SchemaManagerTest { public class SchemaManagerExplicitSchemaTest {
@BeforeEach @BeforeEach
public void clean(SessionFactoryScope scope) { public void clean(SessionFactoryScope scope) {
@ -49,7 +49,7 @@ public class SchemaManagerTest {
return s.createQuery("select count(*) from BookForTesting", Long.class).getSingleResult(); 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(); SessionFactoryImplementor factory = scope.getSessionFactory();
factory.getSchemaManager().exportMappedObjects(true); factory.getSchemaManager().exportMappedObjects(true);
scope.inTransaction( s -> s.persist( new Book() ) ); scope.inTransaction( s -> s.persist( new Book() ) );
@ -69,7 +69,7 @@ public class SchemaManagerTest {
@Entity(name="BookForTesting") @Entity(name="BookForTesting")
static class Book { static class Book {
@Id String isbn = "xyz123"; @Id String isbn = "9781932394153";
String title = "Hibernate in Action"; String title = "Hibernate in Action";
@ManyToOne(cascade = CascadeType.PERSIST) @ManyToOne(cascade = CascadeType.PERSIST)
Author author = new Author(); Author author = new Author();

View File

@ -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 <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
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<Book> books;
}
}

View File

@ -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')