/*
 * 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.tool.schema.internal;

import java.sql.Connection;
import java.util.Map;

import org.hibernate.boot.registry.selector.spi.StrategySelector;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.dialect.Dialect;
import org.hibernate.engine.jdbc.connections.spi.JdbcConnectionAccess;
import org.hibernate.engine.jdbc.dialect.spi.DialectResolutionInfo;
import org.hibernate.engine.jdbc.dialect.spi.DialectResolver;
import org.hibernate.engine.jdbc.spi.JdbcServices;
import org.hibernate.engine.jdbc.spi.SqlExceptionHelper;
import org.hibernate.engine.jdbc.spi.SqlStatementLogger;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.internal.util.config.ConfigurationHelper;
import org.hibernate.resource.transaction.spi.DdlTransactionIsolator;
import org.hibernate.resource.transaction.spi.TransactionCoordinatorBuilder;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.service.spi.ServiceRegistryAwareService;
import org.hibernate.service.spi.ServiceRegistryImplementor;
import org.hibernate.tool.schema.JdbcMetadaAccessStrategy;
import org.hibernate.tool.schema.TargetType;
import org.hibernate.tool.schema.internal.exec.GenerationTarget;
import org.hibernate.tool.schema.internal.exec.GenerationTargetToDatabase;
import org.hibernate.tool.schema.internal.exec.GenerationTargetToScript;
import org.hibernate.tool.schema.internal.exec.GenerationTargetToStdout;
import org.hibernate.tool.schema.internal.exec.JdbcConnectionAccessProvidedConnectionImpl;
import org.hibernate.tool.schema.internal.exec.JdbcContext;
import org.hibernate.tool.schema.spi.SchemaCreator;
import org.hibernate.tool.schema.spi.SchemaDropper;
import org.hibernate.tool.schema.spi.SchemaFilterProvider;
import org.hibernate.tool.schema.spi.SchemaManagementException;
import org.hibernate.tool.schema.spi.SchemaManagementTool;
import org.hibernate.tool.schema.spi.SchemaMigrator;
import org.hibernate.tool.schema.spi.SchemaValidator;
import org.hibernate.tool.schema.spi.TargetDescriptor;

import org.jboss.logging.Logger;

import static org.hibernate.cfg.AvailableSettings.HBM2DDL_CONNECTION;
import static org.hibernate.cfg.AvailableSettings.HBM2DDL_DELIMITER;

The standard Hibernate implementation for performing schema management.
Author:Steve Ebersole
/** * The standard Hibernate implementation for performing schema management. * * @author Steve Ebersole */
public class HibernateSchemaManagementTool implements SchemaManagementTool, ServiceRegistryAwareService { private static final Logger log = Logger.getLogger( HibernateSchemaManagementTool.class ); private ServiceRegistry serviceRegistry; @Override public void injectServices(ServiceRegistryImplementor serviceRegistry) { this.serviceRegistry = serviceRegistry; } @Override public SchemaCreator getSchemaCreator(Map options) { return new SchemaCreatorImpl( this, getSchemaFilterProvider( options ).getCreateFilter() ); } @Override public SchemaDropper getSchemaDropper(Map options) { return new SchemaDropperImpl( this, getSchemaFilterProvider( options ).getDropFilter() ); } @Override public SchemaMigrator getSchemaMigrator(Map options) { if ( determineJdbcMetadaAccessStrategy( options ) == JdbcMetadaAccessStrategy.GROUPED ) { return new GroupedSchemaMigratorImpl( this, getSchemaFilterProvider( options ).getMigrateFilter() ); } else { return new IndividuallySchemaMigratorImpl( this, getSchemaFilterProvider( options ).getMigrateFilter() ); } } @Override public SchemaValidator getSchemaValidator(Map options) { if ( determineJdbcMetadaAccessStrategy( options ) == JdbcMetadaAccessStrategy.GROUPED ) { return new GroupedSchemaValidatorImpl( this, getSchemaFilterProvider( options ).getValidateFilter() ); } else { return new IndividuallySchemaValidatorImpl( this, getSchemaFilterProvider( options ).getValidateFilter() ); } } private SchemaFilterProvider getSchemaFilterProvider(Map options) { final Object configuredOption = (options == null) ? null : options.get( AvailableSettings.HBM2DDL_FILTER_PROVIDER ); return serviceRegistry.getService( StrategySelector.class ).resolveDefaultableStrategy( SchemaFilterProvider.class, configuredOption, DefaultSchemaFilterProvider.INSTANCE ); } private JdbcMetadaAccessStrategy determineJdbcMetadaAccessStrategy(Map options) { return JdbcMetadaAccessStrategy.interpretSetting( options ); } GenerationTarget[] buildGenerationTargets( TargetDescriptor targetDescriptor, JdbcContext jdbcContext, Map options, boolean needsAutoCommit) { final String scriptDelimiter = ConfigurationHelper.getString( HBM2DDL_DELIMITER, options ); final GenerationTarget[] targets = new GenerationTarget[ targetDescriptor.getTargetTypes().size() ]; int index = 0; if ( targetDescriptor.getTargetTypes().contains( TargetType.STDOUT ) ) { targets[index] = new GenerationTargetToStdout( scriptDelimiter ); index++; } if ( targetDescriptor.getTargetTypes().contains( TargetType.SCRIPT ) ) { if ( targetDescriptor.getScriptTargetOutput() == null ) { throw new SchemaManagementException( "Writing to script was requested, but no script file was specified" ); } targets[index] = new GenerationTargetToScript( targetDescriptor.getScriptTargetOutput(), scriptDelimiter ); index++; } if ( targetDescriptor.getTargetTypes().contains( TargetType.DATABASE ) ) { targets[index] = new GenerationTargetToDatabase( getDdlTransactionIsolator( jdbcContext ), true ); } return targets; } GenerationTarget[] buildGenerationTargets( TargetDescriptor targetDescriptor, DdlTransactionIsolator ddlTransactionIsolator, Map options) { final String scriptDelimiter = ConfigurationHelper.getString( HBM2DDL_DELIMITER, options ); final GenerationTarget[] targets = new GenerationTarget[ targetDescriptor.getTargetTypes().size() ]; int index = 0; if ( targetDescriptor.getTargetTypes().contains( TargetType.STDOUT ) ) { targets[index] = new GenerationTargetToStdout( scriptDelimiter ); index++; } if ( targetDescriptor.getTargetTypes().contains( TargetType.SCRIPT ) ) { if ( targetDescriptor.getScriptTargetOutput() == null ) { throw new SchemaManagementException( "Writing to script was requested, but no script file was specified" ); } targets[index] = new GenerationTargetToScript( targetDescriptor.getScriptTargetOutput(), scriptDelimiter ); index++; } if ( targetDescriptor.getTargetTypes().contains( TargetType.DATABASE ) ) { targets[index] = new GenerationTargetToDatabase( ddlTransactionIsolator, false ); } return targets; } public DdlTransactionIsolator getDdlTransactionIsolator(JdbcContext jdbcContext) { if ( jdbcContext.getJdbcConnectionAccess() instanceof JdbcConnectionAccessProvidedConnectionImpl ) { return new DdlTransactionIsolatorProvidedConnectionImpl( jdbcContext ); } return serviceRegistry.getService( TransactionCoordinatorBuilder.class ).buildDdlTransactionIsolator( jdbcContext ); } public JdbcContext resolveJdbcContext(Map configurationValues) { final JdbcContextBuilder jdbcContextBuilder = new JdbcContextBuilder( serviceRegistry ); // see if a specific connection has been provided final Connection providedConnection = (Connection) configurationValues.get( HBM2DDL_CONNECTION ); if ( providedConnection != null ) { jdbcContextBuilder.jdbcConnectionAccess = new JdbcConnectionAccessProvidedConnectionImpl( providedConnection ); } // see if a specific Dialect override has been provided... final String explicitDbName = (String) configurationValues.get( AvailableSettings.HBM2DDL_DB_NAME ); if ( StringHelper.isNotEmpty( explicitDbName ) ) { final String explicitDbMajor = (String) configurationValues.get( AvailableSettings.HBM2DDL_DB_MAJOR_VERSION ); final String explicitDbMinor = (String) configurationValues.get( AvailableSettings.HBM2DDL_DB_MINOR_VERSION ); final Dialect indicatedDialect = serviceRegistry.getService( DialectResolver.class ).resolveDialect( new DialectResolutionInfo() { @Override public String getDatabaseName() { return explicitDbName; } @Override public int getDatabaseMajorVersion() { return StringHelper.isEmpty( explicitDbMajor ) ? NO_VERSION : Integer.parseInt( explicitDbMajor ); } @Override public int getDatabaseMinorVersion() { return StringHelper.isEmpty( explicitDbMinor ) ? NO_VERSION : Integer.parseInt( explicitDbMinor ); } @Override public String getDriverName() { return null; } @Override public int getDriverMajorVersion() { return NO_VERSION; } @Override public int getDriverMinorVersion() { return NO_VERSION; } } ); if ( indicatedDialect == null ) { log.debugf( "Unable to resolve indicated Dialect resolution info (%s, %s, %s)", explicitDbName, explicitDbMajor, explicitDbMinor ); } else { jdbcContextBuilder.dialect = indicatedDialect; } } return jdbcContextBuilder.buildJdbcContext(); } public ServiceRegistry getServiceRegistry() { return serviceRegistry; } private static class JdbcContextBuilder { private final ServiceRegistry serviceRegistry; private final SqlStatementLogger sqlStatementLogger; private final SqlExceptionHelper sqlExceptionHelper; private JdbcConnectionAccess jdbcConnectionAccess; private Dialect dialect; public JdbcContextBuilder(ServiceRegistry serviceRegistry) { this.serviceRegistry = serviceRegistry; final JdbcServices jdbcServices = serviceRegistry.getService( JdbcServices.class ); this.sqlStatementLogger = jdbcServices.getSqlStatementLogger(); this.sqlExceptionHelper = jdbcServices.getSqlExceptionHelper(); this.dialect = jdbcServices.getJdbcEnvironment().getDialect(); this.jdbcConnectionAccess = jdbcServices.getBootstrapJdbcConnectionAccess(); } public JdbcContext buildJdbcContext() { return new JdbcContextImpl( jdbcConnectionAccess, dialect, sqlStatementLogger, sqlExceptionHelper, serviceRegistry ); } } public static class JdbcContextImpl implements JdbcContext { private final JdbcConnectionAccess jdbcConnectionAccess; private final Dialect dialect; private final SqlStatementLogger sqlStatementLogger; private final SqlExceptionHelper sqlExceptionHelper; private final ServiceRegistry serviceRegistry; private JdbcContextImpl( JdbcConnectionAccess jdbcConnectionAccess, Dialect dialect, SqlStatementLogger sqlStatementLogger, SqlExceptionHelper sqlExceptionHelper, ServiceRegistry serviceRegistry) { this.jdbcConnectionAccess = jdbcConnectionAccess; this.dialect = dialect; this.sqlStatementLogger = sqlStatementLogger; this.sqlExceptionHelper = sqlExceptionHelper; this.serviceRegistry = serviceRegistry; } @Override public JdbcConnectionAccess getJdbcConnectionAccess() { return jdbcConnectionAccess; } @Override public Dialect getDialect() { return dialect; } @Override public SqlStatementLogger getSqlStatementLogger() { return sqlStatementLogger; } @Override public SqlExceptionHelper getSqlExceptionHelper() { return sqlExceptionHelper; } @Override public ServiceRegistry getServiceRegistry() { return serviceRegistry; } } }