package com.txtr.hibernatedelta.generator; import java.math.BigInteger; import java.sql.Connection; import java.sql.Types; import java.util.ArrayList; import java.util.List; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.reflect.FieldUtils; import com.google.common.base.Throwables; import com.google.common.collect.Lists; import com.txtr.hibernatedelta.SqlType; import com.txtr.hibernatedelta.SqlUtil; import com.txtr.hibernatedelta.model.HibernateColumn; import com.txtr.hibernatedelta.model.HibernateDatabase; import com.txtr.hibernatedelta.model.HibernateIndex; import com.txtr.hibernatedelta.model.HibernateIndexType; import com.txtr.hibernatedelta.model.HibernateIndexUtil; import com.txtr.hibernatedelta.model.HibernateTable; import liquibase.CatalogAndSchema; import liquibase.database.Database; import liquibase.database.DatabaseFactory; import liquibase.database.core.OracleDatabase; import liquibase.database.jvm.JdbcConnection; import liquibase.datatype.DataTypeFactory; import liquibase.integration.commandline.CommandLineUtils; import liquibase.snapshot.DatabaseSnapshot; import liquibase.snapshot.SnapshotControl; import liquibase.snapshot.SnapshotGeneratorFactory; import liquibase.structure.core.Column; import liquibase.structure.core.DataType; import liquibase.structure.core.DataType.ColumnSizeUnit; import liquibase.structure.core.ForeignKey; import liquibase.structure.core.ForeignKeyConstraintType; import liquibase.structure.core.Index; import liquibase.structure.core.PrimaryKey; import liquibase.structure.core.Schema; import liquibase.structure.core.Sequence; import liquibase.structure.core.Table; import liquibase.structure.core.UniqueConstraint; public class LiquibaseModelFactory { public static final OracleDatabase DATABASE = new OracleDatabase(); public static final int MAX_CHAR_SIZE = 4000; private static final Schema SCHEMA = new Schema(); private LiquibaseModelFactory() { } static { SnapshotGeneratorFactory.getInstance().register(new LiquibaseSnapshotGenerator()); try { FieldUtils.writeDeclaredStaticField(DataTypeFactory.class, "instance", new LiquibaseDataTypeFactory(), true); } catch (IllegalAccessException e) { throw Throwables.propagate(e); } } public static DatabaseSnapshot readSnapshotFromDatabase(String jdbcUrl, String userName, String password) throws Exception { Database database = CommandLineUtils.createDatabaseObject( Thread.currentThread().getContextClassLoader(), jdbcUrl, userName, password, "oracle.jdbc.driver.OracleDriver", null, null, null, null); return prepareDatabase(database); } private static DatabaseSnapshot prepareDatabase(Database database) throws Exception { return removeIgnoredTables(SnapshotGeneratorFactory.getInstance().createSnapshot(CatalogAndSchema.DEFAULT, database, new SnapshotControl(database))); } public static DatabaseSnapshot readSnapshotFromDatabase(Connection connection) throws Exception { DatabaseFactory databaseFactory = DatabaseFactory.getInstance(); Database database = databaseFactory.findCorrectDatabaseImplementation(new JdbcConnection(connection)); return prepareDatabase(database); } public static DatabaseSnapshot removeIgnoredTables(DatabaseSnapshot snapshot) { for (Table table : Lists.newArrayList(snapshot.get(Table.class))) { if (SqlUtil.isIgnoredTable(table.getName())) { snapshot.remove(table); } } for (Column column : Lists.newArrayList(snapshot.get(Column.class))) { if (SqlUtil.isIgnoredTable(column.getRelation().getName())) { snapshot.remove(column); } } for (UniqueConstraint constraint : Lists.newArrayList(snapshot.get(UniqueConstraint.class))) { if (SqlUtil.isIgnoredTable(constraint.getTable().getName())) { snapshot.remove(constraint); } } for (Index index : Lists.newArrayList(snapshot.get(Index.class))) { if (SqlUtil.isIgnoredTable(index.getTable().getName()) || SqlUtil.isIgnoredIndex(index.getName())) { snapshot.remove(index); } } for (PrimaryKey primaryKey : Lists.newArrayList(snapshot.get(PrimaryKey.class))) { if (SqlUtil.isIgnoredTable(primaryKey.getTable().getName())) { snapshot.remove(primaryKey); } } for (ForeignKey foreignKey : Lists.newArrayList(snapshot.get(ForeignKey.class))) { if (SqlUtil.isIgnoredTable(foreignKey.getForeignKeyTable().getName())) { snapshot.remove(foreignKey); } } return snapshot; } public static DatabaseSnapshot create(HibernateDatabase database) { try { DatabaseSnapshot result = createEmptySnapshot(); final List<HibernateTable> tables = removeVirtualTables(database.getTables()); for (HibernateTable hibernateTable : tables) { result.add(createTable(hibernateTable)); } for (HibernateTable hibernateTable : tables) { Table table = result.getTable(hibernateTable.getName()); String sequenceName = hibernateTable.getSequenceName(); if (sequenceName != null) { Sequence sequence = new Sequence(); sequence.setSchema(SCHEMA); sequence.setName(sequenceName); sequence.setIncrementBy(BigInteger.valueOf(SqlUtil.SEQUENCE_INCREMENT)); sequence.setStartValue(BigInteger.valueOf(SqlUtil.SEQUENCE_START)); result.add(sequence); } for (HibernateIndex hibernateIndex : HibernateIndexUtil.getIndexes(hibernateTable)) { HibernateIndexType type = hibernateIndex.getType(); if (type.isPrimaryKey()) { result.add(createPrimaryKey(hibernateIndex, table)); } else { if (type.isUnique() && type.isConstraint()) { result.add(createUniqueConstraint(hibernateIndex, table)); } } result.add(createIndex(hibernateIndex, table)); } for (HibernateColumn column : hibernateTable.getColumns()) { if (column.getTargetTable() != null) { result.add(createForeignKey(table, result.getTable(column.getTargetTable()), database.getTable(column.getTargetTable()), column.getName(), column.getForeignKeyIndexName() + "F")); } result.add(createColumn(column, table)); } } return result; } catch (Exception e) { throw Throwables.propagate(e); } } private static List<HibernateTable> removeVirtualTables(List<HibernateTable> tables) { final ArrayList<HibernateTable> result = new ArrayList<HibernateTable>(); for (HibernateTable table : tables) { if (! table.isVirtualRootTable()){ result.add(table); } } return result; } public static DatabaseSnapshot createEmptySnapshot() { return new DatabaseSnapshot(DATABASE) { }; } private static Index createIndex(HibernateIndex hibernateIndex, Table table) { Index result = new Index(); result.setName(hibernateIndex.getName()); result.getColumns().addAll(hibernateIndex.getColumns()); result.setUnique(hibernateIndex.getType().isUnique()); result.setTable(table); return result; } private static UniqueConstraint createUniqueConstraint(HibernateIndex hibernateIndex, Table table) { UniqueConstraint result = new UniqueConstraint(); result.setName(hibernateIndex.getName() + "U"); result.getColumns().addAll(hibernateIndex.getColumns()); result.setTable(table); return result; } private static PrimaryKey createPrimaryKey(HibernateIndex hibernateIndex, Table table) { PrimaryKey result = new PrimaryKey(); result.setTable(table); result.setName(hibernateIndex.getName() + "P"); result.getColumnNamesAsList().addAll(hibernateIndex.getColumns()); return result; } private static ForeignKey createForeignKey(Table table, Table targetTable, HibernateTable targetHibernateTable, String columnName, String name) { ForeignKey result = new ForeignKey(); result.setPrimaryKeyTable(targetTable); List<String> names = new ArrayList<String>(); for (HibernateColumn column : targetHibernateTable.getPrimaryKeyColumns()) { names.add(column.getName()); } result.setPrimaryKeyColumns(StringUtils.join(names, ", ")); result.setForeignKeyTable(table); result.setForeignKeyColumns(columnName); result.setDeferrable(true); result.setInitiallyDeferred(true); result.setUpdateRule(ForeignKeyConstraintType.importedKeyRestrict); result.setDeleteRule(ForeignKeyConstraintType.importedKeyRestrict); result.setName(name); return result; } private static Table createTable(HibernateTable hibernateTable) { Table result = new Table(); result.setSchema(SCHEMA); result.setName(hibernateTable.getName()); for (HibernateColumn column : hibernateTable.getColumns()) { result.getColumns().add(createColumn(column, result)); } return result; } private static Column createColumn(HibernateColumn column, Table table) { Column result = new Column(); DataType type = new DataType(column.getSqlType()); result.setType(type); result.setName(column.getName()); result.setRelation(table); if (column.getLength() != null) { type.setColumnSize(column.getLength()); } if (column.getDecimalDigits() != null) { type.setDecimalDigits(column.getDecimalDigits()); } SqlType sqlType = new SqlType(column.getSqlType()); if (sqlType.getValue() == Types.VARCHAR) { type.setColumnSizeUnit(ColumnSizeUnit.CHAR); } else if (sqlType.getValue() == Types.CLOB) { type.setColumnSize(MAX_CHAR_SIZE); } result.setNullable(column.isNullable()); return result; } }