package com.openkm.dao;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.Writer;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import org.hibernate.HibernateException;
import org.hibernate.cfg.Configuration;
import org.hibernate.cfg.NamingStrategy;
import org.hibernate.cfg.Settings;
import org.hibernate.connection.ConnectionProvider;
import org.hibernate.connection.ConnectionProviderFactory;
import org.hibernate.dialect.Dialect;
import org.hibernate.tool.hbm2ddl.DatabaseMetadata;
import org.hibernate.util.ReflectHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* A commandline tool to update a database schema. May also be called from
* inside an application.
*
* @see http://opensource.atlassian.com/projects/hibernate/browse/HHH-1186
* @author Christoph Sturm
* @author Paco Avila
*/
public class SchemaUpdate {
private static Logger log = LoggerFactory.getLogger(SchemaUpdate.class);
private ConnectionProvider connectionProvider;
private Configuration configuration;
private Dialect dialect;
private List<Exception> exceptions;
private String outputFile;
public SchemaUpdate(Configuration cfg) throws HibernateException {
this(cfg, cfg.getProperties());
}
public SchemaUpdate(Configuration cfg, Properties connectionProperties) throws HibernateException {
this.configuration = cfg;
dialect = Dialect.getDialect(connectionProperties);
Properties props = new Properties();
props.putAll(dialect.getDefaultProperties());
props.putAll(connectionProperties);
connectionProvider = ConnectionProviderFactory.newConnectionProvider(props);
exceptions = new ArrayList<Exception>();
}
public SchemaUpdate(Configuration cfg, Settings settings) throws HibernateException {
this.configuration = cfg;
dialect = settings.getDialect();
connectionProvider = settings.getConnectionProvider();
exceptions = new ArrayList<Exception>();
}
public static void main(String[] args) {
try {
Configuration cfg = new Configuration();
String outFile = null;
boolean script = true;
// If true then execute db updates, otherwise just generate and
// display updates
boolean doUpdate = true;
String propFile = null;
for (int i = 0; i < args.length; i++) {
if (args[i].startsWith("--")) {
if (args[i].equals("--quiet")) {
script = false;
} else if (args[i].startsWith("--properties=")) {
propFile = args[i].substring(13);
} else if (args[i].startsWith("--config=")) {
cfg.configure(args[i].substring(9));
} else if (args[i].startsWith("--text")) {
doUpdate = false;
} else if (args[i].startsWith("--naming=")) {
cfg.setNamingStrategy((NamingStrategy) ReflectHelper.classForName(
args[i].substring(9)).newInstance());
} else if (args[i].startsWith("--output=")) {
outFile = args[i].substring(9);
}
} else {
cfg.addFile(args[i]);
}
}
if (propFile != null) {
Properties props = new Properties();
props.putAll(cfg.getProperties());
props.load(new FileInputStream(propFile));
cfg.setProperties(props);
}
new SchemaUpdate(cfg).setOutputFile(outFile).execute(script, doUpdate);
} catch (Exception e) {
log.error("Error running schema update", e);
e.printStackTrace();
}
}
/**
* Set an output filename. The generated script will be written to this
* file.
*/
public SchemaUpdate setOutputFile(String filename) {
outputFile = filename;
return this;
}
/**
* Execute the schema updates
*
* @param script
* print all DDL to the console
*/
public void execute(boolean script, boolean doUpdate) {
log.info("Running hbm2ddl schema update");
Connection connection = null;
Statement stmt = null;
boolean autoCommitWasEnabled = true;
Writer outputFileWriter = null;
exceptions.clear();
try {
DatabaseMetadata meta;
try {
log.info("fetching database metadata");
connection = connectionProvider.getConnection();
if (!connection.getAutoCommit()) {
connection.commit();
connection.setAutoCommit(true);
autoCommitWasEnabled = false;
}
meta = new DatabaseMetadata(connection, dialect);
stmt = connection.createStatement();
} catch (SQLException sqle) {
exceptions.add(sqle);
log.error("could not get database metadata", sqle);
throw sqle;
}
log.info("updating schema");
if (outputFile != null) {
log.info("writing generated schema to file: " + outputFile);
outputFileWriter = new FileWriter(outputFile);
}
String[] createSQL = configuration.generateSchemaUpdateScript(dialect, meta);
for (int j = 0; j < createSQL.length; j++) {
final String sql = createSQL[j];
try {
if (script) {
log.info("writing generated schema to console: ");
System.out.println(sql);
}
if (outputFile != null) {
outputFileWriter.write(sql + "\n");
}
if (doUpdate) {
log.debug(sql);
stmt.executeUpdate(sql);
}
} catch (SQLException e) {
exceptions.add(e);
log.error("Unsuccessful: " + sql);
log.error(e.getMessage());
}
}
log.info("schema update complete");
} catch (Exception e) {
exceptions.add(e);
log.error("could not complete schema update", e);
} finally {
try {
if (stmt != null)
stmt.close();
if (!autoCommitWasEnabled)
connection.setAutoCommit(false);
if (connection != null)
connection.close();
if (connectionProvider != null)
connectionProvider.close();
} catch (Exception e) {
exceptions.add(e);
log.error("Error closing connection", e);
}
try {
if (outputFileWriter != null) {
outputFileWriter.close();
}
} catch (Exception e) {
exceptions.add(e);
log.error("Error closing connection", e);
}
}
}
/**
* Returns a List of all Exceptions which occured during the export.
*
* @return A List containig the Exceptions occured during the export
*/
public List<Exception> getExceptions() {
return exceptions;
}
}