de.akquinet.jbosscc.guttenbase.tools.schema.SchemaScriptCreatorTool Maven / Gradle / Ivy
package de.akquinet.jbosscc.guttenbase.tools.schema;
import de.akquinet.jbosscc.guttenbase.connector.DatabaseType;
import de.akquinet.jbosscc.guttenbase.exceptions.IncompatibleColumnsException;
import de.akquinet.jbosscc.guttenbase.exceptions.IncompatibleTablesException;
import de.akquinet.jbosscc.guttenbase.hints.CaseConversionMode;
import de.akquinet.jbosscc.guttenbase.mapping.ColumnMapper;
import de.akquinet.jbosscc.guttenbase.mapping.ColumnTypeMapper;
import de.akquinet.jbosscc.guttenbase.mapping.TableMapper;
import de.akquinet.jbosscc.guttenbase.meta.ColumnMetaData;
import de.akquinet.jbosscc.guttenbase.meta.DatabaseMetaData;
import de.akquinet.jbosscc.guttenbase.meta.ForeignKeyMetaData;
import de.akquinet.jbosscc.guttenbase.meta.IndexMetaData;
import de.akquinet.jbosscc.guttenbase.meta.TableMetaData;
import de.akquinet.jbosscc.guttenbase.repository.ConnectorRepository;
import de.akquinet.jbosscc.guttenbase.tools.TableOrderTool;
import de.akquinet.jbosscc.guttenbase.tools.schema.comparison.DuplicateIndexIssue;
import de.akquinet.jbosscc.guttenbase.tools.schema.comparison.SchemaComparatorTool;
import de.akquinet.jbosscc.guttenbase.tools.schema.comparison.SchemaCompatibilityIssueType;
import de.akquinet.jbosscc.guttenbase.tools.schema.comparison.SchemaCompatibilityIssues;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Random;
import java.util.stream.Collectors;
/**
* Create Custom DDL script from given database meta data.
*
* © 2012-2020 akquinet tech@spree
*
*
* @author M. Dahm
*/
public class SchemaScriptCreatorTool {
private static final Random RANDOM = new Random();
private final String _sourceConnectorId;
private final String _targetConnectorId;
private final ConnectorRepository _connectorRepository;
public SchemaScriptCreatorTool(final ConnectorRepository connectorRepository,
final String sourceConnectorId, final String targetConnectorId) {
assert connectorRepository != null : "connectorRepository != null";
assert sourceConnectorId != null : "sourceConnectorId != null";
assert targetConnectorId != null : "targetConnectorId != null";
_sourceConnectorId = sourceConnectorId;
_connectorRepository = connectorRepository;
_targetConnectorId = targetConnectorId;
}
public List createTableStatements() throws SQLException {
final List tables = new TableOrderTool().getOrderedTables(getDatabaseMetaData().getTableMetaData(), true);
return createTableStatements(tables);
}
public List createTableStatements(final List tables) throws SQLException {
final List result = new ArrayList<>();
for (final TableMetaData tableMetaData : tables) {
result.add(createTable(tableMetaData));
}
return result;
}
public List createPrimaryKeyStatements() throws SQLException {
final List tables = new TableOrderTool().getOrderedTables(getDatabaseMetaData().getTableMetaData(), true);
return createPrimaryKeyStatements(tables);
}
private DatabaseMetaData getDatabaseMetaData() throws SQLException {
return _connectorRepository.getDatabaseMetaData(getSourceConnectorId());
}
public List createPrimaryKeyStatements(final List tables) throws SQLException {
final List result = new ArrayList<>();
for (final TableMetaData tableMetaData : tables) {
int counter = 1;
final List primaryKeyColumns = tableMetaData.getPrimaryKeyColumns();
if (!primaryKeyColumns.isEmpty()) {
result.add(createPrimaryKeyStatement(tableMetaData, primaryKeyColumns, counter++));
}
}
return result;
}
public List createIndexStatements() throws SQLException {
final List tables = new TableOrderTool().getOrderedTables(getDatabaseMetaData().getTableMetaData(), true);
return createIndexStatements(tables);
}
public List createIndexStatements(final List tables) throws SQLException {
final List result = new ArrayList<>();
for (final TableMetaData tableMetaData : tables) {
int counter = 1;
final SchemaCompatibilityIssues issues = new SchemaComparatorTool(_connectorRepository).checkDuplicateIndexes(tableMetaData);
final List conflictedIndexes = issues.getCompatibilityIssues().stream()
.filter(i -> i.getCompatibilityIssueType() == SchemaCompatibilityIssueType.DUPLICATE_INDEX)
.map(i -> ((DuplicateIndexIssue) i).getIndexMetaData()).collect(Collectors.toList());
for (final IndexMetaData indexMetaData : tableMetaData.getIndexes()) {
final List columns = indexMetaData.getColumnMetaData();
final boolean columnsFormPrimaryKey = columns.stream().map(column -> column.isPrimaryKey()).reduce((a, b) -> a && b).orElse(false);
final boolean conflictedIndex = conflictedIndexes.contains(indexMetaData);
if (!columnsFormPrimaryKey && !conflictedIndex) {
result.add(createIndex(indexMetaData, counter++));
}
}
}
return result;
}
public List createForeignKeyStatements() throws SQLException {
final List tables = new TableOrderTool().getOrderedTables(getDatabaseMetaData().getTableMetaData(), true);
return createForeignKeyStatements(tables);
}
public List createForeignKeyStatements(final List tables) throws SQLException {
final List result = new ArrayList<>();
for (final TableMetaData tableMetaData : tables) {
int counter = 1;
for (final ColumnMetaData columnMetaData : tableMetaData.getColumnMetaData()) {
if (columnMetaData.getReferencedColumn() != null) {
result.add(createForeignKey(columnMetaData, counter++));
}
}
}
return result;
}
public String createTable(final TableMetaData tableMetaData) throws SQLException {
final DatabaseMetaData targetDatabaseMetaData = _connectorRepository.getDatabaseMetaData(getTargetConnectorId());
final TableMapper tableMapper = _connectorRepository.getConnectorHint(getTargetConnectorId(), TableMapper.class).getValue();
final String rawTableName = tableMapper.mapTableName(tableMetaData, targetDatabaseMetaData);
final String tableName = tableMapper.fullyQualifiedTableName(tableMetaData, targetDatabaseMetaData);
final StringBuilder builder = new StringBuilder("CREATE TABLE " + tableName + "\n(\n");
final int maxNameLength = getTargetMaxNameLength();
if (rawTableName.length() > maxNameLength) {
throw new IncompatibleTablesException("Table name " + rawTableName + " is too long for the targeted data base (Max. "
+ maxNameLength + "). You will have to provide an appropriate " + TableMapper.class.getName() + " hint");
}
for (final Iterator iterator = tableMetaData.getColumnMetaData().iterator(); iterator.hasNext(); ) {
final ColumnMetaData columnMetaData = iterator.next();
builder.append(" ").append(createColumn(columnMetaData));
if (iterator.hasNext()) {
builder.append(", \n");
}
}
return builder.append("\n);").toString();
}
private String createPrimaryKeyStatement(final TableMetaData tableMetaData, final List primaryKeyColumns,
final int counter) throws SQLException {
final TableMapper tableMapper = _connectorRepository.getConnectorHint(getTargetConnectorId(), TableMapper.class).getValue();
final ColumnMapper columnMapper = _connectorRepository.getConnectorHint(getTargetConnectorId(), ColumnMapper.class).getValue();
final DatabaseMetaData targetDatabaseMetaData = _connectorRepository.getDatabaseMetaData(getTargetConnectorId());
final String qualifiedTableName = tableMapper.fullyQualifiedTableName(tableMetaData, targetDatabaseMetaData);
final String tableName = tableMapper.mapTableName(tableMetaData, targetDatabaseMetaData);
final String pkName = createConstraintName("PK_", tableName + "_", counter);
final StringBuilder builder = new StringBuilder("ALTER TABLE "
+ qualifiedTableName
+ " ADD CONSTRAINT " +
pkName
+ " PRIMARY KEY (");
for (final ColumnMetaData columnMetaData : primaryKeyColumns) {
builder.append(columnMapper.mapColumnName(columnMetaData, tableMetaData)).append(", ");
}
builder.setLength(builder.length() - 2);
builder.append(");");
return builder.toString();
}
private String createIndex(final IndexMetaData indexMetaData, final int counter) throws SQLException {
final TableMetaData tableMetaData = indexMetaData.getTableMetaData();
final TableMapper tableMapper = _connectorRepository.getConnectorHint(getTargetConnectorId(), TableMapper.class).getValue();
final DatabaseMetaData targetDatabaseMetaData = _connectorRepository.getDatabaseMetaData(getTargetConnectorId());
final String tableName = tableMapper.mapTableName(tableMetaData, targetDatabaseMetaData);
final String indexName = createConstraintName("IDX_", CaseConversionMode.UPPER.convert(indexMetaData.getIndexName())
+ "_" + tableName + "_", counter);
return createIndex(indexMetaData, indexName);
}
public String createIndex(final IndexMetaData indexMetaData) throws SQLException {
return createIndex(indexMetaData, indexMetaData.getIndexName());
}
private String createIndex(final IndexMetaData indexMetaData, final String indexName) throws SQLException {
final DatabaseMetaData targetDatabaseMetaData = _connectorRepository.getDatabaseMetaData(getTargetConnectorId());
final TableMapper tableMapper = _connectorRepository.getConnectorHint(getTargetConnectorId(), TableMapper.class).getValue();
final ColumnMapper columnMapper = _connectorRepository.getConnectorHint(getTargetConnectorId(), ColumnMapper.class).getValue();
final TableMetaData tableMetaData = indexMetaData.getTableMetaData();
final String unique = indexMetaData.isUnique() ? " UNIQUE " : " ";
final StringBuilder builder = new StringBuilder("CREATE" + unique
+ "INDEX "
+ indexName
+ " ON "
+ tableMapper.fullyQualifiedTableName(tableMetaData, targetDatabaseMetaData)
+ "(");
for (final Iterator iterator = indexMetaData.getColumnMetaData().iterator(); iterator.hasNext(); ) {
final ColumnMetaData columnMetaData = iterator.next();
builder.append(columnMapper.mapColumnName(columnMetaData, tableMetaData));
if (iterator.hasNext()) {
builder.append(", ");
}
}
return builder.append(");").toString();
}
private String createForeignKey(final ColumnMetaData referencingColumn, final int counter) throws SQLException {
final TableMapper tableMapper = _connectorRepository.getConnectorHint(getTargetConnectorId(), TableMapper.class).getValue();
final ColumnMapper columnMapper = _connectorRepository.getConnectorHint(getTargetConnectorId(), ColumnMapper.class).getValue();
final DatabaseMetaData targetDatabaseMetaData = _connectorRepository.getDatabaseMetaData(getTargetConnectorId());
final TableMetaData tableMetaData = referencingColumn.getTableMetaData();
final ColumnMetaData referencedColumn = referencingColumn.getReferencedColumn();
final String tablename = tableMapper.mapTableName(tableMetaData, targetDatabaseMetaData);
final String fkName = createConstraintName("FK_", tablename + "_"
+ columnMapper.mapColumnName(referencingColumn, referencedColumn.getTableMetaData())
+ "_"
+ columnMapper.mapColumnName(referencedColumn, referencedColumn.getTableMetaData()) + "_", counter);
return createForeignKey(referencingColumn, fkName);
}
public String createForeignKey(final ColumnMetaData referencingColumn, final String fkName) throws SQLException {
final TableMapper tableMapper = _connectorRepository.getConnectorHint(getTargetConnectorId(), TableMapper.class).getValue();
final ColumnMapper columnMapper = _connectorRepository.getConnectorHint(getTargetConnectorId(), ColumnMapper.class).getValue();
final DatabaseMetaData targetDatabaseMetaData = _connectorRepository.getDatabaseMetaData(getTargetConnectorId());
final TableMetaData tableMetaData = referencingColumn.getTableMetaData();
final ColumnMetaData referencedColumn = referencingColumn.getReferencedColumn();
final String qualifiedTableName = tableMapper.fullyQualifiedTableName(tableMetaData, targetDatabaseMetaData);
return "ALTER TABLE " + qualifiedTableName + " ADD CONSTRAINT " + fkName +
" FOREIGN KEY (" + columnMapper.mapColumnName(referencingColumn, referencingColumn.getTableMetaData()) + ") REFERENCES " +
tableMapper.fullyQualifiedTableName(referencedColumn.getTableMetaData(), targetDatabaseMetaData)
+ "(" + columnMapper.mapColumnName(referencedColumn, referencingColumn.getTableMetaData()) + ");";
}
public String createForeignKey(final ForeignKeyMetaData foreignKeyMetaData) throws SQLException {
return createForeignKey(foreignKeyMetaData.getReferencingColumn(), foreignKeyMetaData.getForeignKeyName());
}
public String createColumn(final ColumnMetaData columnMetaData) throws SQLException {
final TableMapper tableMapper = _connectorRepository.getConnectorHint(getTargetConnectorId(), TableMapper.class).getValue();
final DatabaseMetaData targetDatabaseMetaData = _connectorRepository.getDatabaseMetaData(getTargetConnectorId());
final ColumnMapper columnMapper = _connectorRepository.getConnectorHint(getTargetConnectorId(), ColumnMapper.class).getValue();
final ColumnTypeMapper columnTypeMapper = _connectorRepository.getConnectorHint(getTargetConnectorId(), ColumnTypeMapper.class).getValue();
final StringBuilder builder = new StringBuilder();
final DatabaseType sourceType = _connectorRepository.getDatabaseMetaData(getSourceConnectorId()).getDatabaseType();
final DatabaseType targetType = _connectorRepository.getDatabaseMetaData(getTargetConnectorId()).getDatabaseType();
final String columnName = columnMapper.mapColumnName(columnMetaData, columnMetaData.getTableMetaData());
final String columnType = columnTypeMapper.mapColumnType(columnMetaData, sourceType, targetType);
final int maxNameLength = getTargetMaxNameLength();
final String rawTableName = tableMapper.mapTableName(columnMetaData.getTableMetaData(), targetDatabaseMetaData);
if (columnName.length() > maxNameLength) {
throw new IncompatibleColumnsException("Table " + rawTableName + ": Column name " + columnName + " is too long for " +
"the targeted data base (Max. " + maxNameLength + ")." +
" You will have to provide an appropriate " + ColumnMapper.class.getName() + " hint");
}
builder.append(columnName + " " + columnType);
if (!columnMetaData.isNullable()) {
builder.append(" NOT NULL");
}
return builder.toString();
}
public String createConstraintName(final String prefix, final String preferredName, final int uniqueId) throws SQLException {
final StringBuilder name = new StringBuilder(preferredName);
final int maxLength = getTargetMaxNameLength() - prefix.length() - String.valueOf(uniqueId).length();
while (name.length() > maxLength) {
final int index = Math.abs(RANDOM.nextInt() % name.length());
name.deleteCharAt(index);
}
return prefix + name + uniqueId;
}
public int getTargetMaxNameLength() throws SQLException {
final java.sql.DatabaseMetaData metaData = _connectorRepository.getDatabaseMetaData(getTargetConnectorId()).getDatabaseMetaData();
// Since there is no getMaxConstraintNameLength() ...
final int nameLength = metaData.getMaxColumnNameLength();
// Return reasonable default if value is not known or unlimited
return nameLength <= 0 ? 64 : nameLength;
}
public String getTargetConnectorId() {
return _targetConnectorId;
}
public String getSourceConnectorId() {
return _sourceConnectorId;
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy