HHH-7957 - Integrate Public Review Draft of the JPA 2.1 spec : schema generation

This commit is contained in:
Steve Ebersole 2013-02-06 16:41:18 -06:00
parent ec6494020b
commit 854c94bcc7
10 changed files with 377 additions and 364 deletions

View File

@ -195,78 +195,11 @@ public interface AvailableSettings {
public static final String CDI_BEAN_MANAGER = "javax.persistence.bean.manager"; public static final String CDI_BEAN_MANAGER = "javax.persistence.bean.manager";
/** /**
* Specifies the action to be taken by the persistence provider. The set of possible values are:<ul> * Specifies whether schema generation commands for schema creation are to be determine based on object/relational
* <li>none</li> * mapping metadata, DDL scripts, or a combination of the two. See {@link SchemaGenSource} for valid set of values.
* <li>create</li>
* <li>drop</li>
* <li>drop-and-create</li>
* </ul>
*
* If no value is specified, the default is "none".
*
* @see SchemaGenAction
*/
public static final String SCHEMA_GEN_ACTION = "javax.persistence.schema-generation-action";
/**
* Specifies whether the schema is to be created in the database, whether scripts are to be generated, or both.
* The values for this property are:<ul>
* <li>database</li>
* <li>scripts</li>
* <li>database-and-scripts</li>
* </ul>
* If no value is specified, a default is assumed as follows:<ul> * If no value is specified, a default is assumed as follows:<ul>
* <li> * <li>
* if script targets are specified (per {@value #SCHEMA_GEN_CREATE_SCRIPT_TARGET} and * if source scripts are specified (per {@value #SCHEMA_GEN_CREATE_SCRIPT_SOURCE}),then "scripts" is assumed
* {@value #SCHEMA_GEN_DROP_SCRIPT_TARGET}), then the default is assumed to be "scripts"
* </li>
* <li>
* Otherwise, "database" is assumed
* </li>
* </ul>
*
* @see SchemaGenTarget
*/
public static final String SCHEMA_GEN_TARGET = "javax.persistence.schema-generation-target";
/**
* If schema creations scripts are to be generated, the target/location for these scripts must be specified. This
* target may take the form of either a {@link java.io.Writer} or a string designating a
* {@link java.net.URL}.
* <p/>
* Create and drop scripts are written separately (though the same Writer/URL could be passed).
* {@value #SCHEMA_GEN_CREATE_SCRIPT_TARGET} specifies the target for the create script.
*
* @see #SCHEMA_GEN_DROP_SCRIPT_TARGET
*/
@SuppressWarnings("JavaDoc")
public static final String SCHEMA_GEN_CREATE_SCRIPT_TARGET = "javax.persistence.ddl-create-script-target";
/**
* If schema creations scripts are to be generated, the target/location for these scripts must be specified. This
* target may take the form of either a {@link java.io.Writer} or a string designating a
* {@link java.net.URL}.
* <p/>
* Create and drop scripts are written separately (though the same Writer/URL could be passed).
* {@value #SCHEMA_GEN_DROP_SCRIPT_TARGET} specifies the target for the create script.
*
* @see #SCHEMA_GEN_CREATE_SCRIPT_TARGET
*/
@SuppressWarnings("JavaDoc")
public static final String SCHEMA_GEN_DROP_SCRIPT_TARGET = "javax.persistence.ddl-drop-script-target";
/**
* Specifies whether schema generation is to occur on the basis of the object/relational mapping metadata, DDL
* scripts, or a combination of the two. The valid values for this property are: <ul>
* <li>metadata</li>
* <li>scripts</li>
* <li>metadata-then-scripts</li>
* <li>scripts-then-metadata</li>
* </ul>
* If no value is specified, a default is assumed as follows:<ul>
* <li>
* if source scripts are specified (per {@value #SCHEMA_GEN_CREATE_SCRIPT_SOURCE} and
* {@value #SCHEMA_GEN_DROP_SCRIPT_SOURCE}),then "scripts" is assumed
* </li> * </li>
* <li> * <li>
* otherwise, "metadata" is assumed * otherwise, "metadata" is assumed
@ -275,23 +208,83 @@ public interface AvailableSettings {
* *
* @see SchemaGenSource * @see SchemaGenSource
*/ */
public static final String SCHEMA_GEN_SOURCE = "javax.persistence.schema-generation-source"; public static final String SCHEMA_GEN_CREATE_SOURCE = "javax.persistence.schema-generation.create-source ";
/**
* Specifies whether schema generation commands for schema dropping are to be determine based on object/relational
* mapping metadata, DDL scripts, or a combination of the two. See {@link SchemaGenSource} for valid set of values.
* If no value is specified, a default is assumed as follows:<ul>
* <li>
* if source scripts are specified (per {@value #SCHEMA_GEN_DROP_SCRIPT_SOURCE}),then "scripts" is assumed
* </li>
* <li>
* otherwise, "metadata" is assumed
* </li>
* </ul>
*
* @see SchemaGenSource
*/
public static final String SCHEMA_GEN_DROP_SOURCE = "javax.persistence.schema-generation.drop-source ";
/** /**
* Specifies the CREATE script file as either a {@link java.io.Reader} configured for reading of the DDL script * Specifies the CREATE script file as either a {@link java.io.Reader} configured for reading of the DDL script
* file or a string designating a file {@link java.net.URL} for the DDL script. * file or a string designating a file {@link java.net.URL} for the DDL script.
* *
* @see #SCHEMA_GEN_CREATE_SOURCE
* @see #SCHEMA_GEN_DROP_SCRIPT_SOURCE * @see #SCHEMA_GEN_DROP_SCRIPT_SOURCE
*/ */
public static final String SCHEMA_GEN_CREATE_SCRIPT_SOURCE = "javax.persistence.ddl-create-script-source"; public static final String SCHEMA_GEN_CREATE_SCRIPT_SOURCE = "javax.persistence.schema-generation.create-script-source ";
/** /**
* Specifies the DROP script file as either a {@link java.io.Reader} configured for reading of the DDL script * Specifies the DROP script file as either a {@link java.io.Reader} configured for reading of the DDL script
* file or a string designating a file {@link java.net.URL} for the DDL script. * file or a string designating a file {@link java.net.URL} for the DDL script.
* *
* @see #SCHEMA_GEN_DROP_SOURCE
* @see #SCHEMA_GEN_CREATE_SCRIPT_SOURCE * @see #SCHEMA_GEN_CREATE_SCRIPT_SOURCE
*/ */
public static final String SCHEMA_GEN_DROP_SCRIPT_SOURCE = "javax.persistence.ddl-drop-script-source"; public static final String SCHEMA_GEN_DROP_SCRIPT_SOURCE = "javax.persistence.schema-generation.drop-script-source ";
/**
* Specifies the type of schema generation action to be taken by the persistence provider in regards to sending
* commands directly to the database via JDBC. See {@link SchemaGenAction} for the set of possible values.
* <p/>
* If no value is specified, the default is "none".
*
* @see SchemaGenAction
*/
public static final String SCHEMA_GEN_DATABASE_ACTION = "javax.persistence.schema-generation.database.action";
/**
* Specifies the type of schema generation action to be taken by the persistence provider in regards to writing
* commands to DDL script files. See {@link SchemaGenAction} for the set of possible values.
* <p/>
* If no value is specified, the default is "none".
*
* @see SchemaGenAction
*/
public static final String SCHEMA_GEN_SCRIPTS_ACTION = "javax.persistence.schema-generation.scripts.action";
/**
* For cases where the {@value #SCHEMA_GEN_SCRIPTS_ACTION} value indicates that schema creation commands should
* be written to DDL script file, {@value #SCHEMA_GEN_SCRIPTS_CREATE_TARGET} specifies either a
* {@link java.io.Writer} configured for output of the DDL script or a string specifying the file URL for the DDL
* script.
*
* @see #SCHEMA_GEN_SCRIPTS_ACTION
*/
@SuppressWarnings("JavaDoc")
public static final String SCHEMA_GEN_SCRIPTS_CREATE_TARGET = "javax.persistence.schema-generation.scripts.create-target";
/**
* For cases where the {@value #SCHEMA_GEN_SCRIPTS_ACTION} value indicates that schema drop commands should
* be written to DDL script file, {@value #SCHEMA_GEN_SCRIPTS_DROP_TARGET} specifies either a
* {@link java.io.Writer} configured for output of the DDL script or a string specifying the file URL for the DDL
* script.
*
* @see #SCHEMA_GEN_SCRIPTS_ACTION
*/
@SuppressWarnings("JavaDoc")
public static final String SCHEMA_GEN_SCRIPTS_DROP_TARGET = "javax.persistence.schema-generation.scripts.drop-target";
/** /**
* Specifies whether the persistence provider is to create the database schema(s) in addition to creating * Specifies whether the persistence provider is to create the database schema(s) in addition to creating

View File

@ -26,9 +26,11 @@ package org.hibernate.jpa;
import org.hibernate.internal.util.StringHelper; import org.hibernate.internal.util.StringHelper;
/** /**
* Describes the allowable values of the {@value AvailableSettings#SCHEMA_GEN_ACTION} setting. * Describes the allowable values of the {@value AvailableSettings#SCHEMA_GEN_DATABASE_ACTION} and
* {@link AvailableSettings#SCHEMA_GEN_SCRIPTS_ACTION} settings.
* *
* @see AvailableSettings#SCHEMA_GEN_ACTION * @see AvailableSettings#SCHEMA_GEN_DATABASE_ACTION
* @see AvailableSettings#SCHEMA_GEN_SCRIPTS_ACTION
* *
* @author Steve Ebersole * @author Steve Ebersole
*/ */
@ -57,9 +59,9 @@ public enum SchemaGenAction {
} }
/** /**
* Used when processing JPA configuration to interpret the {@value AvailableSettings#SCHEMA_GEN_ACTION} setting. * Used when processing JPA configuration to interpret the user config values.
* *
* @param value The encountered value of {@value AvailableSettings#SCHEMA_GEN_ACTION} * @param value The encountered config value
* *
* @return The matching enum value. An empty value will return {@link #NONE}. * @return The matching enum value. An empty value will return {@link #NONE}.
* *
@ -82,7 +84,12 @@ public enum SchemaGenAction {
} }
throw new IllegalArgumentException( throw new IllegalArgumentException(
String.format( "Unrecognized '%s' value : %s", AvailableSettings.SCHEMA_GEN_ACTION, value ) String.format(
"Unrecognized '%s' or '%s' value : %s",
AvailableSettings.SCHEMA_GEN_DATABASE_ACTION,
AvailableSettings.SCHEMA_GEN_SCRIPTS_ACTION,
value
)
); );
} }

View File

@ -26,9 +26,11 @@ package org.hibernate.jpa;
import org.hibernate.internal.util.StringHelper; import org.hibernate.internal.util.StringHelper;
/** /**
* Describes the allowable values of the {@value AvailableSettings#SCHEMA_GEN_SOURCE} setting. * Describes the allowable values of the {@value AvailableSettings#SCHEMA_GEN_CREATE_SOURCE} and
* {@value AvailableSettings#SCHEMA_GEN_DROP_SOURCE} settings.
* *
* @see AvailableSettings#SCHEMA_GEN_SOURCE * @see AvailableSettings#SCHEMA_GEN_CREATE_SOURCE
* @see AvailableSettings#SCHEMA_GEN_DROP_SOURCE
* *
* @author Steve Ebersole * @author Steve Ebersole
*/ */
@ -71,9 +73,9 @@ public enum SchemaGenSource {
} }
/** /**
* Used when processing JPA configuration to interpret the {@value AvailableSettings#SCHEMA_GEN_SOURCE} setting. * Used when processing JPA configuration to interpret the user config value
* *
* @param value The encountered value of {@value AvailableSettings#SCHEMA_GEN_SOURCE} * @param value The encountered user config value
* *
* @return The matching enum value. An empty value will return {@code null}. * @return The matching enum value. An empty value will return {@code null}.
* *

View File

@ -1,92 +0,0 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.jpa;
import org.hibernate.internal.util.StringHelper;
/**
* Describes the allowable values of the {@value AvailableSettings#SCHEMA_GEN_TARGET} setting.
*
* @see AvailableSettings#SCHEMA_GEN_TARGET
*
* @author Steve Ebersole
*/
public enum SchemaGenTarget {
/**
* "database" - Generation commands will be executed directly against the database (via JDBC Statements).
*/
DATABASE( "database" ),
/**
* "scripts" - Generation commands will be written to script (text) "targets" as indicated by the
* {@value AvailableSettings#SCHEMA_GEN_CREATE_SCRIPT_TARGET} and
* {@value AvailableSettings#SCHEMA_GEN_DROP_SCRIPT_TARGET} settings.
*/
SCRIPTS( "scripts" ),
/**
* "database-and-scripts" - Generation commands will be sent to both.
*
* @see #DATABASE
* @see #SCRIPTS
*/
BOTH( "database-and-scripts" );
private final String externalName;
private SchemaGenTarget(String externalName) {
this.externalName = externalName;
}
/**
* Used when processing JPA configuration to interpret the {@value AvailableSettings#SCHEMA_GEN_TARGET} setting.
*
* @param value The encountered value of {@value AvailableSettings#SCHEMA_GEN_TARGET}
*
* @return The matching enum value. An empty value will return {@code null}.
*
* @throws IllegalArgumentException If the incoming value is unrecognized
*/
public static SchemaGenTarget interpret(String value) {
if ( StringHelper.isEmpty( value ) ) {
// empty is in fact valid as means to interpret default value based on other settings
return null;
}
if ( DATABASE.externalName.equals( value ) ) {
return DATABASE;
}
else if ( SCRIPTS.externalName.equals( value ) ) {
return SCRIPTS;
}
else if ( BOTH.externalName.equals( value ) ) {
return BOTH;
}
throw new IllegalArgumentException( "Unknown schema generation target value : " + value );
}
@Override
public String toString() {
return getClass().getSimpleName() + "(" + externalName + ")";
}
}

