All Downloads are FREE. Search and download functionalities are using the official Maven repository.

de.akquinet.jbosscc.guttenbase.tools.schema.comparison.SchemaComparatorTool Maven / Gradle / Ivy

There is a newer version: 2.1.0
Show newest version
package de.akquinet.jbosscc.guttenbase.tools.schema.comparison;

import de.akquinet.jbosscc.guttenbase.hints.ColumnOrderHint;
import de.akquinet.jbosscc.guttenbase.hints.TableOrderHint;
import de.akquinet.jbosscc.guttenbase.mapping.ColumnMapper;
import de.akquinet.jbosscc.guttenbase.mapping.ColumnMapper.ColumnMapperResult;
import de.akquinet.jbosscc.guttenbase.mapping.ColumnTypeMapping;
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.CommonColumnTypeResolverTool;
import java.sql.SQLException;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * Will check two schemas for compatibility and report found issues.
 * 

* © 2012-2020 akquinet tech@spree *

* * @author M. Dahm * Hint is used by {@link TableOrderHint} to determine order of tables */ @SuppressWarnings("RedundantThrows") public class SchemaComparatorTool { private final ConnectorRepository _connectorRepository; private final SchemaCompatibilityIssues _schemaCompatibilityIssues = new SchemaCompatibilityIssues(); public SchemaComparatorTool(final ConnectorRepository connectorRepository) { assert connectorRepository != null : "connectorRepository != null"; _connectorRepository = connectorRepository; } /** * Check compatibility of both connectors/schemata. * * @param sourceConnectorId * @param targetConnectorId * @return List of found issues. If empty the schemas are completely compatible * @throws SQLException */ @SuppressWarnings("JavaDoc") public SchemaCompatibilityIssues check(final String sourceConnectorId, final String targetConnectorId) throws SQLException { final List sourceTables = TableOrderHint.getSortedTables(_connectorRepository, sourceConnectorId); final TableMapper tableMapper = _connectorRepository.getConnectorHint(targetConnectorId, TableMapper.class).getValue(); final DatabaseMetaData targetDatabase = _connectorRepository.getDatabaseMetaData(targetConnectorId); checkEqualTables(sourceTables, targetDatabase, tableMapper); for (final TableMetaData sourceTable : sourceTables) { final TableMetaData targetTable = tableMapper.map(sourceTable, targetDatabase); if (targetTable != null) { checkEqualColumns(sourceConnectorId, targetConnectorId, sourceTable, targetTable); checkEqualForeignKeys(sourceTable, targetTable); checkEqualIndexes(sourceTable, targetTable); checkDuplicateIndexes(sourceTable); checkForeignKeys(sourceTable); checkDuplicateIndexes(targetTable); checkForeignKeys(targetTable); } } return _schemaCompatibilityIssues; } public SchemaCompatibilityIssues checkEqualForeignKeys(final TableMetaData sourceTable, final TableMetaData targetTable) throws SQLException { for (final ForeignKeyMetaData sourceFK : sourceTable.getImportedForeignKeys()) { ForeignKeyMetaData matchingFK = null; for (final ForeignKeyMetaData targetFK : targetTable.getImportedForeignKeys()) { if (sourceFK.getReferencedColumn().equals(targetFK.getReferencedColumn()) && sourceFK.getTableMetaData().equals(targetFK.getTableMetaData()) && sourceFK.getReferencingColumn().equals(targetFK.getReferencingColumn())) { matchingFK = targetFK; } } if (matchingFK == null) { _schemaCompatibilityIssues.addIssue(new MissingForeignKeyIssue("Missing/incompatible foreign key " + sourceFK, sourceFK)); } } return _schemaCompatibilityIssues; } public SchemaCompatibilityIssues checkEqualIndexes(final TableMetaData sourceTable, final TableMetaData targetTable) throws SQLException { for (final IndexMetaData sourceIndex : sourceTable.getIndexes()) { IndexMetaData matchingIndex = null; for (final IndexMetaData targetIndex : targetTable.getIndexes()) { if (sourceIndex.getColumnMetaData().equals(targetIndex.getColumnMetaData())) { matchingIndex = targetIndex; } } if (matchingIndex == null) { _schemaCompatibilityIssues.addIssue(new MissingIndexIssue("Missing index " + sourceIndex, sourceIndex)); } } return _schemaCompatibilityIssues; } public SchemaCompatibilityIssues checkDuplicateIndexes(final TableMetaData table) { final Map indexMetaDataMap = new LinkedHashMap<>(); for (final IndexMetaData index : table.getIndexes()) { final String sortedColumnNames = index.getColumnMetaData().stream().map(ColumnMetaData::getColumnName) .sorted().collect(Collectors.toList()).toString(); if (indexMetaDataMap.containsKey(sortedColumnNames)) { final IndexMetaData conflictingIndex = indexMetaDataMap.get(sortedColumnNames); _schemaCompatibilityIssues.addIssue(new DuplicateIndexIssue("Duplicate index " + conflictingIndex + "vs." + index, index)); } else { indexMetaDataMap.put(sortedColumnNames, index); } } return _schemaCompatibilityIssues; } public SchemaCompatibilityIssues checkForeignKeys(final TableMetaData table) { final Map fkMetaDataMap = new LinkedHashMap<>(); for (final ForeignKeyMetaData foreignKey : table.getExportedForeignKeys()) { final String columNames = getFullyQualifiedColumnName(foreignKey.getReferencingColumn()) + ":" + getFullyQualifiedColumnName(foreignKey.getReferencingColumn()); if (fkMetaDataMap.containsKey(columNames)) { final ForeignKeyMetaData conflictingKey = fkMetaDataMap.get(columNames); _schemaCompatibilityIssues.addIssue(new DuplicateForeignKeyIssue("Duplicate foreignKey " + conflictingKey + "vs." + foreignKey, foreignKey)); } else { fkMetaDataMap.put(columNames, foreignKey); } } return _schemaCompatibilityIssues; } private static String getFullyQualifiedColumnName(final ColumnMetaData columnMetaData) { return columnMetaData.getTableMetaData().getTableName() + "." + columnMetaData.getColumnName(); } public SchemaCompatibilityIssues checkEqualColumns(final String sourceConnectorId, final String targetConnectorId, final TableMetaData tableMetaData1, final TableMetaData tableMetaData2) throws SQLException { final ColumnMapper columnMapper = _connectorRepository.getConnectorHint(targetConnectorId, ColumnMapper.class).getValue(); final CommonColumnTypeResolverTool commonColumnTypeResolver = new CommonColumnTypeResolverTool(_connectorRepository); final ColumnMapper sourceColumnNameMapper = _connectorRepository.getConnectorHint(sourceConnectorId, ColumnMapper.class).getValue(); final ColumnMapper targetColumnNameMapper = _connectorRepository.getConnectorHint(targetConnectorId, ColumnMapper.class).getValue(); final String tableName = tableMetaData1.getTableName(); final List sourceColumns = ColumnOrderHint.getSortedColumns(_connectorRepository, sourceConnectorId, tableMetaData1); final Set mappedTargetColumns = new HashSet<>(tableMetaData2.getColumnMetaData()); for (final ColumnMetaData sourceColumn : sourceColumns) { final ColumnMapperResult mapping = columnMapper.map(sourceColumn, tableMetaData2); final List targetColumns = mapping.getColumns(); final String sourceColumnName = sourceColumnNameMapper.mapColumnName(sourceColumn, tableMetaData1); if (targetColumns.isEmpty()) { if (mapping.isEmptyColumnListOk()) { _schemaCompatibilityIssues.addIssue(new DroppedColumnIssue("No mapping column(s) found for: " + tableName + ":" + sourceColumn + " -> Will be dropped", sourceColumn)); } else { _schemaCompatibilityIssues.addIssue(new MissingColumnIssue("No mapping column(s) found for: " + tableName + ":" + sourceColumn, sourceColumn)); } } mappedTargetColumns.removeAll(targetColumns); for (final ColumnMetaData targetColumn : targetColumns) { final String targetColumnName = targetColumnNameMapper.mapColumnName(targetColumn, tableMetaData2); final ColumnTypeMapping columnTypeMapping = commonColumnTypeResolver.getCommonColumnTypeMapping( sourceColumn, targetConnectorId, targetColumn); if (columnTypeMapping == null) { _schemaCompatibilityIssues.addIssue(new IncompatibleColumnsIssue( tableName + ":" + sourceColumn + ": Columns have incompatible types: " + sourceColumnName + "/" + sourceColumn.getColumnTypeName() + "/" + sourceColumn.getColumnClassName() + " vs. " + targetColumnName + "/" + targetColumn.getColumnTypeName() + "/" + targetColumn.getColumnClassName(), sourceColumn, targetColumn)); } } } for (final ColumnMetaData targetColumn : mappedTargetColumns) { if (targetColumn.isNullable()) { _schemaCompatibilityIssues.addIssue(new AdditionalColumnIssue("Unmapped target column (Will be null): " + tableName + ":" + targetColumn, targetColumn)); } else { _schemaCompatibilityIssues.addIssue(new AdditionalNonNullColumnIssue("Unmapped target column with not-null " + "constraint will cause error" + " : " + tableName + ":" + targetColumn, targetColumn)); } } return _schemaCompatibilityIssues; } private void checkEqualTables(final List sourceTableMetaData, final DatabaseMetaData targetDatabaseMetaData, final TableMapper tableMapper) throws SQLException { for (final TableMetaData tableMetaData : sourceTableMetaData) { final TableMetaData targetTableMetaData = tableMapper.map(tableMetaData, targetDatabaseMetaData); if (targetTableMetaData == null) { _schemaCompatibilityIssues.addIssue(new MissingTableIssue("Table " + tableMetaData + " is unknown/unmapped in target schema", tableMetaData)); } } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy