Skip to content

Commit 8b43fdc

Browse files
gavinkingsebersole
authored andcommitted
HHH-19103 add SchemaManager.populate()
1 parent 25929f7 commit 8b43fdc

File tree

13 files changed

+436
-345
lines changed

13 files changed

+436
-345
lines changed

hibernate-core/src/main/java/org/hibernate/cfg/SchemaToolingSettings.java

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -384,16 +384,19 @@ public interface SchemaToolingSettings {
384384
* <p>
385385
* The default value is {@code /import.sql}.
386386
*
387-
* @deprecated The JPA-standard setting {@link #JAKARTA_HBM2DDL_CREATE_SCRIPT_SOURCE} is now preferred.
387+
* @deprecated The JPA-standard setting {@link #JAKARTA_HBM2DDL_LOAD_SCRIPT_SOURCE} is now preferred.
388388
*/
389389
@Deprecated
390390
String HBM2DDL_IMPORT_FILES = "hibernate.hbm2ddl.import_files";
391391

392392
/**
393-
* Specifies if the default {@code /import.sql} script file should not be executed
394-
* when {@link #HBM2DDL_IMPORT_FILES} is not specified and {@value #HBM2DDL_AUTO} is set to {@code create} or {@code create-drop}.
393+
* Specifies that the default {@code /import.sql} script file should not be executed
394+
* when {@link #HBM2DDL_IMPORT_FILES} is not specified and {@value #HBM2DDL_AUTO}
395+
* is set to {@code create} or {@code create-drop}.
395396
*
396-
* The default value is {@code false}.
397+
* @settingDefault {@code false}.
398+
*
399+
* @since 6.6
397400
*/
398401
String HBM2DDL_SKIP_DEFAULT_IMPORT_FILE = "hibernate.hbm2ddl.skip_default_import_file";
399402

hibernate-core/src/main/java/org/hibernate/relational/SchemaManager.java

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,8 @@ public interface SchemaManager extends jakarta.persistence.SchemaManager {
6262
void validateMappedObjects();
6363

6464
/**
65-
* Truncate the database tables mapped by Hibernate entities, and
66-
* then re-import initial data from any configured
65+
* Truncate the database tables mapped by Hibernate entities, and then
66+
* reimport initial data from {@code /import.sql} and any other configured
6767
* {@linkplain org.hibernate.cfg.AvailableSettings#JAKARTA_HBM2DDL_LOAD_SCRIPT_SOURCE
6868
* load script}.
6969
* <p>
@@ -72,4 +72,16 @@ public interface SchemaManager extends jakarta.persistence.SchemaManager {
7272
* @apiNote This operation is a synonym for {@link #truncate}.
7373
*/
7474
void truncateMappedObjects();
75+
76+
/**
77+
* Populate the database by executing {@code /import.sql} and any other configured
78+
* {@linkplain org.hibernate.cfg.AvailableSettings#JAKARTA_HBM2DDL_LOAD_SCRIPT_SOURCE
79+
* load script}.
80+
* <p>
81+
* Programmatic way to run {@link org.hibernate.tool.schema.spi.SchemaPopulator}.
82+
*
83+
* @since 7.0
84+
*/
85+
@Incubating
86+
void populate();
7587
}

hibernate-core/src/main/java/org/hibernate/relational/internal/SchemaManagerImpl.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,19 @@ public void truncateMappedObjects() {
8989
);
9090
}
9191

92+
@Override
93+
public void populate() {
94+
Map<String, Object> properties = new HashMap<>( sessionFactory.getProperties() );
95+
properties.put( AvailableSettings.JAKARTA_HBM2DDL_DATABASE_ACTION, Action.POPULATE );
96+
properties.put( AvailableSettings.JAKARTA_HBM2DDL_SCRIPTS_ACTION, Action.NONE );
97+
SchemaManagementToolCoordinator.process(
98+
metadata,
99+
sessionFactory.getServiceRegistry(),
100+
properties,
101+
action -> {}
102+
);
103+
}
104+
92105
@Override
93106
public void create(boolean createSchemas) {
94107
exportMappedObjects( createSchemas );

hibernate-core/src/main/java/org/hibernate/tool/schema/Action.java

Lines changed: 34 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,18 @@ public enum Action {
105105
*
106106
* @since 6.2
107107
*/
108-
TRUNCATE;
108+
TRUNCATE,
109+
/**
110+
* Populate an existing schema by executing {@code /import.sql} and other scripts specified
111+
* via {@value org.hibernate.cfg.SchemaToolingSettings#JAKARTA_HBM2DDL_LOAD_SCRIPT_SOURCE}.
112+
*
113+
* @apiNote This action is not defined by JPA.
114+
*
115+
* @see org.hibernate.tool.schema.spi.SchemaPopulator
116+
*
117+
* @since 7.0
118+
*/
119+
POPULATE;
109120

110121
/**
111122
* @see #NONE
@@ -135,6 +146,10 @@ public enum Action {
135146
* @see #UPDATE
136147
*/
137148
public static final String ACTION_UPDATE = "update";
149+
/**
150+
* @see #POPULATE
151+
*/
152+
public static final String ACTION_POPULATE = "populate";
138153

139154
/**
140155
* @see #NONE
@@ -159,43 +174,31 @@ public enum Action {
159174
* or {@value org.hibernate.cfg.AvailableSettings#JAKARTA_HBM2DDL_SCRIPTS_ACTION}.
160175
*/
161176
public String getExternalJpaName() {
162-
switch (this) {
163-
case NONE:
164-
return SPEC_ACTION_NONE;
165-
case CREATE_ONLY:
166-
return SPEC_ACTION_CREATE;
167-
case DROP:
168-
return SPEC_ACTION_DROP;
169-
case CREATE:
170-
return SPEC_ACTION_DROP_AND_CREATE;
171-
default:
172-
return null;
173-
}
177+
return switch ( this ) {
178+
case NONE -> SPEC_ACTION_NONE;
179+
case CREATE_ONLY -> SPEC_ACTION_CREATE;
180+
case DROP -> SPEC_ACTION_DROP;
181+
case CREATE -> SPEC_ACTION_DROP_AND_CREATE;
182+
default -> null;
183+
};
174184
}
175185

176186
/**
177187
* The string configuration value identifying this action in old-school Hibernate
178188
* configuration via {@value org.hibernate.cfg.AvailableSettings#HBM2DDL_AUTO}.
179189
*/
180190
public String getExternalHbm2ddlName() {
181-
switch (this) {
182-
case NONE:
183-
return ACTION_NONE;
184-
case CREATE_ONLY:
185-
return ACTION_CREATE_ONLY;
186-
case DROP:
187-
return ACTION_DROP;
188-
case CREATE:
189-
return ACTION_CREATE;
190-
case CREATE_DROP:
191-
return ACTION_CREATE_THEN_DROP;
192-
case VALIDATE:
193-
return ACTION_VALIDATE;
194-
case UPDATE:
195-
return ACTION_UPDATE;
196-
default:
197-
return null;
198-
}
191+
return switch ( this ) {
192+
case NONE -> ACTION_NONE;
193+
case CREATE_ONLY -> ACTION_CREATE_ONLY;
194+
case DROP -> ACTION_DROP;
195+
case CREATE -> ACTION_CREATE;
196+
case CREATE_DROP -> ACTION_CREATE_THEN_DROP;
197+
case VALIDATE -> ACTION_VALIDATE;
198+
case UPDATE -> ACTION_UPDATE;
199+
case POPULATE -> ACTION_POPULATE;
200+
default -> null;
201+
};
199202
}
200203

201204
@Override
Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
/*
2+
* SPDX-License-Identifier: Apache-2.0
3+
* Copyright Red Hat Inc. and Hibernate Authors
4+
*/
5+
package org.hibernate.tool.schema.internal;
6+
7+
import org.hibernate.boot.registry.classloading.spi.ClassLoaderService;
8+
import org.hibernate.dialect.Dialect;
9+
import org.hibernate.engine.jdbc.internal.FormatStyle;
10+
import org.hibernate.engine.jdbc.internal.Formatter;
11+
import org.hibernate.internal.util.StringHelper;
12+
import org.hibernate.tool.schema.internal.exec.ScriptSourceInputFromUrl;
13+
import org.hibernate.tool.schema.internal.exec.ScriptSourceInputNonExistentImpl;
14+
import org.hibernate.tool.schema.spi.ExecutionOptions;
15+
import org.hibernate.tool.schema.spi.GenerationTarget;
16+
import org.hibernate.tool.schema.spi.SchemaManagementException;
17+
import org.hibernate.tool.schema.spi.ScriptSourceInput;
18+
import org.hibernate.tool.schema.spi.SqlScriptCommandExtractor;
19+
20+
import java.net.URL;
21+
import java.util.Map;
22+
23+
import static org.hibernate.cfg.SchemaToolingSettings.HBM2DDL_CHARSET_NAME;
24+
import static org.hibernate.cfg.SchemaToolingSettings.HBM2DDL_IMPORT_FILES;
25+
import static org.hibernate.cfg.SchemaToolingSettings.HBM2DDL_LOAD_SCRIPT_SOURCE;
26+
import static org.hibernate.cfg.SchemaToolingSettings.HBM2DDL_SKIP_DEFAULT_IMPORT_FILE;
27+
import static org.hibernate.cfg.SchemaToolingSettings.JAKARTA_HBM2DDL_LOAD_SCRIPT_SOURCE;
28+
import static org.hibernate.internal.util.config.ConfigurationHelper.getBoolean;
29+
import static org.hibernate.internal.util.config.ConfigurationHelper.getString;
30+
import static org.hibernate.tool.schema.internal.Helper.applyScript;
31+
import static org.hibernate.tool.schema.internal.Helper.interpretScriptSourceSetting;
32+
33+
/**
34+
* Handles population from {@value #DEFAULT_IMPORT_FILE} and other scripts.
35+
*/
36+
public abstract class AbstractSchemaPopulator {
37+
38+
public static final String DEFAULT_IMPORT_FILE = "/import.sql";
39+
40+
abstract ClassLoaderService getClassLoaderService();
41+
42+
void applyImportSources(
43+
ExecutionOptions options,
44+
SqlScriptCommandExtractor commandExtractor,
45+
boolean format,
46+
Dialect dialect,
47+
GenerationTarget... targets) {
48+
49+
final Formatter formatter = getImportScriptFormatter(format);
50+
51+
boolean hasDefaultImportFileScriptBeenExecuted = applyImportScript(
52+
options,
53+
commandExtractor,
54+
dialect,
55+
formatter,
56+
targets
57+
);
58+
applyImportFiles(
59+
options,
60+
commandExtractor,
61+
dialect,
62+
formatter,
63+
hasDefaultImportFileScriptBeenExecuted ? "" : getDefaultImportFile( options ),
64+
targets
65+
);
66+
}
67+
68+
private String getDefaultImportFile(ExecutionOptions options) {
69+
return skipDefaultFileImport( options ) ? "" : DEFAULT_IMPORT_FILE;
70+
}
71+
72+
private static boolean skipDefaultFileImport(ExecutionOptions options) {
73+
return getBoolean( HBM2DDL_SKIP_DEFAULT_IMPORT_FILE, options.getConfigurationValues(), false );
74+
}
75+
76+
/**
77+
* In principle, we should format the commands in the import script if the
78+
* {@code format} parameter is {@code true}, and since it's supposed to be
79+
* a list of DML statements, we should use the {@linkplain FormatStyle#BASIC
80+
* basic DML formatter} to do that. However, in practice we don't really know
81+
* much about what this file contains, and we have never formatted it in the
82+
* past, so there's no compelling reason to start now. In fact, if we have
83+
* lists of many {@code insert} statements on the same table, which is what
84+
* we typically expect, it's probably better to not format.
85+
*/
86+
private static Formatter getImportScriptFormatter(boolean format) {
87+
// return format ? FormatStyle.BASIC.getFormatter() : FormatStyle.NONE.getFormatter();
88+
return FormatStyle.NONE.getFormatter();
89+
}
90+
91+
/**
92+
* Handles import scripts specified using
93+
* {@link org.hibernate.cfg.AvailableSettings#HBM2DDL_IMPORT_FILES}.
94+
*
95+
* @return {@code true} if the legacy {@linkplain #DEFAULT_IMPORT_FILE default import file}
96+
* was one of the listed imported files that were executed
97+
*/
98+
private boolean applyImportScript(
99+
ExecutionOptions options,
100+
SqlScriptCommandExtractor commandExtractor,
101+
Dialect dialect,
102+
Formatter formatter,
103+
GenerationTarget[] targets) {
104+
final Object importScriptSetting = getImportScriptSetting( options );
105+
if ( importScriptSetting != null ) {
106+
final ScriptSourceInput importScriptInput =
107+
interpretScriptSourceSetting( importScriptSetting, getClassLoaderService(), getCharsetName( options ) );
108+
applyScript(
109+
options,
110+
commandExtractor,
111+
dialect,
112+
importScriptInput,
113+
formatter,
114+
targets
115+
);
116+
return containsDefaultImportFile( importScriptInput, options );
117+
}
118+
else {
119+
return false;
120+
}
121+
}
122+
123+
private boolean containsDefaultImportFile(ScriptSourceInput importScriptInput,ExecutionOptions options ) {
124+
if ( skipDefaultFileImport( options ) ) {
125+
return false;
126+
}
127+
final URL defaultImportFileUrl = getClassLoaderService().locateResource( DEFAULT_IMPORT_FILE );
128+
return defaultImportFileUrl != null && importScriptInput.containsScript( defaultImportFileUrl );
129+
}
130+
131+
/**
132+
* Handles import scripts specified using
133+
* {@link org.hibernate.cfg.AvailableSettings#JAKARTA_HBM2DDL_LOAD_SCRIPT_SOURCE}.
134+
*/
135+
private void applyImportFiles(
136+
ExecutionOptions options,
137+
SqlScriptCommandExtractor commandExtractor,
138+
Dialect dialect,
139+
Formatter formatter,
140+
String defaultImportFile,
141+
GenerationTarget[] targets) {
142+
final String[] importFiles =
143+
StringHelper.split( ",",
144+
getString( HBM2DDL_IMPORT_FILES, options.getConfigurationValues(), defaultImportFile ) );
145+
final String charsetName = getCharsetName( options );
146+
final ClassLoaderService classLoaderService = getClassLoaderService();
147+
for ( String currentFile : importFiles ) {
148+
final String resourceName = currentFile.trim();
149+
if ( !resourceName.isEmpty() ) { //skip empty resource names
150+
applyScript(
151+
options,
152+
commandExtractor,
153+
dialect,
154+
interpretLegacyImportScriptSetting( resourceName, classLoaderService, charsetName ),
155+
formatter,
156+
targets
157+
);
158+
}
159+
}
160+
}
161+
162+
private ScriptSourceInput interpretLegacyImportScriptSetting(
163+
String resourceName,
164+
ClassLoaderService classLoaderService,
165+
String charsetName) {
166+
try {
167+
final URL resourceUrl = classLoaderService.locateResource( resourceName );
168+
return resourceUrl == null
169+
? ScriptSourceInputNonExistentImpl.INSTANCE
170+
: new ScriptSourceInputFromUrl( resourceUrl, charsetName );
171+
}
172+
catch (Exception e) {
173+
throw new SchemaManagementException( "Error resolving legacy import resource : " + resourceName, e );
174+
}
175+
}
176+
177+
/**
178+
* @see org.hibernate.cfg.AvailableSettings#HBM2DDL_CHARSET_NAME
179+
*/
180+
private static String getCharsetName(ExecutionOptions options) {
181+
return (String) options.getConfigurationValues().get( HBM2DDL_CHARSET_NAME );
182+
}
183+
184+
/**
185+
* @see org.hibernate.cfg.AvailableSettings#JAKARTA_HBM2DDL_LOAD_SCRIPT_SOURCE
186+
*
187+
* @return a {@link java.io.Reader} or a string URL
188+
*/
189+
private static Object getImportScriptSetting(ExecutionOptions options) {
190+
final Map<String, Object> configuration = options.getConfigurationValues();
191+
final Object importScriptSetting = configuration.get( HBM2DDL_LOAD_SCRIPT_SOURCE );
192+
return importScriptSetting == null
193+
? configuration.get( JAKARTA_HBM2DDL_LOAD_SCRIPT_SOURCE )
194+
: importScriptSetting;
195+
}
196+
}

hibernate-core/src/main/java/org/hibernate/tool/schema/internal/HibernateSchemaManagementTool.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
import org.hibernate.tool.schema.spi.SchemaManagementException;
4444
import org.hibernate.tool.schema.spi.SchemaManagementTool;
4545
import org.hibernate.tool.schema.spi.SchemaMigrator;
46+
import org.hibernate.tool.schema.spi.SchemaPopulator;
4647
import org.hibernate.tool.schema.spi.SchemaTruncator;
4748
import org.hibernate.tool.schema.spi.SchemaValidator;
4849
import org.hibernate.tool.schema.spi.TargetDescriptor;
@@ -101,6 +102,11 @@ public SchemaTruncator getSchemaTruncator(Map<String,Object> options) {
101102
return new SchemaTruncatorImpl( this, getSchemaFilterProvider( options ).getTruncatorFilter() );
102103
}
103104

105+
@Override
106+
public SchemaPopulator getSchemaPopulator(Map<String, Object> options) {
107+
return new SchemaPopulatorImpl( this );
108+
}
109+
104110
@Override
105111
public SchemaMigrator getSchemaMigrator(Map<String,Object> options) {
106112
final SchemaFilter migrateFilter = getSchemaFilterProvider( options ).getMigrateFilter();

0 commit comments

Comments
 (0)