View File

@ -29,27 +29,32 @@ import java.sql.Statement;
import org.jboss.logging.Logger; import org.jboss.logging.Logger;
import org.hibernate.jpa.SchemaGenAction;
/** /**
* GenerationTarget implementation for handling generation directly to the database * GenerationTarget implementation for handling generation directly to the database
* *
* @see org.hibernate.jpa.SchemaGenTarget#DATABASE
* @see org.hibernate.jpa.SchemaGenTarget#BOTH
*
* @author Steve Ebersole * @author Steve Ebersole
*/ */
class DatabaseTarget implements GenerationTarget { class DatabaseTarget implements GenerationTarget {
private static final Logger log = Logger.getLogger( DatabaseTarget.class ); private static final Logger log = Logger.getLogger( DatabaseTarget.class );
private final JdbcConnectionContext jdbcConnectionContext; private final JdbcConnectionContext jdbcConnectionContext;
private final SchemaGenAction databaseAction;
private Statement jdbcStatement; private Statement jdbcStatement;
DatabaseTarget(JdbcConnectionContext jdbcConnectionContext) { DatabaseTarget(JdbcConnectionContext jdbcConnectionContext, SchemaGenAction databaseAction) {
this.jdbcConnectionContext = jdbcConnectionContext; this.jdbcConnectionContext = jdbcConnectionContext;
this.databaseAction = databaseAction;
} }
@Override @Override
public void acceptCreateCommands(Iterable<String> commands) { public void acceptCreateCommands(Iterable<String> commands) {
if ( !databaseAction.includesCreate() ) {
return;
}
for ( String command : commands ) { for ( String command : commands ) {
try { try {
jdbcStatement().execute( command ); jdbcStatement().execute( command );
@ -76,6 +81,10 @@ class DatabaseTarget implements GenerationTarget {
@Override @Override
public void acceptDropCommands(Iterable<String> commands) { public void acceptDropCommands(Iterable<String> commands) {
if ( !databaseAction.includesDrop() ) {
return;
}
for ( String command : commands ) { for ( String command : commands ) {
try { try {
jdbcStatement().execute( command ); jdbcStatement().execute( command );

View File

@ -24,10 +24,11 @@
package org.hibernate.jpa.internal.schemagen; package org.hibernate.jpa.internal.schemagen;
/** /**
* Contract describing a generation source for create commands * Contract describing a source for create/drop commands.
* *
* @see org.hibernate.jpa.SchemaGenSource * @see org.hibernate.jpa.SchemaGenSource
* @see org.hibernate.jpa.AvailableSettings#SCHEMA_GEN_SOURCE * @see org.hibernate.jpa.AvailableSettings#SCHEMA_GEN_CREATE_SOURCE
* @see org.hibernate.jpa.AvailableSettings#SCHEMA_GEN_DROP_SOURCE
* *
* @author Steve Ebersole * @author Steve Ebersole
*/ */
@ -37,14 +38,7 @@ interface GenerationSource {
* *
* @return The generation commands * @return The generation commands
*/ */
public Iterable<String> getCreateCommands(); public Iterable<String> getCommands();
/**
* Retrieve the drop generation commands from this source
*
* @return The generation commands
*/
public Iterable<String> getDropCommands();
/** /**
* Release this source. Give it a change to release its resources, if any. * Release this source. Give it a change to release its resources, if any.

View File

@ -26,8 +26,10 @@ package org.hibernate.jpa.internal.schemagen;
import javax.persistence.PersistenceException; import javax.persistence.PersistenceException;
import java.io.Reader; import java.io.Reader;
import java.sql.Connection; import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
import java.util.Iterator; import java.util.Iterator;
@ -35,20 +37,19 @@ import java.util.List;
import org.jboss.logging.Logger; import org.jboss.logging.Logger;
import org.hibernate.HibernateException;
import org.hibernate.boot.registry.selector.spi.StrategySelector;
import org.hibernate.cfg.Configuration; import org.hibernate.cfg.Configuration;
import org.hibernate.dialect.Dialect; import org.hibernate.dialect.Dialect;
import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider; import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider;
import org.hibernate.engine.jdbc.dialect.spi.DatabaseInfoDialectResolver; import org.hibernate.engine.jdbc.dialect.spi.DatabaseInfoDialectResolver;
import org.hibernate.engine.jdbc.dialect.spi.DialectResolver;
import org.hibernate.engine.jdbc.spi.JdbcConnectionAccess; import org.hibernate.engine.jdbc.spi.JdbcConnectionAccess;
import org.hibernate.engine.jdbc.spi.JdbcServices;
import org.hibernate.id.IdentifierGenerator;
import org.hibernate.id.PersistentIdentifierGenerator;
import org.hibernate.internal.util.StringHelper; import org.hibernate.internal.util.StringHelper;
import org.hibernate.internal.util.config.ConfigurationHelper; import org.hibernate.internal.util.config.ConfigurationHelper;
import org.hibernate.jpa.AvailableSettings; import org.hibernate.jpa.AvailableSettings;
import org.hibernate.jpa.SchemaGenAction; import org.hibernate.jpa.SchemaGenAction;
import org.hibernate.jpa.SchemaGenSource; import org.hibernate.jpa.SchemaGenSource;
import org.hibernate.jpa.SchemaGenTarget;
import org.hibernate.mapping.Table; import org.hibernate.mapping.Table;
import org.hibernate.service.ServiceRegistry; import org.hibernate.service.ServiceRegistry;
import org.hibernate.tool.hbm2ddl.ImportSqlCommandExtractor; import org.hibernate.tool.hbm2ddl.ImportSqlCommandExtractor;
@ -65,11 +66,15 @@ public class JpaSchemaGenerator {
// First, determine the actions (if any) to be performed ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // First, determine the actions (if any) to be performed ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
final SchemaGenAction action = SchemaGenAction.interpret( final SchemaGenAction databaseAction = SchemaGenAction.interpret(
hibernateConfiguration.getProperty( AvailableSettings.SCHEMA_GEN_ACTION ) hibernateConfiguration.getProperty( AvailableSettings.SCHEMA_GEN_DATABASE_ACTION )
); );
if ( action == SchemaGenAction.NONE ) { final SchemaGenAction scriptsAction = SchemaGenAction.interpret(
// no generation requested hibernateConfiguration.getProperty( AvailableSettings.SCHEMA_GEN_SCRIPTS_ACTION )
);
if ( databaseAction == SchemaGenAction.NONE && scriptsAction == SchemaGenAction.NONE ) {
// no actions needed
return; return;
} }
@ -81,97 +86,99 @@ public class JpaSchemaGenerator {
serviceRegistry serviceRegistry
); );
final Dialect dialect = determineDialect( jdbcConnectionContext, hibernateConfiguration, serviceRegistry ); try {
final Dialect dialect = determineDialect( jdbcConnectionContext, hibernateConfiguration, serviceRegistry );
final ImportSqlCommandExtractor scriptCommandExtractor = serviceRegistry.getService( ImportSqlCommandExtractor.class );
// Next, determine the targets ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // determine sources ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
final List<GenerationTarget> generationTargetList = new ArrayList<GenerationTarget>();
SchemaGenTarget target = SchemaGenTarget.interpret( final List<GenerationSource> createSourceList = databaseAction.includesCreate() || scriptsAction.includesCreate()
hibernateConfiguration.getProperty( AvailableSettings.SCHEMA_GEN_TARGET ) ? buildCreateSourceList( hibernateConfiguration, serviceRegistry, dialect )
); : Collections.<GenerationSource>emptyList();
// the default is dependent upon whether script targets were also specified... final List<GenerationSource> dropSourceList = databaseAction.includesDrop() || scriptsAction.includesDrop()
final Object createScriptTargetSetting = hibernateConfiguration.getProperties().get( ? buildDropSourceList( hibernateConfiguration, serviceRegistry, dialect )
AvailableSettings.SCHEMA_GEN_CREATE_SCRIPT_TARGET : Collections.<GenerationSource>emptyList();
);
final Object dropScriptTargetSetting = hibernateConfiguration.getProperties().get(
AvailableSettings.SCHEMA_GEN_CREATE_SCRIPT_TARGET
);
if ( target == null ) {
if ( createScriptTargetSetting != null && dropScriptTargetSetting != null ) { // determine targets ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
target = SchemaGenTarget.SCRIPTS;
final GenerationTarget databaseTarget = new DatabaseTarget( jdbcConnectionContext, databaseAction );
final Object createScriptTargetSetting = hibernateConfiguration.getProperties().get(
AvailableSettings.SCHEMA_GEN_SCRIPTS_CREATE_TARGET
);
final Object dropScriptTargetSetting = hibernateConfiguration.getProperties().get(
AvailableSettings.SCHEMA_GEN_SCRIPTS_DROP_TARGET
);
final GenerationTarget scriptsTarget = new ScriptsTarget( createScriptTargetSetting, dropScriptTargetSetting, scriptsAction );
final List<GenerationTarget> targets = Arrays.asList( databaseTarget, scriptsTarget );
// finally, do the generation
try {
doGeneration( createSourceList, dropSourceList, targets );
} }
else { finally {
target = SchemaGenTarget.DATABASE; releaseTargets( targets );
releaseSources( createSourceList );
releaseSources( dropSourceList );
} }
} }
finally {
if ( target == SchemaGenTarget.DATABASE || target == SchemaGenTarget.BOTH ) { releaseJdbcConnectionContext( jdbcConnectionContext );
generationTargetList.add( new DatabaseTarget( jdbcConnectionContext ) );
}
if ( target == SchemaGenTarget.SCRIPTS || target == SchemaGenTarget.BOTH ) {
// both create and drop scripts are expected per JPA spec
if ( createScriptTargetSetting == null ) {
throw new IllegalArgumentException( "For schema generation creation script target missing" );
}
if ( dropScriptTargetSetting == null ) {
throw new IllegalArgumentException( "For schema generation drop script target missing" );
}
generationTargetList.add( new ScriptsTarget( createScriptTargetSetting, dropScriptTargetSetting ) );
} }
}
private static List<GenerationSource> buildCreateSourceList(
// determine sources ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Configuration hibernateConfiguration,
ServiceRegistry serviceRegistry,
Dialect dialect) {
final List<GenerationSource> generationSourceList = new ArrayList<GenerationSource>(); final List<GenerationSource> generationSourceList = new ArrayList<GenerationSource>();
SchemaGenSource source = SchemaGenSource.interpret(
hibernateConfiguration.getProperty( AvailableSettings.SCHEMA_GEN_SOURCE )
);
// the default for sources is dependent upon whether script sources were specified...
final Object createScriptSourceSetting = hibernateConfiguration.getProperties().get(
AvailableSettings.SCHEMA_GEN_CREATE_SCRIPT_SOURCE
);
final Object dropScriptSourceSetting = hibernateConfiguration.getProperties().get(
AvailableSettings.SCHEMA_GEN_DROP_SCRIPT_SOURCE
);
if ( source == null ) {
if ( createScriptSourceSetting != null && dropScriptSourceSetting != null ) {
source = SchemaGenSource.SCRIPTS;
}
else {
source = SchemaGenSource.METADATA;
}
}
final boolean createSchemas = ConfigurationHelper.getBoolean( final boolean createSchemas = ConfigurationHelper.getBoolean(
AvailableSettings.SCHEMA_GEN_CREATE_SCHEMAS, AvailableSettings.SCHEMA_GEN_CREATE_SCHEMAS,
hibernateConfiguration.getProperties(), hibernateConfiguration.getProperties(),
false false
); );
if ( createSchemas ) { if ( createSchemas ) {
// todo : does it make sense to generate schema(s) defined in metadata if only script sources are to be used?
generationSourceList.add( new CreateSchemaCommandSource( hibernateConfiguration, dialect ) ); generationSourceList.add( new CreateSchemaCommandSource( hibernateConfiguration, dialect ) );
} }
if ( source == SchemaGenSource.METADATA ) { SchemaGenSource sourceType = SchemaGenSource.interpret(
generationSourceList.add( new MetadataSource( hibernateConfiguration, dialect ) ); hibernateConfiguration.getProperty( AvailableSettings.SCHEMA_GEN_CREATE_SOURCE )
);
final Object createScriptSourceSetting = hibernateConfiguration.getProperties().get(
AvailableSettings.SCHEMA_GEN_CREATE_SCRIPT_SOURCE
);
if ( sourceType == null ) {
if ( createScriptSourceSetting != null ) {
sourceType = SchemaGenSource.SCRIPTS;
}
else {
sourceType = SchemaGenSource.METADATA;
}
} }
else if ( source == SchemaGenSource.SCRIPTS ) {
generationSourceList.add( new ScriptSource( createScriptSourceSetting, dropScriptSourceSetting, scriptCommandExtractor ) ); final ImportSqlCommandExtractor scriptCommandExtractor = serviceRegistry.getService( ImportSqlCommandExtractor.class );
if ( sourceType == SchemaGenSource.METADATA ) {
generationSourceList.add( new MetadataSource( hibernateConfiguration, dialect, true ) );
} }
else if ( source == SchemaGenSource.METADATA_THEN_SCRIPTS ) { else if ( sourceType == SchemaGenSource.SCRIPTS ) {
generationSourceList.add( new MetadataSource( hibernateConfiguration, dialect ) ); generationSourceList.add( new ScriptSource( createScriptSourceSetting, scriptCommandExtractor ) );
generationSourceList.add( new ScriptSource( createScriptSourceSetting, dropScriptSourceSetting, scriptCommandExtractor ) );
} }
else if ( source == SchemaGenSource.SCRIPTS_THEN_METADATA ) { else if ( sourceType == SchemaGenSource.METADATA_THEN_SCRIPTS ) {
generationSourceList.add( new ScriptSource( createScriptSourceSetting, dropScriptSourceSetting, scriptCommandExtractor ) ); generationSourceList.add( new MetadataSource( hibernateConfiguration, dialect, true ) );
generationSourceList.add( new MetadataSource( hibernateConfiguration, dialect ) ); generationSourceList.add( new ScriptSource( createScriptSourceSetting, scriptCommandExtractor ) );
}
else if ( sourceType == SchemaGenSource.SCRIPTS_THEN_METADATA ) {
generationSourceList.add( new ScriptSource( createScriptSourceSetting, scriptCommandExtractor ) );
generationSourceList.add( new MetadataSource( hibernateConfiguration, dialect, true ) );
} }
final Object importScriptSetting = hibernateConfiguration.getProperties().get( final Object importScriptSetting = hibernateConfiguration.getProperties().get(
@ -181,16 +188,51 @@ public class JpaSchemaGenerator {
generationSourceList.add( new ImportScriptSource( importScriptSetting, scriptCommandExtractor ) ); generationSourceList.add( new ImportScriptSource( importScriptSetting, scriptCommandExtractor ) );
} }
return generationSourceList;
// do the generation ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
try {
doGeneration( action, generationSourceList, generationTargetList );
}
finally {
releaseResources( generationSourceList, generationTargetList, jdbcConnectionContext );
}
} }
private static List<GenerationSource> buildDropSourceList(
Configuration hibernateConfiguration,
ServiceRegistry serviceRegistry,
Dialect dialect) {
final List<GenerationSource> generationSourceList = new ArrayList<GenerationSource>();
SchemaGenSource sourceType = SchemaGenSource.interpret(
hibernateConfiguration.getProperty( AvailableSettings.SCHEMA_GEN_DROP_SOURCE )
);
final Object dropScriptSourceSetting = hibernateConfiguration.getProperties().get(
AvailableSettings.SCHEMA_GEN_DROP_SCRIPT_SOURCE
);
if ( sourceType == null ) {
if ( dropScriptSourceSetting != null ) {
sourceType = SchemaGenSource.SCRIPTS;
}
else {
sourceType = SchemaGenSource.METADATA;
}
}
final ImportSqlCommandExtractor scriptCommandExtractor = serviceRegistry.getService( ImportSqlCommandExtractor.class );
if ( sourceType == SchemaGenSource.METADATA ) {
generationSourceList.add( new MetadataSource( hibernateConfiguration, dialect, false ) );
}
else if ( sourceType == SchemaGenSource.SCRIPTS ) {
generationSourceList.add( new ScriptSource( dropScriptSourceSetting, scriptCommandExtractor ) );
}
else if ( sourceType == SchemaGenSource.METADATA_THEN_SCRIPTS ) {
generationSourceList.add( new MetadataSource( hibernateConfiguration, dialect, false ) );
generationSourceList.add( new ScriptSource( dropScriptSourceSetting, scriptCommandExtractor ) );
}
else if ( sourceType == SchemaGenSource.SCRIPTS_THEN_METADATA ) {
generationSourceList.add( new ScriptSource( dropScriptSourceSetting, scriptCommandExtractor ) );
generationSourceList.add( new MetadataSource( hibernateConfiguration, dialect, false ) );
}
return generationSourceList;
}
private static JdbcConnectionContext determineAppropriateJdbcConnectionContext( private static JdbcConnectionContext determineAppropriateJdbcConnectionContext(
Configuration hibernateConfiguration, Configuration hibernateConfiguration,
@ -285,44 +327,83 @@ public class JpaSchemaGenerator {
); );
} }
return serviceRegistry.getService( JdbcServices.class ).getDialect(); return buildDialect( hibernateConfiguration, serviceRegistry, jdbcConnectionContext );
}
private static Dialect buildDialect(
Configuration hibernateConfiguration,
ServiceRegistry serviceRegistry,
JdbcConnectionContext jdbcConnectionContext) {
// todo : a lot of copy/paste from the DialectFactory impl...
final String dialectName = hibernateConfiguration.getProperty( org.hibernate.cfg.AvailableSettings.DIALECT );
if ( dialectName != null ) {
return constructDialect( dialectName, serviceRegistry );
}
else {
return determineDialectBasedOnJdbcMetadata( jdbcConnectionContext, serviceRegistry );
}
}
private static Dialect constructDialect(String dialectName, ServiceRegistry serviceRegistry) {
final Dialect dialect;
final StrategySelector strategySelector = serviceRegistry.getService( StrategySelector.class );
try {
dialect = strategySelector.resolveStrategy( Dialect.class, dialectName );
if ( dialect == null ) {
throw new HibernateException( "Unable to construct requested dialect [" + dialectName + "]" );
}
return dialect;
}
catch (HibernateException e) {
throw e;
}
catch (Exception e) {
throw new HibernateException( "Unable to construct requested dialect [" + dialectName+ "]", e );
}
}
private static Dialect determineDialectBasedOnJdbcMetadata(
JdbcConnectionContext jdbcConnectionContext,
ServiceRegistry serviceRegistry) {
DialectResolver dialectResolver = serviceRegistry.getService( DialectResolver.class );
try {
final DatabaseMetaData databaseMetaData = jdbcConnectionContext.getJdbcConnection().getMetaData();
final Dialect dialect = dialectResolver.resolveDialect( databaseMetaData );
if ( dialect == null ) {
throw new HibernateException(
"Unable to determine Dialect to use [name=" + databaseMetaData.getDatabaseProductName() +
", majorVersion=" + databaseMetaData.getDatabaseMajorVersion() +
"]; user must register resolver or explicitly set 'hibernate.dialect'"
);
}
return dialect;
}
catch ( SQLException sqlException ) {
throw new HibernateException(
"Unable to access java.sql.DatabaseMetaData to determine appropriate Dialect to use",
sqlException
);
}
} }
private static void doGeneration( private static void doGeneration(
SchemaGenAction action, List<GenerationSource> createSourceList,
List<GenerationSource> generationSourceList, List<GenerationSource> dropSourceList,
List<GenerationTarget> generationTargetList) { List<GenerationTarget> targets) {
for ( GenerationTarget target : targets ) {
for ( GenerationSource source : generationSourceList ) { for ( GenerationSource source : createSourceList ) {
if ( action.includesCreate() ) { target.acceptCreateCommands( source.getCommands() );
final Iterable<String> createCommands = source.getCreateCommands();
for ( GenerationTarget target : generationTargetList ) {
target.acceptCreateCommands( createCommands );
}
} }
if ( action.includesDrop() ) { for ( GenerationSource source : dropSourceList ) {
final Iterable<String> dropCommands = source.getDropCommands(); target.acceptDropCommands( source.getCommands() );
for ( GenerationTarget target : generationTargetList ) {
target.acceptDropCommands( dropCommands );
}
} }
} }
} }
private static void releaseResources( private static void releaseSources(List<GenerationSource> generationSourceList ) {
List<GenerationSource> generationSourceList,
List<GenerationTarget> generationTargetList,
JdbcConnectionContext jdbcConnectionContext) {
for ( GenerationTarget target : generationTargetList ) {
try {
target.release();
}
catch (Exception e) {
log.debug( "Problem releasing generation target : " + e.toString() );
}
}
for ( GenerationSource source : generationSourceList ) { for ( GenerationSource source : generationSourceList ) {
try { try {
source.release(); source.release();
@ -331,7 +412,20 @@ public class JpaSchemaGenerator {
log.debug( "Problem releasing generation source : " + e.toString() ); log.debug( "Problem releasing generation source : " + e.toString() );
} }
} }
}
private static void releaseTargets(List<GenerationTarget> generationTargetList) {
for ( GenerationTarget target : generationTargetList ) {
try {
target.release();
}
catch (Exception e) {
log.debug( "Problem releasing generation target : " + e.toString() );
}
}
}
private static void releaseJdbcConnectionContext(JdbcConnectionContext jdbcConnectionContext) {
try { try {
jdbcConnectionContext.release(); jdbcConnectionContext.release();
} }
@ -385,15 +479,10 @@ public class JpaSchemaGenerator {
} }
@Override @Override
public Iterable<String> getCreateCommands() { public Iterable<String> getCommands() {
return commands; return commands;
} }
@Override
public Iterable<String> getDropCommands() {
return Collections.emptyList();
}
@Override @Override
public void release() { public void release() {
// nothing to do // nothing to do
@ -416,18 +505,14 @@ public class JpaSchemaGenerator {
} }
@Override @Override
public Iterable<String> getCreateCommands() { public Iterable<String> getCommands() {
return sourceReader.read( scriptCommandExtractor ); return sourceReader.read( scriptCommandExtractor );
} }
@Override
public Iterable<String> getDropCommands() {
return Collections.emptyList();
}
@Override @Override
public void release() { public void release() {
sourceReader.release(); sourceReader.release();
} }
} }
} }

View File

@ -34,20 +34,19 @@ import org.hibernate.dialect.Dialect;
public class MetadataSource implements GenerationSource { public class MetadataSource implements GenerationSource {
private final Configuration hibernateConfiguration; private final Configuration hibernateConfiguration;
private final Dialect dialect; private final Dialect dialect;
private final boolean creation;
public MetadataSource(Configuration hibernateConfiguration, Dialect dialect) { public MetadataSource(Configuration hibernateConfiguration, Dialect dialect, boolean creation) {
this.hibernateConfiguration = hibernateConfiguration; this.hibernateConfiguration = hibernateConfiguration;
this.dialect = dialect; this.dialect = dialect;
this.creation = creation;
} }
@Override @Override
public Iterable<String> getCreateCommands() { public Iterable<String> getCommands() {
return Arrays.asList( hibernateConfiguration.generateSchemaCreationScript( dialect ) ); return creation
} ? Arrays.asList( hibernateConfiguration.generateSchemaCreationScript( dialect ) )
: Arrays.asList( hibernateConfiguration.generateDropSchemaScript( dialect ) );
@Override
public Iterable<String> getDropCommands() {
return Arrays.asList( hibernateConfiguration.generateDropSchemaScript( dialect ) );
} }
@Override @Override

View File

@ -23,14 +23,7 @@
*/ */
package org.hibernate.jpa.internal.schemagen; package org.hibernate.jpa.internal.schemagen;
import javax.persistence.PersistenceException;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader; import java.io.Reader;
import java.io.Writer;
import org.jboss.logging.Logger;
import org.hibernate.tool.hbm2ddl.ImportSqlCommandExtractor; import org.hibernate.tool.hbm2ddl.ImportSqlCommandExtractor;
@ -38,47 +31,28 @@ import org.hibernate.tool.hbm2ddl.ImportSqlCommandExtractor;
* @author Steve Ebersole * @author Steve Ebersole
*/ */
public class ScriptSource implements GenerationSource { public class ScriptSource implements GenerationSource {
private static final Logger log = Logger.getLogger( ScriptSource.class ); private final SqlScriptReader reader;
private final SqlScriptReader createSource;
private final SqlScriptReader dropSource;
private final ImportSqlCommandExtractor scriptCommandExtractor; private final ImportSqlCommandExtractor scriptCommandExtractor;
public ScriptSource( public ScriptSource(Object scriptSourceSetting, ImportSqlCommandExtractor scriptCommandExtractor) {
Object createScriptSourceSetting,
Object dropScriptSourceSetting,
ImportSqlCommandExtractor scriptCommandExtractor) {
this.scriptCommandExtractor = scriptCommandExtractor; this.scriptCommandExtractor = scriptCommandExtractor;
if ( Reader.class.isInstance( createScriptSourceSetting ) ) { if ( Reader.class.isInstance( scriptSourceSetting ) ) {
createSource = new ReaderScriptSource( (Reader) createScriptSourceSetting ); reader = new ReaderScriptSource( (Reader) scriptSourceSetting );
} }
else { else {
createSource = new FileScriptSource( createScriptSourceSetting.toString() ); reader = new FileScriptSource( scriptSourceSetting.toString() );
}
if ( Writer.class.isInstance( dropScriptSourceSetting ) ) {
dropSource = new ReaderScriptSource( (Reader) dropScriptSourceSetting );
}
else {
dropSource = new FileScriptSource( dropScriptSourceSetting.toString() );
} }
} }
@Override @Override
public Iterable<String> getCreateCommands() { public Iterable<String> getCommands() {
return createSource.read( scriptCommandExtractor ); return reader.read( scriptCommandExtractor );
}
@Override
public Iterable<String> getDropCommands() {
return dropSource.read( scriptCommandExtractor );
} }
@Override @Override
public void release() { public void release() {
createSource.release(); reader.release();
dropSource.release();
} }
} }

View File

@ -31,12 +31,12 @@ import java.io.Writer;
import org.jboss.logging.Logger; import org.jboss.logging.Logger;
import org.hibernate.jpa.AvailableSettings;
import org.hibernate.jpa.SchemaGenAction;
/** /**
* GenerationTarget implementation for handling generation to scripts * GenerationTarget implementation for handling generation to scripts
* *
* @see org.hibernate.jpa.SchemaGenTarget#SCRIPTS
* @see org.hibernate.jpa.SchemaGenTarget#BOTH
*
* @author Steve Ebersole * @author Steve Ebersole
*/ */
class ScriptsTarget implements GenerationTarget { class ScriptsTarget implements GenerationTarget {
@ -44,25 +44,63 @@ class ScriptsTarget implements GenerationTarget {
private final ScriptTargetTarget createScriptTarget; private final ScriptTargetTarget createScriptTarget;
private final ScriptTargetTarget dropScriptTarget; private final ScriptTargetTarget dropScriptTarget;
private final SchemaGenAction scriptsAction;
public ScriptsTarget(Object createScriptTargetSetting, Object dropScriptTargetSetting) { public ScriptsTarget(
if ( Writer.class.isInstance( createScriptTargetSetting ) ) { Object createScriptTargetSetting,
createScriptTarget = new WriterScriptTarget( (Writer) createScriptTargetSetting ); Object dropScriptTargetSetting,
SchemaGenAction scriptsAction) {
this.scriptsAction = scriptsAction;
if ( scriptsAction.includesCreate() ) {
if ( Writer.class.isInstance( createScriptTargetSetting ) ) {
createScriptTarget = new WriterScriptTarget( (Writer) createScriptTargetSetting );
}
else {
createScriptTarget = new FileScriptTarget( createScriptTargetSetting.toString() );
}
} }
else { else {
createScriptTarget = new FileScriptTarget( createScriptTargetSetting.toString() ); if ( createScriptTargetSetting != null ) {
// the wording in the spec hints that this maybe should be an error, but does not explicitly
// call out an exception to use.
log.debugf(
"Value was specified for '%s' [%s], but create scripting was not requested",
AvailableSettings.SCHEMA_GEN_SCRIPTS_CREATE_TARGET,
createScriptTargetSetting
);
}
createScriptTarget = null;
} }
if ( Writer.class.isInstance( dropScriptTargetSetting ) ) { if ( scriptsAction.includesDrop() ) {
dropScriptTarget = new WriterScriptTarget( (Writer) dropScriptTargetSetting ); if ( Writer.class.isInstance( dropScriptTargetSetting ) ) {
dropScriptTarget = new WriterScriptTarget( (Writer) dropScriptTargetSetting );
}
else {
dropScriptTarget = new FileScriptTarget( dropScriptTargetSetting.toString() );
}
} }
else { else {
dropScriptTarget = new FileScriptTarget( dropScriptTargetSetting.toString() ); if ( dropScriptTargetSetting != null ) {
// the wording in the spec hints that this maybe should be an error, but does not explicitly
// call out an exception to use.
log.debugf(
"Value was specified for '%s' [%s], but drop scripting was not requested",
AvailableSettings.SCHEMA_GEN_SCRIPTS_DROP_TARGET,
dropScriptTargetSetting
);
}
dropScriptTarget = null;
} }
} }
@Override @Override
public void acceptCreateCommands(Iterable<String> commands) { public void acceptCreateCommands(Iterable<String> commands) {
if ( ! scriptsAction.includesCreate() ) {
return;
}
for ( String command : commands ) { for ( String command : commands ) {
createScriptTarget.accept( command ); createScriptTarget.accept( command );
} }
@ -70,6 +108,10 @@ class ScriptsTarget implements GenerationTarget {
@Override @Override
public void acceptDropCommands(Iterable<String> commands) { public void acceptDropCommands(Iterable<String> commands) {
if ( ! scriptsAction.includesDrop() ) {
return;
}
for ( String command : commands ) { for ( String command : commands ) {
dropScriptTarget.accept( command ); dropScriptTarget.accept( command );
} }