private static SQLException createIncompatibleDataException(
     final String tableName,
     final int index,
     final ColumnType columnType,
     final String columnName,
     final Object data1,
     final Object data2) {
   return new UnequalDataException(
       tableName
           + ": Row "
           + index
           + ": Data not equal on column "
           + columnName
           + ": \n'"
           + data1
           + "'\n vs. \n'"
           + data2
           + "'\n, column class = "
           + columnType.getColumnClasses());
 }
  private void checkTableData(
      final String sourceConnectorId,
      final Connection sourceConnection,
      final SourceDatabaseConfiguration sourceConfiguration,
      final TableMetaData sourceTableMetaData,
      final String targetConnectorId,
      final Connection targetConnection,
      final SourceDatabaseConfiguration targetConfiguration,
      final TableMetaData targetTableMetaData,
      final int numberOfCheckData)
      throws SQLException {
    final String tableName1 =
        _connectorRepository
            .getConnectorHint(sourceConnectorId, TableNameMapper.class)
            .getValue()
            .mapTableName(sourceTableMetaData);
    final String tableName2 =
        _connectorRepository
            .getConnectorHint(targetConnectorId, TableNameMapper.class)
            .getValue()
            .mapTableName(targetTableMetaData);
    final CommonColumnTypeResolverTool commonColumnTypeResolver =
        new CommonColumnTypeResolverTool(_connectorRepository);
    final ColumnNameMapper sourceColumnNameMapper =
        _connectorRepository.getConnectorHint(sourceConnectorId, ColumnNameMapper.class).getValue();
    final ColumnNameMapper targetColumnNameMapper =
        _connectorRepository.getConnectorHint(targetConnectorId, ColumnNameMapper.class).getValue();

    if (sourceTableMetaData.getRowCount() != targetTableMetaData.getRowCount()) {
      throw new UnequalNumberOfRowsException(
          "Number of rows is not equal: "
              + tableName1
              + "="
              + sourceTableMetaData.getRowCount()
              + " vs. "
              + tableName2
              + "="
              + targetTableMetaData.getRowCount());
    }

    LOG.info("Checking data of " + tableName1 + " <--> " + tableName2 + " started");

    final PreparedStatement selectStatement1 =
        new SelectStatementCreator(_connectorRepository, sourceConnectorId)
            .createSelectStatement(sourceConnection, tableName1, sourceTableMetaData);
    selectStatement1.setFetchSize(numberOfCheckData);

    sourceConfiguration.beforeSelect(sourceConnection, sourceConnectorId, sourceTableMetaData);
    final ResultSet resultSet1 = selectStatement1.executeQuery();
    sourceConfiguration.afterSelect(sourceConnection, sourceConnectorId, sourceTableMetaData);

    final PreparedStatement selectStatement2 =
        new SelectStatementCreator(_connectorRepository, targetConnectorId)
            .createMappedSelectStatement(
                targetConnection,
                sourceTableMetaData,
                tableName2,
                targetTableMetaData,
                sourceConnectorId);
    selectStatement2.setFetchSize(numberOfCheckData);

    targetConfiguration.beforeSelect(targetConnection, targetConnectorId, targetTableMetaData);
    final ResultSet resultSet2 = selectStatement2.executeQuery();
    targetConfiguration.afterSelect(targetConnection, targetConnectorId, targetTableMetaData);

    final List<ColumnMetaData> orderedSourceColumns =
        ColumnOrderHint.getSortedColumns(
            _connectorRepository, sourceConnectorId, sourceTableMetaData);
    final ColumnMapper columnMapper =
        _connectorRepository.getConnectorHint(targetConnectorId, ColumnMapper.class).getValue();

    int rowIndex = 1;

    try {
      while (resultSet1.next() && resultSet2.next() && rowIndex <= numberOfCheckData) {
        int targetColumnIndex = 1;

        for (int sourceColumnIndex = 1;
            sourceColumnIndex <= orderedSourceColumns.size();
            sourceColumnIndex++) {
          final ColumnMetaData sourceColumn = orderedSourceColumns.get(sourceColumnIndex - 1);
          final ColumnMapperResult mapping = columnMapper.map(sourceColumn, targetTableMetaData);

          for (final ColumnMetaData columnMetaData2 : mapping.getColumns()) {
            final ColumnTypeMapping columnTypeMapping =
                commonColumnTypeResolver.getCommonColumnTypeMapping(
                    sourceConnectorId, sourceColumn, targetConnectorId, columnMetaData2);
            final String columnName1 = sourceColumnNameMapper.mapColumnName(sourceColumn);
            final String columnName2 = targetColumnNameMapper.mapColumnName(columnMetaData2);

            if (columnTypeMapping == null) {
              throw new IncompatibleColumnsException(
                  tableName1
                      + ": Columns have incompatible types: "
                      + columnName1
                      + "/"
                      + sourceColumn.getColumnTypeName()
                      + " vs. "
                      + columnName2
                      + "/"
                      + columnMetaData2.getColumnTypeName());
            }

            final ColumnType sourceColumnType = columnTypeMapping.getSourceColumnType();
            Object data1 = sourceColumnType.getValue(resultSet1, sourceColumnIndex);
            data1 =
                columnTypeMapping.getColumnDataMapper().map(sourceColumn, columnMetaData2, data1);
            Object data2 =
                columnTypeMapping.getTargetColumnType().getValue(resultSet2, targetColumnIndex);

            switch (sourceColumnType) {
              case CLASS_STRING:
                final ConnectorInfo connectionInfo1 =
                    _connectorRepository.getConnectionInfo(sourceConnectorId);
                final ConnectorInfo connectionInfo2 =
                    _connectorRepository.getConnectionInfo(targetConnectorId);

                // See http://www.postgresql.org/docs/8.3/static/datatype-character.html
                if (DatabaseType.POSTGRESQL.equals(connectionInfo1.getDatabaseType())
                    || DatabaseType.POSTGRESQL.equals(connectionInfo2.getDatabaseType())) {
                  data1 = trim((String) data1);
                  data2 = trim((String) data2);
                }
                break;
              case CLASS_BLOB:
                final Blob blob1 = (Blob) data1;
                final Blob blob2 = (Blob) data2;
                data1 = createStringFromBlob(blob1);
                data2 = createStringFromBlob(blob2);
                break;

              default:
                // No conversion needed
                break;
            }

            if ((data1 == null && data2 != null) || (data1 != null && data2 == null)) {
              throw createIncompatibleDataException(
                  tableName1, rowIndex, sourceColumnType, columnName1, data1, data2);
            } else if (data1 != null && data2 != null && !data1.equals(data2)) {
              throw createIncompatibleDataException(
                  tableName1, rowIndex, sourceColumnType, columnName1, data1, data2);
            }
          }

          targetColumnIndex += mapping.getColumns().size();
        }

        rowIndex++;
      }
    } finally {
      try {
        resultSet1.close();
        selectStatement1.close();
        resultSet2.close();
        selectStatement2.close();
      } catch (final Exception e) {
      }
    }

    LOG.info("Checking data of " + tableName1 + " <--> " + tableName2 + " finished");
  }