package org.hibernate.cfg.reveng;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.JDBCException;
import org.hibernate.MappingException;
import org.hibernate.cfg.JDBCBinderException;
import org.hibernate.cfg.reveng.dialect.MetaDataDialect;
import org.hibernate.connection.ConnectionProvider;
import org.hibernate.exception.SQLExceptionConverter;
import org.hibernate.mapping.Column;
import org.hibernate.mapping.ForeignKey;
import org.hibernate.mapping.Index;
import org.hibernate.mapping.PrimaryKey;
import org.hibernate.mapping.Table;
import org.hibernate.mapping.UniqueKey;
import org.hibernate.sql.Alias;
import org.hibernate.util.StringHelper;
public class JDBCReader {
private static final Log log = LogFactory.getLog(JDBCReader.class);
private final ReverseEngineeringStrategy revengStrategy;
private MetaDataDialect metadataDialect;
private final ConnectionProvider provider;
private final SQLExceptionConverter sec;
private final String defaultSchema;
private final String defaultCatalog;
public JDBCReader(MetaDataDialect dialect, ConnectionProvider provider, SQLExceptionConverter sec, String defaultCatalog, String defaultSchema, ReverseEngineeringStrategy reveng) {
this.metadataDialect = dialect;
this.provider = provider;
this.sec = sec;
this.revengStrategy = reveng;
this.defaultCatalog = defaultCatalog;
this.defaultSchema = defaultSchema;
if(revengStrategy==null) {
throw new IllegalStateException("Strategy cannot be null");
}
}
public List readDatabaseSchema(DatabaseCollector dbs, String catalog, String schema, ProgressListener progress) {
try {
ReverseEngineeringRuntimeInfo info = new ReverseEngineeringRuntimeInfo(provider, sec, dbs);
getMetaDataDialect().configure(info);
revengStrategy.configure(info);
Set hasIndices = new HashSet();
List schemaSelectors = revengStrategy.getSchemaSelections();
List foundTables = new ArrayList();
if(schemaSelectors==null) {
foundTables.addAll( processTables(dbs, new SchemaSelection(catalog, schema), hasIndices, progress) );
} else {
for (Iterator iter = schemaSelectors.iterator(); iter.hasNext();) {
SchemaSelection selection = (SchemaSelection) iter.next();
foundTables.addAll( processTables(dbs, selection, hasIndices, progress) );
}
}
Iterator tables = foundTables.iterator(); // not dbs.iterateTables() to avoid "double-read" of columns etc.
while ( tables.hasNext() ) {
Table table = (Table) tables.next();
processBasicColumns(table, progress);
processPrimaryKey(dbs, table);
if(hasIndices.contains(table)) {
processIndices(table);
}
}
tables = foundTables.iterator(); //dbs.iterateTables();
Map oneToManyCandidates = resolveForeignKeys( dbs, tables, progress );
dbs.setOneToManyCandidates(oneToManyCandidates);
return foundTables;
} finally {
getMetaDataDialect().close();
revengStrategy.close();
}
}
/**
* Iterates the tables and find all the foreignkeys that refers to something that is available inside the DatabaseCollector.
* @param dbs
* @param progress
* @param tables
* @return
*/
private Map resolveForeignKeys(DatabaseCollector dbs, Iterator tables, ProgressListener progress) {
List fks = new ArrayList();
while ( tables.hasNext() ) {
Table table = (Table) tables.next();
// Done here after the basic process of collections as we might not have touched
// all referenced tables (this ensure the columns are the same instances througout the basic JDBC derived model.
// after this stage it should be "ok" to divert from keeping columns in sync as it can be required if the same
//column is used with different aliases in the ORM mapping.
ForeignKeysInfo foreignKeys = processForeignKeys(dbs, table, progress);
fks.add( foreignKeys );
}
Map oneToManyCandidates = new HashMap();
for (Iterator iter = fks.iterator(); iter.hasNext();) {
ForeignKeysInfo element = (ForeignKeysInfo) iter.next();
Map map = element.process( revengStrategy ); // the actual foreignkey is created here.
mergeMultiMap( oneToManyCandidates, map );
}
return oneToManyCandidates;
}
static class ForeignKeysInfo {
final Map dependentTables;
final Map dependentColumns;
final Map referencedColumns;
private final Table referencedTable;
public ForeignKeysInfo(Table referencedTable, Map tables, Map columns, Map refColumns) {
this.referencedTable = referencedTable;
this.dependentTables = tables;
this.dependentColumns = columns;
this.referencedColumns = refColumns;
}
Map process(ReverseEngineeringStrategy revengStrategy) {
Map oneToManyCandidates = new HashMap();
Iterator iterator = dependentTables.entrySet().iterator();
while (iterator.hasNext() ) {
Map.Entry entry = (Map.Entry) iterator.next();
String fkName = (String) entry.getKey();
Table fkTable = (Table) entry.getValue();
List columns = (List) dependentColumns.get(fkName);
List refColumns = (List) referencedColumns.get(fkName);
String className = revengStrategy.tableToClassName(TableIdentifier.create(referencedTable) );
ForeignKey key = fkTable.createForeignKey(fkName, columns, className, refColumns);
key.setReferencedTable(referencedTable);
addToMultiMap(oneToManyCandidates, className, key);
}
// map<className, foreignkey>
return oneToManyCandidates;
}
}
protected ForeignKeysInfo processForeignKeys(DatabaseCollector dbs, Table referencedTable, ProgressListener progress) throws JDBCBinderException {
// foreign key name to list of columns
Map dependentColumns = new HashMap();
// foreign key name to Table
Map dependentTables = new HashMap();
Map referencedColumns = new HashMap();
short bogusFkName = 0;
// first get all the relationships dictated by the database schema
Iterator exportedKeyIterator = null;
log.debug("Calling getExportedKeys on " + referencedTable);
progress.startSubTask("Finding exported foreignkeys on " + referencedTable.getName());
try {
Map exportedKeyRs = null;
exportedKeyIterator = getMetaDataDialect().getExportedKeys(getCatalogForDBLookup(referencedTable.getCatalog()), getSchemaForDBLookup(referencedTable.getSchema()), referencedTable.getName() );
try {
while (exportedKeyIterator.hasNext() ) {
exportedKeyRs = (Map) exportedKeyIterator.next();
String fkCatalog = getCatalogForModel((String) exportedKeyRs.get("FKTABLE_CAT"));
String fkSchema = getSchemaForModel((String) exportedKeyRs.get("FKTABLE_SCHEM"));
String fkTableName = (String) exportedKeyRs.get("FKTABLE_NAME");
String fkColumnName = (String) exportedKeyRs.get("FKCOLUMN_NAME");
String pkColumnName = (String) exportedKeyRs.get("PKCOLUMN_NAME");
String fkName = (String) exportedKeyRs.get("FK_NAME");
short keySeq = ((Short)exportedKeyRs.get("KEY_SEQ")).shortValue();
Table fkTable = dbs.getTable(fkSchema, fkCatalog, fkTableName);
if(fkTable==null) {
// filter out stuff we don't have tables for!
log.debug("Foreign key " + fkName + " references unknown or filtered table " + Table.qualify(fkCatalog, fkSchema, fkTableName) );
continue;
} else {
log.debug("Foreign key " + fkName);
}
// TODO: if there is a relation to a column which is not a pk
// then handle it as a property-ref
if (keySeq == 0) {
bogusFkName++;
}
if (fkName == null) {
// somehow reuse hibernates name generator ?
fkName = Short.toString(bogusFkName);
}
//Table fkTable = mappings.addTable(fkSchema, fkCatalog, fkTableName, null, false);
List depColumns = (List) dependentColumns.get(fkName);
if (depColumns == null) {
depColumns = new ArrayList();
dependentColumns.put(fkName,depColumns);
dependentTables.put(fkName, fkTable);
}
else {
Object previousTable = dependentTables.get(fkName);
if(fkTable != previousTable) {
throw new JDBCBinderException("Foreign key name (" + fkName + ") mapped to different tables! previous: " + previousTable + " current:" + fkTable);
}
}
Column column = new Column(fkColumnName);
Column existingColumn = fkTable.getColumn(column);
column = existingColumn==null ? column : existingColumn;
depColumns.add(column);
List primColumns = (List) referencedColumns.get(fkName);
if (primColumns == null) {
primColumns = new ArrayList();
referencedColumns.put(fkName,primColumns);
}
Column refColumn = new Column(pkColumnName);
existingColumn = referencedTable.getColumn(refColumn);
refColumn = existingColumn==null?refColumn:existingColumn;
primColumns.add(refColumn);
}
}
finally {
try {
if(exportedKeyIterator!=null) {
getMetaDataDialect().close(exportedKeyIterator);
}
} catch(JDBCException se) {
log.warn("Exception while closing result set for foreign key meta data",se);
}
}
} catch(JDBCException se) {
//throw sec.convert(se, "Exception while reading foreign keys for " + referencedTable, null);
log.warn("Exception while reading foreign keys for " + referencedTable + " [" + se.toString() + "]", se);
// sybase (and possibly others has issues with exportedkeys) see HBX-411
// we continue after this to allow user provided keys to be added.
}
List userForeignKeys = revengStrategy.getForeignKeys(TableIdentifier.create(referencedTable));
if(userForeignKeys!=null) {
Iterator iterator = userForeignKeys.iterator();
while ( iterator.hasNext() ) {
ForeignKey element = (ForeignKey) iterator.next();
if(!equalTable(referencedTable, element.getReferencedTable() ) ) {
log.debug("Referenced table " + element.getReferencedTable().getName() + " is not " + referencedTable + ". Ignoring userdefined foreign key " + element );
continue; // skip non related foreign keys
}
String userfkName = element.getName();
Table userfkTable = element.getTable();
List userColumns = element.getColumns();
List userrefColumns = element.getReferencedColumns();
Table deptable = (Table) dependentTables.get(userfkName);
if(deptable!=null) { // foreign key already defined!?
throw new MappingException("Foreign key " + userfkName + " already defined in the database!");
}
deptable = dbs.getTable(userfkTable.getSchema(), userfkTable.getCatalog(), userfkTable.getName() );
if(deptable==null) {
// filter out stuff we don't have tables for!
log.debug("User defined foreign key " + userfkName + " references unknown or filtered table " + TableIdentifier.create(userfkTable) );
continue;
}
dependentTables.put(userfkName, deptable);
List depColumns = new ArrayList(userColumns.size() );
Iterator colIterator = userColumns.iterator();
while(colIterator.hasNext() ) {
Column jdbcColumn = (Column) colIterator.next();
Column column = new Column(jdbcColumn.getName() );
Column existingColumn = deptable.getColumn(column);
column = existingColumn==null ? column : existingColumn;
depColumns.add(column);
}
List refColumns = new ArrayList(userrefColumns.size() );
colIterator = userrefColumns.iterator();
while(colIterator.hasNext() ) {
Column jdbcColumn = (Column) colIterator.next();
Column column = new Column(jdbcColumn.getName() );
Column existingColumn = referencedTable.getColumn(column);
column = existingColumn==null ? column : existingColumn;
refColumns.add(column);
}
referencedColumns.put(userfkName, refColumns );
dependentColumns.put(userfkName, depColumns );
}
}
return new ForeignKeysInfo(referencedTable, dependentTables, dependentColumns, referencedColumns);
}
/**
* @param dbs
* @param catalog
* @param schema
* @param table
* @param primaryKeys
* @return
* @throws SQLException
*/
private void processPrimaryKey(DatabaseCollector dbs, Table table) {
List columns = new ArrayList();
PrimaryKey key = null;
Iterator primaryKeyIterator = null;
try {
Map primaryKeyRs = null;
primaryKeyIterator = getMetaDataDialect().getPrimaryKeys(getCatalogForDBLookup(table.getCatalog()), getSchemaForDBLookup(table.getSchema()), table.getName() );
while (primaryKeyIterator.hasNext() ) {
primaryKeyRs = (Map) primaryKeyIterator.next();
/*String ownCatalog = primaryKeyRs.getString("TABLE_CAT");
String ownSchema = primaryKeyRs.getString("TABLE_SCHEM");
String ownTable = primaryKeyRs.getString("TABLE_NAME");*/
String columnName = (String) primaryKeyRs.get("COLUMN_NAME");
short seq = ((Short)primaryKeyRs.get("KEY_SEQ")).shortValue();
String name = (String) primaryKeyRs.get("PK_NAME");
if(key==null) {
key = new PrimaryKey();
key.setName(name);
key.setTable(table);
if(table.getPrimaryKey()!=null) {
throw new JDBCBinderException(table + " already has a primary key!"); //TODO: ignore ?
}
table.setPrimaryKey(key);
}
else {
if(!(name==key.getName() ) && name!=null && !name.equals(key.getName() ) ) {
throw new JDBCBinderException("Duplicate names found for primarykey. Existing name: " + key.getName() + " JDBC name: " + name + " on table " + table);
}
}
columns.add(new Object[] { new Short(seq), columnName});
}
} finally {
if (primaryKeyIterator!=null) {
try {
getMetaDataDialect().close(primaryKeyIterator);
} catch(JDBCException se) {
log.warn("Exception when closing resultset for reading primary key information",se);
}
}
}
// sort the columns accoring to the key_seq.
Collections.sort(columns,new Comparator() {
public boolean equals(Object obj) {
return super.equals(obj);
}
public int compare(Object o1, Object o2) {
Short left = (Short) ( (Object[]) o1)[0];
Short right = (Short) ( (Object[]) o2)[0];
return left.compareTo(right);
}
public int hashCode() {
return super.hashCode();
}
});
List t = new ArrayList(columns.size());
Iterator cols = columns.iterator();
while (cols.hasNext() ) {
Object[] element = (Object[]) cols.next();
t.add(element[1]);
}
columns = t;
if(key==null) {
log.warn("The JDBC driver didn't report any primary key columns in " + table.getName() + ". Asking rev.eng. strategy" );
List userPrimaryKey = revengStrategy.getPrimaryKeyColumnNames(TableIdentifier.create(table));
if(userPrimaryKey!=null && !userPrimaryKey.isEmpty()) {
key = new PrimaryKey();
key.setName(new Alias(15, "PK").toAliasString( table.getName()));
key.setTable(table);
if(table.getPrimaryKey()!=null) {
throw new JDBCBinderException(table + " already has a primary key!"); //TODO: ignore ?
}
table.setPrimaryKey(key);
columns = new ArrayList(userPrimaryKey);
} else {
log.warn("Rev.eng. strategy did not report any primary key columns for " + table.getName());
}
}
Iterator suggestedPrimaryKeyStrategyName = getMetaDataDialect().getSuggestedPrimaryKeyStrategyName( getCatalogForDBLookup(table.getCatalog()), getSchemaForDBLookup(table.getSchema()), table.getName() );
try {
if(suggestedPrimaryKeyStrategyName.hasNext()) {
Map m = (Map) suggestedPrimaryKeyStrategyName.next();
String suggestion = (String) m.get( "HIBERNATE_STRATEGY" );
if(suggestion!=null) {
dbs.addSuggestedIdentifierStrategy( table.getCatalog(), table.getSchema(), table.getName(), suggestion );
}
}
} finally {
if(suggestedPrimaryKeyStrategyName!=null) {
try {
getMetaDataDialect().close(suggestedPrimaryKeyStrategyName);
} catch(JDBCException se) {
log.warn("Exception while closing iterator for suggested primary key strategy name",se);
}
}
}
if(key!=null) {
cols = columns.iterator();
while (cols.hasNext() ) {
String name = (String) cols.next();
// should get column from table if it already exists!
Column col = getColumn(table, name);
key.addColumn(col);
}
log.debug("primary key for " + table + " -> " + key);
}
}
private boolean safeEquals(Object value, Object tf) {
if(value==tf) return true;
if(value==null) return false;
return value.equals(tf);
}
private Collection processTables(DatabaseCollector dbs, SchemaSelection schemaSelection, Set hasIndices, ProgressListener progress) {
Map tableRs = null;
Iterator tableIterator = null;
List tables = new ArrayList();
boolean multiSchema = false; // TODO: the code below detects if the reveng is multischema'ed, but not used for anything yet. should be used to remove schema/catalog info from output if only one schema/catalog used.
try {
progress.startSubTask("Finding tables in " + schemaSelection);
tableIterator = getMetaDataDialect().getTables(StringHelper.replace(schemaSelection.getMatchCatalog(),".*", "%"),
StringHelper.replace(schemaSelection.getMatchSchema(),".*", "%"),
StringHelper.replace(schemaSelection.getMatchTable(),".*", "%"));
String[] lastQualifier = null;
String[] foundQualifier = new String[2];
while (tableIterator.hasNext() ) {
tableRs = (Map) tableIterator.next();
String tableName = (String) tableRs.get("TABLE_NAME");
String schemaName = (String) tableRs.get("TABLE_SCHEM");
String catalogName = (String) tableRs.get("TABLE_CAT");
TableIdentifier ti = new TableIdentifier(catalogName, schemaName, tableName);
if(revengStrategy.excludeTable(ti) ) {
log.debug("Table " + ti + " excluded by strategy");
continue;
}
if(!multiSchema) {
foundQualifier[0] = catalogName;
foundQualifier[1] = schemaName;
if(lastQualifier==null) {
lastQualifier=new String[2];
lastQualifier[0] = foundQualifier[0];
lastQualifier[1] = foundQualifier[1];
}
if((!safeEquals(lastQualifier[0],foundQualifier[0])) || (!safeEquals(lastQualifier[1],foundQualifier[1]))) {
multiSchema = true;
}
}
tables.add(new HashMap(tableRs));
}
}
finally {
try {
if (tableIterator!=null) getMetaDataDialect().close(tableIterator);
}
catch (Exception ignore) {
}
}
List processedTables = new ArrayList();
tableIterator = tables.iterator();
while (tableIterator.hasNext() ) {
tableRs = (Map) tableIterator.next();
String tableName = (String) tableRs.get("TABLE_NAME");
String schemaName = (String) tableRs.get("TABLE_SCHEM");
String catalogName = (String) tableRs.get("TABLE_CAT");
/*TableIdentifier ti = new TableIdentifier(catalogName, schemaName, tableName);
if(revengStrategy.excludeTable(ti) ) {
log.debug("Table " + ti + " excluded by strategy");
continue;
}*/
String comment = (String) tableRs.get("REMARKS");
String tableType = (String) tableRs.get("TABLE_TYPE");
if(dbs.getTable(schemaName, catalogName, tableName)!=null) {
log.debug("Ignoring " + tableName + " since it has already been processed");
continue;
} else {
if ( ("TABLE".equalsIgnoreCase(tableType) || "VIEW".equalsIgnoreCase(tableType) /*|| "SYNONYM".equals(tableType) */) ) { //||
// ("SYNONYM".equals(tableType) && isOracle() ) ) { // only on oracle ? TODO: HBX-218
// it's a regular table or a synonym
// ensure schema and catalogname is truly empty (especially mysql returns null schema, "" catalog)
if(schemaName!=null && schemaName.trim().length()==0) {
schemaName = null;
}
if(catalogName!=null && catalogName.trim().length()==0) {
catalogName=null;
}
log.debug("Adding table " + tableName + " of type " + tableType);
progress.startSubTask("Found " + tableName);
Table table = dbs.addTable(getSchemaForModel(schemaName), getCatalogForModel(catalogName), tableName);
table.setComment(comment);
if(tableType.equalsIgnoreCase("TABLE")) {
hasIndices.add(table);
}
processedTables.add( table );
}
else {
log.debug("Ignoring table " + tableName + " of type " + tableType);
}
}
}
return processedTables;
}
private void processBasicColumns(Table table, ProgressListener progress) {
// get the columns
String qualify = Table.qualify(table.getCatalog(), table.getSchema(), table.getName() );
Iterator columnIterator = null;
try {
Map columnRs = null;
log.debug("Finding columns for " + qualify );
progress.startSubTask("Finding columns for " + qualify);
columnIterator = getMetaDataDialect().getColumns(getCatalogForDBLookup(table.getCatalog()), getSchemaForDBLookup(table.getSchema()), table.getName(), null);
//dumpHeader(columnRs);
while (columnIterator.hasNext() ) {
//dumpRow(columnRs);
columnRs = (Map) columnIterator.next();
String tableName = (String) columnRs.get("TABLE_NAME");
int sqlType = ((Integer)columnRs.get("DATA_TYPE")).intValue();
//String sqlTypeName = (String) columnRs.get("TYPE_NAME");
String columnName = (String) columnRs.get("COLUMN_NAME");
String comment = (String) columnRs.get("REMARKS");
TableIdentifier ti = TableIdentifier.create(table);
if(revengStrategy.excludeColumn(ti, columnName)) {
log.debug("Column " + ti + "." + columnName + " excluded by strategy");
continue;
}
if(!tableName.equals(table.getName())) {
log.debug("Table name " + tableName + " does not match requested " + table.getName() + ". Ignoring column " + columnName + " since it either is invalid or a duplicate" );
continue;
}
//String columnDefaultValue = columnRs.getString("COLUMN_DEF"); TODO: only read if have a way to avoid issues with clobs/lobs and similar
int dbNullability = ((Integer)columnRs.get("NULLABLE")).intValue();
boolean isNullable = true;
switch (dbNullability) {
case DatabaseMetaData.columnNullable:
case DatabaseMetaData.columnNullableUnknown:
isNullable = true;
break;
case DatabaseMetaData.columnNoNulls:
isNullable = false;
break;
default:
isNullable = true;
}
int size = ((Integer)columnRs.get("COLUMN_SIZE")).intValue();
int decimalDigits = ((Integer)columnRs.get("DECIMAL_DIGITS")).intValue();
Column column = new Column();
column.setName(quote(columnName));
Column existing = table.getColumn(column);
if(existing!=null) {
// TODO: should we just pick it up and fill it up with whatever we get from the db instead ?
throw new JDBCBinderException(column + " already exists in " + qualify);
}
//TODO: column.setSqlType(sqlTypeName); //this does not work 'cos the precision/scale/length are not retured in TYPE_NAME
//column.setSqlType(sqlTypeName);
column.setComment(comment);
column.setSqlTypeCode(new Integer(sqlType) );
if(intBounds(size) ) {
if(JDBCToHibernateTypeHelper.typeHasLength(sqlType) ) {
column.setLength(size);
}
if(JDBCToHibernateTypeHelper.typeHasScaleAndPrecision(sqlType) ) {
column.setPrecision(size);
}
}
if(intBounds(decimalDigits) ) {
if(JDBCToHibernateTypeHelper.typeHasScaleAndPrecision(sqlType) ) {
column.setScale(decimalDigits);
}
}
column.setNullable(isNullable);
// columnDefaultValue is useless for Hibernate
// isIndexed (available via Indexes)
// unique - detected when getting indexes
// isPk - detected when finding primary keys
table.addColumn(column);
}
}
finally {
if(columnIterator!=null) {
try {
getMetaDataDialect().close(columnIterator);
} catch(JDBCException se) {
log.warn("Exception while closing iterator for column meta data",se);
}
}
}
}
private String quote(String columnName) {
if(columnName==null) return columnName;
if(getMetaDataDialect().needQuote(columnName)) {
if(columnName.length()>1 && columnName.charAt(0)=='`' && columnName.charAt(columnName.length()-1)=='`') {
return columnName; // avoid double quoting
}
return "`" + columnName + "`";
} else {
return columnName;
}
}
public MetaDataDialect getMetaDataDialect() {
return metadataDialect;
}
/**
* @param size
* @return
*/
private boolean intBounds(int size) {
return size>=0 && size!=Integer.MAX_VALUE;
}
private void processIndices(Table table) {
Map indexes = new HashMap(); // indexname (String) -> Index
Map uniquekeys = new HashMap(); // name (String) -> UniqueKey
Map uniqueColumns = new HashMap(); // Column -> List<Index>
Iterator indexIterator = null;
try {
Map indexRs = null;
indexIterator = getMetaDataDialect().getIndexInfo(getCatalogForDBLookup(table.getCatalog()), getSchemaForDBLookup(table.getSchema()), table.getName());
while (indexIterator.hasNext() ) {
indexRs = (Map) indexIterator.next();
String indexName = (String) indexRs.get("INDEX_NAME");
String columnName = (String) indexRs.get("COLUMN_NAME");
boolean unique = !((Boolean)indexRs.get("NON_UNIQUE")).booleanValue();
if (columnName != null || indexName != null) { // both can be non-null with statistical indexs which we don't have any use for.
if(unique) {
UniqueKey key = (UniqueKey) uniquekeys.get(indexName);
if (key==null) {
key = new UniqueKey();
key.setName(indexName);
key.setTable(table);
table.addUniqueKey(key);
uniquekeys.put(indexName, key);
}
if(indexes.containsKey(indexName) ) {
throw new JDBCBinderException("UniqueKey exists also as Index! ");
}
Column column = getColumn(table, columnName);
key.addColumn(column);
if (unique && key.getColumnSpan()==1) {
// make list of columns that has the chance of being unique
List l = (List) uniqueColumns.get(column);
if (l == null) {
l = new ArrayList();
uniqueColumns.put(column, l);
}
l.add(key);
}
}
else {
Index index = (Index) indexes.get(indexName);
if(index==null) {
index = new Index();
index.setName(indexName);
index.setTable(table);
table.addIndex(index);
indexes.put(indexName, index);
}
if(uniquekeys.containsKey(indexName) ) {
throw new JDBCBinderException("Index exists also as Unique! ");
}
Column column = getColumn(table, columnName);
index.addColumn(column);
}
}
else {
if(DatabaseMetaData.tableIndexStatistic != ((Short)indexRs.get("TYPE")).shortValue() ) {
log.warn("Index was not statistical, but no column name was found in " + indexName);
}
}
}
}
catch (JDBCException t) {
log.warn("Exception while trying to get indexinfo on " + Table.qualify(table.getCatalog(), table.getSchema(), table.getName() ) + "=" + t.getMessage() );
// Bug #604761 Oracle getIndexInfo() needs major grants And other dbs sucks too ;)
// http://sourceforge.net/tracker/index.php?func=detail&aid=604761&group_id=36044&atid=415990
}
finally {
if (indexIterator != null) {
try {
getMetaDataDialect().close(indexIterator);
} catch(JDBCException se) {
log.warn("Exception while trying to close resultset for index meta data",se);
}
}
}
// mark columns that are unique TODO: multiple columns are not unique on their own.
Iterator iterator = uniqueColumns.entrySet().iterator();
while (iterator.hasNext() ) {
Map.Entry entry = (Map.Entry) iterator.next();
Column col = (Column) entry.getKey();
Iterator keys = ( (List)entry.getValue() ).iterator();
while (keys.hasNext() ) {
UniqueKey key = (UniqueKey) keys.next();
if(key.getColumnSpan()==1) {
col.setUnique(true);
}
}
}
iterator = uniquekeys.entrySet().iterator();
while(iterator.hasNext()) {
// if keyset has no overlaps with primary key (table.getPrimaryKey())
// if only key matches then mark as setNaturalId(true);
iterator.next();
}
}
private void mergeMultiMap(Map dest, Map src) {
Iterator items = src.entrySet().iterator();
while ( items.hasNext() ) {
Map.Entry element = (Map.Entry) items.next();
List existing = (List) dest.get( element.getKey() );
if(existing == null) {
dest.put( element.getKey(), element.getValue() );
}
else {
existing.addAll( (List)element.getValue() );
}
}
}
private boolean equalTable(Table table1, Table table2) {
return table1.getName().equals(table2.getName())
&& ( equal(table1.getSchema(), table2.getSchema() )
&& ( equal(table1.getCatalog(), table2.getCatalog() ) ) );
}
private boolean equal(String str, String str2) {
if(str==str2) return true;
if(str!=null && str.equals(str2) ) return true;
return false;
}
static private void addToMultiMap(Map multimap, String key, Object item) {
List existing = (List) multimap.get(key);
if(existing == null) {
existing = new ArrayList();
multimap.put(key, existing);
}
existing.add(item);
}
private Column getColumn(Table table, String columnName) {
Column column = new Column();
column.setName(quote(columnName));
Column existing = table.getColumn(column);
if(existing!=null) {
column = existing;
}
return column;
}
static class NoopProgressListener implements ProgressListener {
public void startSubTask(String name) { // noop };
}
}
public List readDatabaseSchema(DatabaseCollector dbs, String catalog, String schema) {
return readDatabaseSchema(dbs, catalog, schema, new NoopProgressListener());
}
/** If catalog is equal to defaultCatalog then we return null so it will be null in the generated code. */
protected String getCatalogForModel(String catalog) {
if(catalog==null) return null;
if(catalog.equals(defaultCatalog)) return null;
return catalog;
}
/** If catalog is equal to defaultSchema then we return null so it will be null in the generated code. */
protected String getSchemaForModel(String schema) {
if(schema==null) return null;
if(schema.equals(defaultSchema)) return null;
return schema;
}
protected String getCatalogForDBLookup(String catalog) {
return catalog==null?defaultCatalog:catalog;
}
protected String getSchemaForDBLookup(String schema) {
return schema==null?defaultSchema:schema;
}
public Set readSequences(String sql) {
Set sequences = new HashSet();
if (sql!=null) {
Connection connection = null;
try {
connection = provider.getConnection();
Statement statement = null;
ResultSet rs = null;
try {
statement = connection.createStatement();
rs = statement.executeQuery(sql);
while ( rs.next() ) {
sequences.add( rs.getString(1).toLowerCase().trim() );
}
}
finally {
if (rs!=null) rs.close();
if (statement!=null) statement.close();
}
} catch (SQLException e) {
sec.convert(e, "Problem while closing connection", null);
}
finally {
if(connection!=null)
try {
provider.closeConnection( connection );
}
catch (SQLException e) {
sec.convert(e, "Problem while closing connection", null);
}
}
}
return sequences;
}
}