org.jbpm.db.JbpmSchema Maven / Gradle / Ivy
The newest version!
/*
* JBoss, Home of Professional Open Source
* Copyright 2005, JBoss Inc., and individual contributors as indicated
* by the @authors tag. See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software 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 software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jbpm.db;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.SQLWarning;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Collection;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import javax.persistence.metamodel.EntityType;
import org.hibernate.Metamodel;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.model.naming.Identifier;
import org.hibernate.boot.spi.MetadataImplementor;
import org.hibernate.cfg.Configuration;
import org.hibernate.cfg.Environment;
import org.hibernate.dialect.Dialect;
import org.hibernate.engine.jdbc.connections.spi.JdbcConnectionAccess;
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;
import org.hibernate.engine.jdbc.spi.JdbcServices;
import org.hibernate.engine.jdbc.spi.SqlExceptionHelper;
import org.hibernate.engine.jdbc.spi.SqlStatementLogger;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.jdbc.ReturningWork;
import org.hibernate.jdbc.Work;
import org.hibernate.mapping.Table;
import org.hibernate.persister.entity.AbstractEntityPersister;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.resource.transaction.spi.DdlTransactionIsolator;
import org.hibernate.resource.transaction.spi.TransactionCoordinatorBuilder;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.tool.hbm2ddl.SchemaExport;
import org.hibernate.tool.hbm2ddl.SchemaExport.Action;
import org.hibernate.tool.hbm2ddl.SchemaUpdate;
import org.hibernate.tool.schema.TargetType;
import org.hibernate.tool.schema.extract.internal.DatabaseInformationImpl;
import org.hibernate.tool.schema.extract.spi.DatabaseInformation;
import org.hibernate.tool.schema.extract.spi.TableInformation;
import org.hibernate.tool.schema.internal.HibernateSchemaManagementTool.JdbcContextImpl;
import org.hibernate.tool.schema.internal.exec.JdbcConnectionAccessProvidedConnectionImpl;
import org.hibernate.tool.schema.internal.exec.JdbcContext;
import org.jbpm.JbpmContext;
import org.jbpm.JbpmException;
import org.jbpm.db.hibernate.JbpmHibernateConfiguration;
import org.jbpm.logging.db.JDBCExceptionReporter;
/**
* utilities for the jBPM database schema.
*
* {@link "http://stackoverflow.com/questions/32178041/where-did-configuration-generateschemacreationscript-go-in-hibernate-5"}
* {@link "http://stackoverflow.com/questions/24132167/how-to-export-database-schema-using-hibernate-schemaexport-with-beanvalidation-c"}
*/
@SuppressWarnings( "nls" )
public class JbpmSchema {
private final JbpmHibernateConfiguration jbpmHibernateConfiguration;
private String delimiter;
private final List exceptions = new ArrayList<>();
private MetadataSources metadataSources;
private MetadataImplementor metadataImplementor;
private SessionFactory sessionFactory;
private Session session;
private SessionImplementor sessionImplementor;
private static final String[] TABLE_TYPES = { "TABLE" };
public JbpmSchema(JbpmHibernateConfiguration jbpmHibernateConfiguration) {
super();
this.delimiter = ";";
this.jbpmHibernateConfiguration = jbpmHibernateConfiguration;
}
JbpmSchema(JbpmHibernateConfiguration jbpmHibernateConfiguration, JbpmContext context) {
super();
this.delimiter = ";";
this.session = context.getSession();
this.sessionImplementor = (SessionImplementor) this.session;
this.sessionFactory = session.getSessionFactory();
this.jbpmHibernateConfiguration = jbpmHibernateConfiguration;
this.metadataImplementor = this.jbpmHibernateConfiguration.getMetadataImplementor();
this.metadataSources = this.jbpmHibernateConfiguration.getMetadataSources();
}
private synchronized void configure() {
if (sessionFactory == null) {
this.sessionFactory = jbpmHibernateConfiguration.buildSessionFactory();
this.metadataSources = jbpmHibernateConfiguration.getMetadataSources();
this.metadataImplementor = jbpmHibernateConfiguration.getMetadataImplementor();
this.session = sessionFactory.openSession();
this.sessionImplementor = (SessionImplementor)session;
}
}
Dialect getDialect() {
return Dialect.getDialect(jbpmHibernateConfiguration.getConfigurationProxy().getProperties());
}
private String getDefaultCatalog() {
return jbpmHibernateConfiguration.getConfigurationProxy().getProperty(Environment.DEFAULT_CATALOG);
}
private String getDefaultSchema() {
return jbpmHibernateConfiguration.getConfigurationProxy().getProperty(Environment.DEFAULT_SCHEMA);
}
private boolean getShowSql() {
return "true".equalsIgnoreCase(jbpmHibernateConfiguration.getConfigurationProxy().getProperty(Environment.SHOW_SQL));
}
public void setDelimiter(String delimiter) {
this.delimiter = delimiter;
}
public List getExceptions() {
return exceptions;
}
public void dropSchema() {
dropSchema(true, null);
}
public void dropSchema(boolean exportToDb, String exportToFile) {
configure();
SchemaExport schemaExport = new SchemaExport();
schemaExport.setOutputFile(exportToFile);
schemaExport.setDelimiter(delimiter);
EnumSet list = EnumSet.of(TargetType.SCRIPT);
if (exportToDb) {
list.add(TargetType.DATABASE);
}
schemaExport.drop(list, metadataImplementor);
@SuppressWarnings( "unchecked" )
List schemaExceptions = schemaExport.getExceptions();
if (schemaExceptions != null && schemaExceptions.size() > 0) {
for ( Exception e : schemaExceptions ) {
exceptions.add( e );
JDBCExceptionReporter.logExceptions(e, "failed to drop schema");
}
}
}
public void createSchema() {
createSchema( true, null );
}
public void createSchema(boolean exportToDb, String exportToFile) {
configure();
SchemaExport schemaExport = new SchemaExport();
schemaExport.setOutputFile(exportToFile);
schemaExport.setDelimiter(delimiter);
EnumSet list = EnumSet.of(TargetType.SCRIPT);
if (exportToDb) {
list.add(TargetType.DATABASE);
}
schemaExport.create(list, metadataImplementor);
@SuppressWarnings( "unchecked" )
List schemaExceptions = schemaExport.getExceptions();
if (schemaExceptions != null && schemaExceptions.size() > 0) {
for ( Exception e : schemaExceptions ) {
exceptions.add( e );
JDBCExceptionReporter.logExceptions(e, "failed to create schema");
}
}
}
public void cleanSchema() {
cleanSchema( true, null );
}
public void cleanSchema(boolean exportToDb, String exportToFile) {
configure();
SchemaExport schemaExportForDrop = new SchemaExport();
schemaExportForDrop.setOutputFile(exportToFile);
schemaExportForDrop.setDelimiter(delimiter);
EnumSet list = EnumSet.of(TargetType.SCRIPT);
if (exportToDb) {
list = EnumSet.of(TargetType.DATABASE);
}
schemaExportForDrop.execute(list, Action.DROP, metadataImplementor);
@SuppressWarnings( "unchecked" )
List schemaExceptions = schemaExportForDrop.getExceptions();
if (schemaExceptions != null && schemaExceptions.size() > 0) {
for ( Exception e : schemaExceptions ) {
exceptions.add( e );
JDBCExceptionReporter.logExceptions(e, "failed to clean schema");
}
}
}
public void updateSchema() {
updateSchema( true, null );
}
public void updateSchema(boolean exportToDb, String exportToFile) {
configure();
SchemaUpdate schemaUpdate = new SchemaUpdate();
schemaUpdate.setOutputFile(exportToFile);
schemaUpdate.setDelimiter(delimiter);
EnumSet list = EnumSet.of(TargetType.SCRIPT);
if (exportToDb) {
list.add(TargetType.DATABASE);
}
schemaUpdate.execute(list, metadataImplementor);
@SuppressWarnings( "unchecked" )
List schemaExceptions = schemaUpdate.getExceptions();
if (schemaExceptions != null && schemaExceptions.size() > 0) {
for ( Exception e : schemaExceptions ) {
exceptions.add( e );
JDBCExceptionReporter.logExceptions(e, "failed to update schema");
}
}
}
public void saveSqlScripts(String dir, String prefix) {
File path = new File(dir);
if (!path.isDirectory()) {
throw new JbpmException(path + " is not a directory");
}
dropSchema(true, new File(path, prefix + ".drop.sql").getAbsolutePath());
createSchema(true, new File(path, prefix + ".create.sql").getAbsolutePath());
for ( Exception e : exceptions ) {
JDBCExceptionReporter.logExceptions(e, "failed to generate scripts");
}
}
public Set getJbpmTables() {
configure();
Set jbpmTables = new HashSet<>();
Metamodel metamodel = sessionFactory.getMetamodel();
for (EntityType> entityType: metamodel.getEntities()) {
try {
Class> modelClazz = entityType.getJavaType();
EntityPersister persister = sessionImplementor.getEntityPersister(null, modelClazz.getDeclaredConstructor().newInstance());
if (persister instanceof AbstractEntityPersister) {
AbstractEntityPersister persisterImpl = (AbstractEntityPersister) persister;
String tableName = persisterImpl.getTableName();
jbpmTables.add(tableName);
}
}
catch (Exception ignored) {
// nop
}
}
return jbpmTables;
}
public Map getRowsPerTable() {
configure();
final Map rowsPerTable = new HashMap<>();
session.doWork( new Work(){
@Override
public void execute( Connection connection ) {
try {
JbpmSchema.prepareConnection( connection );
for (String tableName :getJbpmTables()) {
String sql = "SELECT COUNT(*) FROM " + tableName;
try ( Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery(sql); ) {
if (!resultSet.next()) continue;
long count = resultSet.getLong(1);
if (resultSet.wasNull()) continue;
rowsPerTable.put(tableName, new Long(count));
}
}
} catch (SQLException e) {
exceptions.add(e);
JDBCExceptionReporter.logExceptions(e, "could not count records");
rowsPerTable.clear();
}
}
});
return rowsPerTable;
}
public Set getExistingTables() {
configure();
final Set existingTables = new HashSet<>();
session.doWork( new Work(){
@Override
public void execute( Connection connection ) {
try {
JbpmSchema.prepareConnection( connection );
DatabaseMetaData metaData = connection.getMetaData();
boolean storesLowerCaseIdentifiers = metaData.storesLowerCaseIdentifiers();
try ( ResultSet resultSet = metaData.getTables(
getDefaultCatalog(), getDefaultSchema(), null, TABLE_TYPES) ) {
while (resultSet.next()) {
String tableName = resultSet.getString("TABLE_NAME");
if (storesLowerCaseIdentifiers) tableName = tableName.toUpperCase();
existingTables.add(tableName);
}
}
} catch (SQLException e) {
exceptions.add(e);
JDBCExceptionReporter.logExceptions(e, "could not count records");
existingTables.clear();
}
}
});
return existingTables;
}
public boolean tableExists(final String tableName) {
configure();
final Boolean found = session.doReturningWork( new ReturningWork() {
@Override
public Boolean execute( Connection connection ) throws SQLException {
JbpmSchema.prepareConnection( connection );
DatabaseMetaData metaData = connection.getMetaData();
try (ResultSet resultSet = metaData.getTables(getDefaultCatalog(), getDefaultSchema(), tableName, TABLE_TYPES) ){
return resultSet.next();
}
catch (SQLException e) {
exceptions.add(e);
JDBCExceptionReporter.logExceptions(e, "could not determine whether table exists");
return false;
}
}
} );
return found;
}
public void updateTable(String tableName) {
configure();
final Table table = findTableMapping(tableName);
final boolean showSql = getShowSql();
final TableInformation tableInformation = getDatabaseInformation().getTableInformation( table.getQualifiedTableName() );
session.doWork( new Work(){
@Override
public void execute( Connection connection ) {
try {
JbpmSchema.prepareConnection( connection );
try ( Statement statement = connection.createStatement();) {
if ( tableInformation == null) {
String sql = table.sqlCreateString(getDialect(), metadataImplementor, getDefaultCatalog(), getDefaultSchema());
JbpmSchema.execute(sql, statement, showSql);
}
else {
Iterator scripts = table.sqlAlterStrings(
getDialect(),
metadataImplementor,
tableInformation,
Identifier.toIdentifier(getDefaultCatalog()),
Identifier.toIdentifier(getDefaultSchema()));
for (Iterator script = scripts; script.hasNext();) {
String sql = script.next();
JbpmSchema.execute(sql, statement, showSql);
}
}
}
} catch (SQLException e) {
exceptions.add(e);
JDBCExceptionReporter.logExceptions(e, "failed to update table");
}
}
});
}
private DatabaseInformation getDatabaseInformation() {
final ServiceRegistry serviceRegistry = metadataImplementor.getMetadataBuildingOptions().getServiceRegistry();
// final ConfigurationService cfgService = serviceRegistry.getService( ConfigurationService.class );
// final SchemaManagementTool schemaManagementTool = serviceRegistry.getService( SchemaManagementTool.class );
// final SchemaMigrator schemaMigrator = schemaManagementTool.getSchemaMigrator( cfgService.getSettings() );
// final JdbcServices jdbcServices = serviceRegistry.getService( JdbcServices.class );
// final JdbcConnectionAccess jdbcConnectionAccess = jdbcServices.getBootstrapJdbcConnectionAccess();
final DatabaseInformation databaseInformation;
DdlTransactionIsolator ddlt = null;
try {
JdbcContext ctx = new JdbcContextBuilder (serviceRegistry).buildJdbcContext();
ddlt = getDdlTransactionIsolator(ctx);
databaseInformation = new DatabaseInformationImpl(
serviceRegistry,
serviceRegistry.getService(JdbcEnvironment.class),
ddlt,
metadataImplementor.getDatabase().getDefaultNamespace().getPhysicalName());
}
catch (SQLException e) {
throw new RuntimeException(e);
}
return databaseInformation;
}
private Table findTableMapping(String tableName) {
configure();
Collection tableMappings = metadataImplementor.collectTableMappings();
for ( Table table : tableMappings ) {
if (tableName.equals(table.getName())) return table;
}
throw new JbpmException("no mapping found for table: " + tableName);
}
public void createTable(String tableName) {
configure();
final Table table = findTableMapping(tableName);
final boolean showSql = getShowSql();
session.doWork( new Work(){
@Override
public void execute( Connection connection ) {
try {
JbpmSchema.prepareConnection( connection );
try ( Statement statement = connection.createStatement();) {
String sql = table.sqlCreateString(getDialect(), metadataImplementor, getDefaultCatalog(), getDefaultSchema());
JbpmSchema.execute(sql, statement, showSql);
}
} catch (SQLException e) {
exceptions.add(e);
JDBCExceptionReporter.logExceptions(e, "failed to create table");
}
}
});
}
private static Exception execute(String sql, Statement statement, boolean showSql) {
try {
if (showSql) System.out.println(sql);
statement.executeUpdate(sql);
SQLWarning warning = statement.getWarnings();
if (warning != null) {
JDBCExceptionReporter.walkWarnings( warning, JDBCExceptionReporter.STANDARD_WARNING_HANDLER );
statement.clearWarnings();
}
} catch (SQLException e) {
JDBCExceptionReporter.logExceptions(e, "failed to execute update");
return e;
}
return null;
}
private static void prepareConnection( Connection connection ) throws SQLException {
if ( connection.getAutoCommit() == false ) {
connection.commit();
connection.setAutoCommit( true );
}
}
public static void main(String[] args) {
if (args.length > 0) {
String action = args[0];
if ("create".equalsIgnoreCase(action)) {
getJbpmSchema(args, 1).createSchema();
}
else if ("drop".equalsIgnoreCase(action)) {
getJbpmSchema(args, 1).dropSchema();
}
else if ("clean".equalsIgnoreCase(action)) {
getJbpmSchema(args, 1).cleanSchema();
}
else if ("update".equalsIgnoreCase(action)) {
getJbpmSchema(args, 1).updateSchema();
}
else if ("scripts".equalsIgnoreCase(action) && args.length > 2) {
getJbpmSchema(args, 3).saveSqlScripts(args[1], args[2]);
}
else
syntax();
}
else
syntax();
}
private static void syntax() {
System.err.println("Syntax:");
System.err.println("JbpmSchema create [ []]");
System.err.println("JbpmSchema drop [ []]");
System.err.println("JbpmSchema clean [ []]");
System.err.println("JbpmSchema update [ []]");
System.err
.println("JbpmSchema scripts [ []]");
System.exit(1);
}
private static JbpmSchema getJbpmSchema(String[] args, int index) {
JbpmHibernateConfiguration jbpmHibernateConfiguration = new JbpmHibernateConfiguration();
Configuration configuration = jbpmHibernateConfiguration.getConfigurationProxy();
if (index < args.length) {
// read configuration xml file
String hibernateCfgXml = args[index];
configuration.configure(new File(hibernateCfgXml));
// read extra properties
if (index + 1 < args.length) {
String hibernateProperties = args[index + 1];
try {
InputStream fileSource = new BufferedInputStream(Files.newInputStream(Paths.get(hibernateProperties)));
Properties properties = new Properties();
properties.load(fileSource);
fileSource.close();
configuration.addProperties(properties);
}
catch (IOException e) {
throw new JbpmException("failed to load hibernate properties", e);
}
}
}
else {
// read configuration from default resource
configuration.configure();
}
return new JbpmSchema(jbpmHibernateConfiguration);
}
public JbpmHibernateConfiguration getJbpmHibernateConfiguration() {
return jbpmHibernateConfiguration;
}
public String getDelimiter() {
return delimiter;
}
public MetadataSources getMetadataSources() {
configure();
return metadataSources;
}
public MetadataImplementor getMetadataImplementor() {
configure();
return metadataImplementor;
}
public SessionFactory getSessionFactory() {
configure();
return sessionFactory;
}
public Session getSession()
{
return session;
}
public SessionImplementor getSessionImplementor() {
configure();
return sessionImplementor;
}
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;
}
}
private DdlTransactionIsolator getDdlTransactionIsolator(JdbcContext jdbcContext) {
return jdbcContext.getServiceRegistry().getService( TransactionCoordinatorBuilder.class ).buildDdlTransactionIsolator( jdbcContext );
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy