/*
 * Hibernate, Relational Persistence for Idiomatic Java
 *
 * Copyright (c) 2008-2011, 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.tool.hbm2ddl;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.Writer;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.SQLWarning;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

import org.hibernate.HibernateException;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.boot.registry.internal.StandardServiceRegistryImpl;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.cfg.Configuration;
import org.hibernate.cfg.Environment;
import org.hibernate.cfg.NamingStrategy;
import org.hibernate.cfg.naming.NamingStrategyDelegator;
import org.hibernate.dialect.Dialect;
import org.hibernate.engine.config.spi.ConfigurationService;
import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider;
import org.hibernate.engine.jdbc.internal.FormatStyle;
import org.hibernate.engine.jdbc.internal.Formatter;
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.CoreMessageLogger;
import org.hibernate.internal.util.ConfigHelper;
import org.hibernate.internal.util.ReflectHelper;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.internal.util.config.ConfigurationHelper;
import org.hibernate.metamodel.source.MetadataImplementor;
import org.hibernate.service.ServiceRegistry;

import org.jboss.logging.Logger;

Commandline tool to export table schema to the database. This class may also be called from inside an application.
Author:Daniel Bradby, Gavin King, Steve Ebersole
/** * Commandline tool to export table schema to the database. This class may also be called from inside an application. * * @author Daniel Bradby * @author Gavin King * @author Steve Ebersole */
public class SchemaExport { private static final CoreMessageLogger LOG = Logger.getMessageLogger(CoreMessageLogger.class, SchemaExport.class.getName()); private static final String DEFAULT_IMPORT_FILE = "/import.sql"; public static enum Type { CREATE, DROP, NONE, BOTH; public boolean doCreate() { return this == BOTH || this == CREATE; } public boolean doDrop() { return this == BOTH || this == DROP; } } private final ConnectionHelper connectionHelper; private final SqlStatementLogger sqlStatementLogger; private final SqlExceptionHelper sqlExceptionHelper; private final String[] dropSQL; private final String[] createSQL; private final String importFiles; private final List<Exception> exceptions = new ArrayList<Exception>(); private Formatter formatter; private ImportSqlCommandExtractor importSqlCommandExtractor = ImportSqlCommandExtractorInitiator.DEFAULT_EXTRACTOR; private String outputFile; private String delimiter; private boolean haltOnError; public SchemaExport(ServiceRegistry serviceRegistry, Configuration configuration) { this.connectionHelper = new SuppliedConnectionProviderConnectionHelper( serviceRegistry.getService( ConnectionProvider.class ) ); this.sqlStatementLogger = serviceRegistry.getService( JdbcServices.class ).getSqlStatementLogger(); this.formatter = ( sqlStatementLogger.isFormat() ? FormatStyle.DDL : FormatStyle.NONE ).getFormatter(); this.sqlExceptionHelper = serviceRegistry.getService( JdbcServices.class ).getSqlExceptionHelper(); this.importFiles = ConfigurationHelper.getString( AvailableSettings.HBM2DDL_IMPORT_FILES, configuration.getProperties(), DEFAULT_IMPORT_FILE ); final Dialect dialect = serviceRegistry.getService( JdbcServices.class ).getDialect(); this.dropSQL = configuration.generateDropSchemaScript( dialect ); this.createSQL = configuration.generateSchemaCreationScript( dialect ); } public SchemaExport(MetadataImplementor metadata) { ServiceRegistry serviceRegistry = metadata.getServiceRegistry(); this.connectionHelper = new SuppliedConnectionProviderConnectionHelper( serviceRegistry.getService( ConnectionProvider.class ) ); JdbcServices jdbcServices = serviceRegistry.getService( JdbcServices.class ); this.sqlStatementLogger = jdbcServices.getSqlStatementLogger(); this.formatter = ( sqlStatementLogger.isFormat() ? FormatStyle.DDL : FormatStyle.NONE ).getFormatter(); this.sqlExceptionHelper = jdbcServices.getSqlExceptionHelper(); this.importFiles = ConfigurationHelper.getString( AvailableSettings.HBM2DDL_IMPORT_FILES, serviceRegistry.getService( ConfigurationService.class ).getSettings(), DEFAULT_IMPORT_FILE ); final Dialect dialect = jdbcServices.getDialect(); this.dropSQL = metadata.getDatabase().generateDropSchemaScript( dialect ); this.createSQL = metadata.getDatabase().generateSchemaCreationScript( dialect ); }
Create a schema exporter for the given Configuration
Params:
  • configuration – The configuration from which to build a schema export.
Throws:
/** * Create a schema exporter for the given Configuration * * @param configuration The configuration from which to build a schema export. * @throws HibernateException Indicates problem preparing for schema export. */
public SchemaExport(Configuration configuration) { this( configuration, configuration.getProperties() ); }
Create a schema exporter for the given Configuration, with the given database connection properties.
Params:
  • configuration – The configuration from which to build a schema export.
  • properties – The properties from which to configure connectivity etc.
Throws:
Deprecated:properties may be specified via the Configuration object
/** * Create a schema exporter for the given Configuration, with the given * database connection properties. * * @param configuration The configuration from which to build a schema export. * @param properties The properties from which to configure connectivity etc. * @throws HibernateException Indicates problem preparing for schema export. * * @deprecated properties may be specified via the Configuration object */
@Deprecated public SchemaExport(Configuration configuration, Properties properties) throws HibernateException { final Dialect dialect = Dialect.getDialect( properties ); Properties props = new Properties(); props.putAll( dialect.getDefaultProperties() ); props.putAll( properties ); this.connectionHelper = new ManagedProviderConnectionHelper( props ); this.sqlStatementLogger = new SqlStatementLogger( false, true ); this.formatter = FormatStyle.DDL.getFormatter(); this.sqlExceptionHelper = new SqlExceptionHelper(); this.importFiles = ConfigurationHelper.getString( AvailableSettings.HBM2DDL_IMPORT_FILES, properties, DEFAULT_IMPORT_FILE ); this.dropSQL = configuration.generateDropSchemaScript( dialect ); this.createSQL = configuration.generateSchemaCreationScript( dialect ); }
Create a schema exporter for the given Configuration, using the supplied connection for connectivity.
Params:
  • configuration – The configuration to use.
  • connection – The JDBC connection to use.
Throws:
/** * Create a schema exporter for the given Configuration, using the supplied connection for connectivity. * * @param configuration The configuration to use. * @param connection The JDBC connection to use. * @throws HibernateException Indicates problem preparing for schema export. */
public SchemaExport(Configuration configuration, Connection connection) throws HibernateException { this.connectionHelper = new SuppliedConnectionHelper( connection ); this.sqlStatementLogger = new SqlStatementLogger( false, true ); this.formatter = FormatStyle.DDL.getFormatter(); this.sqlExceptionHelper = new SqlExceptionHelper(); this.importFiles = ConfigurationHelper.getString( AvailableSettings.HBM2DDL_IMPORT_FILES, configuration.getProperties(), DEFAULT_IMPORT_FILE ); final Dialect dialect = Dialect.getDialect( configuration.getProperties() ); this.dropSQL = configuration.generateDropSchemaScript( dialect ); this.createSQL = configuration.generateSchemaCreationScript( dialect ); } public SchemaExport( ConnectionHelper connectionHelper, String[] dropSql, String[] createSql) { this.connectionHelper = connectionHelper; this.dropSQL = dropSql; this.createSQL = createSql; this.importFiles = ""; this.sqlStatementLogger = new SqlStatementLogger( false, true ); this.sqlExceptionHelper = new SqlExceptionHelper(); this.formatter = FormatStyle.DDL.getFormatter(); }
For generating a export script file, this is the file which will be written.
Params:
  • filename – The name of the file to which to write the export script.
Returns:this
/** * For generating a export script file, this is the file which will be written. * * @param filename The name of the file to which to write the export script. * @return this */
public SchemaExport setOutputFile(String filename) { outputFile = filename; return this; }
Set the end of statement delimiter
Params:
  • delimiter – The delimiter
Returns:this
/** * Set the end of statement delimiter * * @param delimiter The delimiter * @return this */
public SchemaExport setDelimiter(String delimiter) { this.delimiter = delimiter; return this; }
Should we format the sql strings?
Params:
  • format – Should we format SQL strings
Returns:this
/** * Should we format the sql strings? * * @param format Should we format SQL strings * @return this */
public SchemaExport setFormat(boolean format) { this.formatter = ( format ? FormatStyle.DDL : FormatStyle.NONE ).getFormatter(); return this; }
Set import.sql command extractor. By default SingleLineSqlCommandExtractor is used.
Params:
  • importSqlCommandExtractor – import.sql command extractor.
Returns:this
/** * Set <i>import.sql</i> command extractor. By default {@link SingleLineSqlCommandExtractor} is used. * * @param importSqlCommandExtractor <i>import.sql</i> command extractor. * @return this */
public SchemaExport setImportSqlCommandExtractor(ImportSqlCommandExtractor importSqlCommandExtractor) { this.importSqlCommandExtractor = importSqlCommandExtractor; return this; }
Should we stop once an error occurs?
Params:
  • haltOnError – True if export should stop after error.
Returns:this
/** * Should we stop once an error occurs? * * @param haltOnError True if export should stop after error. * @return this */
public SchemaExport setHaltOnError(boolean haltOnError) { this.haltOnError = haltOnError; return this; }
Run the schema creation script; drop script is automatically executed before running the creation script.
Params:
  • script – print the DDL to the console
  • export – export the script to the database
/** * Run the schema creation script; drop script is automatically * executed before running the creation script. * * @param script print the DDL to the console * @param export export the script to the database */
public void create(boolean script, boolean export) { create( Target.interpret( script, export ) ); }
Run the schema creation script; drop script is automatically executed before running the creation script.
Params:
  • output – the target of the script.
/** * Run the schema creation script; drop script is automatically * executed before running the creation script. * * @param output the target of the script. */
public void create(Target output) { // need to drop tables before creating so need to specify Type.BOTH execute( output, Type.BOTH ); }
Run the drop schema script.
Params:
  • script – print the DDL to the console
  • export – export the script to the database
/** * Run the drop schema script. * * @param script print the DDL to the console * @param export export the script to the database */
public void drop(boolean script, boolean export) { drop( Target.interpret( script, export ) ); } public void drop(Target output) { execute( output, Type.DROP ); } public void execute(boolean script, boolean export, boolean justDrop, boolean justCreate) { execute( Target.interpret( script, export ), interpretType( justDrop, justCreate ) ); } private Type interpretType(boolean justDrop, boolean justCreate) { if ( justDrop ) { return Type.DROP; } else if ( justCreate ) { return Type.CREATE; } else { return Type.BOTH; } } public void execute(Target output, Type type) { if ( (outputFile == null && output == Target.NONE) || type == SchemaExport.Type.NONE ) { return; } exceptions.clear(); LOG.runningHbm2ddlSchemaExport(); final List<NamedReader> importFileReaders = new ArrayList<NamedReader>(); for ( String currentFile : importFiles.split(",") ) { try { final String resourceName = currentFile.trim(); InputStream stream = ConfigHelper.getResourceAsStream( resourceName ); importFileReaders.add( new NamedReader( resourceName, stream ) ); } catch ( HibernateException e ) { LOG.debugf("Import file not found: %s", currentFile); } } final List<Exporter> exporters = new ArrayList<Exporter>(); try { // prepare exporters ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ if ( output.doScript() ) { exporters.add( new ScriptExporter() ); } if ( outputFile != null ) { exporters.add( new FileExporter( outputFile ) ); } if ( output.doExport() ) { exporters.add( new DatabaseExporter( connectionHelper, sqlExceptionHelper ) ); } // perform exporters ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ if ( type.doDrop() ) { perform( dropSQL, exporters ); } if ( type.doCreate() ) { perform( createSQL, exporters ); if ( ! importFileReaders.isEmpty() ) { for ( NamedReader namedReader : importFileReaders ) { importScript( namedReader, exporters ); } } } } catch (Exception e) { exceptions.add( e ); LOG.schemaExportUnsuccessful( e ); } finally { // release exporters ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ for ( Exporter exporter : exporters ) { try { exporter.release(); } catch (Exception ignore) { } } // release the named readers from import scripts for ( NamedReader namedReader : importFileReaders ) { try { namedReader.getReader().close(); } catch (Exception ignore) { } } LOG.schemaExportComplete(); } } private void perform(String[] sqlCommands, List<Exporter> exporters) { for ( String sqlCommand : sqlCommands ) { String formatted = formatter.format( sqlCommand ); if ( delimiter != null ) { formatted += delimiter; } sqlStatementLogger.logStatement( sqlCommand, formatter ); for ( Exporter exporter : exporters ) { try { exporter.export( formatted ); } catch (Exception e) { if ( haltOnError ) { throw new HibernateException( "Error during DDL export", e ); } exceptions.add( e ); LOG.unsuccessfulCreate( sqlCommand ); LOG.error( e.getMessage() ); } } } } private void importScript(NamedReader namedReader, List<Exporter> exporters) throws Exception { BufferedReader reader = new BufferedReader( namedReader.getReader() ); String[] statements = importSqlCommandExtractor.extractCommands( reader ); if (statements != null) { for ( String statement : statements ) { if ( statement != null ) { String trimmedSql = statement.trim(); if ( trimmedSql.endsWith( ";" )) { trimmedSql = trimmedSql.substring( 0, statement.length() - 1 ); } if ( !StringHelper.isEmpty( trimmedSql ) ) { try { for ( Exporter exporter : exporters ) { if ( exporter.acceptsImportScripts() ) { exporter.export( trimmedSql ); } } } catch ( Exception e ) { if (haltOnError) { throw new ImportScriptException( "Error during statement execution (file: '" + namedReader.getName() + "'): " + trimmedSql, e ); } exceptions.add(e); LOG.unsuccessful(trimmedSql); LOG.error(e.getMessage()); } } } } } } private static class NamedReader { private final Reader reader; private final String name; public NamedReader(String name, InputStream stream) { this.name = name; this.reader = new InputStreamReader( stream ); } public Reader getReader() { return reader; } public String getName() { return name; } } private void execute(boolean script, boolean export, Writer fileOutput, Statement statement, final String sql) throws IOException, SQLException { final SqlExceptionHelper sqlExceptionHelper = new SqlExceptionHelper(); String formatted = formatter.format( sql ); if (delimiter != null) formatted += delimiter; if (script) System.out.println(formatted); LOG.debug(formatted); if ( outputFile != null ) { fileOutput.write( formatted + "\n" ); } if ( export ) { statement.executeUpdate( sql ); try { SQLWarning warnings = statement.getWarnings(); if ( warnings != null) { sqlExceptionHelper.logAndClearWarnings( connectionHelper.getConnection() ); } } catch( SQLException sqle ) { LOG.unableToLogSqlWarnings(sqle); } } } private static StandardServiceRegistryImpl createServiceRegistry(Properties properties) { Environment.verifyProperties( properties ); ConfigurationHelper.resolvePlaceHolders( properties ); return (StandardServiceRegistryImpl) new StandardServiceRegistryBuilder().applySettings( properties ).build(); } public static void main(String[] args) { try { Configuration cfg = new Configuration(); boolean script = true; boolean drop = false; boolean create = false; boolean halt = false; boolean export = true; String outFile = null; String importFile = DEFAULT_IMPORT_FILE; String propFile = null; boolean format = false; String delim = null; boolean hasNaming = false; boolean hasNamingDelegator = false; for ( int i = 0; i < args.length; i++ ) { if ( args[i].startsWith( "--" ) ) { if ( args[i].equals( "--quiet" ) ) { script = false; } else if ( args[i].equals( "--drop" ) ) { drop = true; } else if ( args[i].equals( "--create" ) ) { create = true; } else if ( args[i].equals( "--haltonerror" ) ) { halt = true; } else if ( args[i].equals( "--text" ) ) { export = false; } else if ( args[i].startsWith( "--output=" ) ) { outFile = args[i].substring( 9 ); } else if ( args[i].startsWith( "--import=" ) ) { importFile = args[i].substring( 9 ); } else if ( args[i].startsWith( "--properties=" ) ) { propFile = args[i].substring( 13 ); } else if ( args[i].equals( "--format" ) ) { format = true; } else if ( args[i].startsWith( "--delimiter=" ) ) { delim = args[i].substring( 12 ); } else if ( args[i].startsWith( "--config=" ) ) { cfg.configure( args[i].substring( 9 ) ); } else if ( args[i].startsWith( "--naming=" ) ) { hasNaming = true; checkNamingAndNamingDelegatorNotBothSpecified( hasNaming, hasNamingDelegator ); cfg.setNamingStrategy( ( NamingStrategy ) ReflectHelper.classForName( args[i].substring( 9 ) ) .newInstance() ); } else if ( args[i].startsWith( "--namingdelegator=" ) ) { hasNamingDelegator = true; checkNamingAndNamingDelegatorNotBothSpecified( hasNaming, hasNamingDelegator ); cfg.setNamingStrategyDelegator( (NamingStrategyDelegator) ReflectHelper.classForName( args[i].substring( 18 ) ) .newInstance() ); } } else { String filename = args[i]; if ( filename.endsWith( ".jar" ) ) { cfg.addJar( new File( filename ) ); } else { cfg.addFile( filename ); } } } if ( propFile != null ) { Properties props = new Properties(); props.putAll( cfg.getProperties() ); props.load( new FileInputStream( propFile ) ); cfg.setProperties( props ); } if (importFile != null) { cfg.setProperty( AvailableSettings.HBM2DDL_IMPORT_FILES, importFile ); } StandardServiceRegistryImpl serviceRegistry = createServiceRegistry( cfg.getProperties() ); try { SchemaExport se = new SchemaExport( serviceRegistry, cfg ) .setHaltOnError( halt ) .setOutputFile( outFile ) .setDelimiter( delim ) .setImportSqlCommandExtractor( serviceRegistry.getService( ImportSqlCommandExtractor.class ) ); if ( format ) { se.setFormat( true ); } se.execute( script, export, drop, create ); } finally { serviceRegistry.destroy(); } } catch ( Exception e ) { LOG.unableToCreateSchema(e); e.printStackTrace(); } } private static void checkNamingAndNamingDelegatorNotBothSpecified(boolean namingSpecified, boolean namingDelegatorSpecified) { if ( namingSpecified && namingDelegatorSpecified ) { throw new HibernateException( "--naming=<naming_strategy> and --namingdelegator=<naming_strategy_delegator> cannot be used together." ); } }
Returns a List of all Exceptions which occured during the export.
Returns:A List containig the Exceptions occured during the export
/** * Returns a List of all Exceptions which occured during the export. * * @return A List containig the Exceptions occured during the export */
public List getExceptions() { return exceptions; } }