package org.hibernate.cfg.reveng; import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import org.hibernate.cfg.reveng.dialect.MetaDataDialect; import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider; import org.hibernate.exception.spi.SQLExceptionConverter; import org.hibernate.mapping.ForeignKey; import org.hibernate.mapping.Table; public class JDBCReader { 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<Table> readDatabaseSchema(DatabaseCollector dbs, String catalog, String schema, ProgressListener progress) { try { ReverseEngineeringRuntimeInfo info = new ReverseEngineeringRuntimeInfo(provider, sec, dbs); getMetaDataDialect().configure(info); revengStrategy.configure(info); Set<Table> hasIndices = new HashSet<Table>(); List<SchemaSelection> schemaSelectors = revengStrategy.getSchemaSelections(); List<Table> foundTables = new ArrayList<Table>(); if(schemaSelectors==null) { foundTables.addAll(TableProcessor.processTables(getMetaDataDialect(), revengStrategy, defaultSchema, defaultCatalog, dbs, new SchemaSelection(catalog, schema), hasIndices, progress)); } else { for (Iterator<SchemaSelection> iter = schemaSelectors.iterator(); iter.hasNext();) { SchemaSelection selection = iter.next(); foundTables.addAll(TableProcessor.processTables(getMetaDataDialect(), revengStrategy, defaultSchema, defaultCatalog, dbs, selection, hasIndices, progress)); } } Iterator<Table> tables = foundTables.iterator(); // not dbs.iterateTables() to avoid "double-read" of columns etc. while ( tables.hasNext() ) { Table table = tables.next(); BasicColumnProcessor.processBasicColumns(getMetaDataDialect(), revengStrategy, defaultSchema, defaultCatalog, table, progress); PrimaryKeyProcessor.processPrimaryKey(getMetaDataDialect(), revengStrategy, defaultSchema, defaultCatalog, dbs, table); if(hasIndices.contains(table)) { IndexProcessor.processIndices(getMetaDataDialect(), defaultSchema, defaultCatalog, table); } } tables = foundTables.iterator(); //dbs.iterateTables(); Map<String, List<ForeignKey>> 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<String, List<ForeignKey>> resolveForeignKeys(DatabaseCollector dbs, Iterator<Table> tables, ProgressListener progress) { List<ForeignKeysInfo> fks = new ArrayList<ForeignKeysInfo>(); 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 = ForeignKeyProcessor.processForeignKeys(getMetaDataDialect(), revengStrategy, defaultSchema, defaultCatalog, dbs, table, progress); fks.add( foreignKeys ); } Map<String, List<ForeignKey>> oneToManyCandidates = new HashMap<String, List<ForeignKey>>(); for (Iterator<ForeignKeysInfo> iter = fks.iterator(); iter.hasNext();) { ForeignKeysInfo element = iter.next(); Map<String, List<ForeignKey>> map = element.process( revengStrategy ); // the actual foreignkey is created here. mergeMultiMap( oneToManyCandidates, map ); } return oneToManyCandidates; } public MetaDataDialect getMetaDataDialect() { return metadataDialect; } private void mergeMultiMap(Map<String, List<ForeignKey>> dest, Map<String, List<ForeignKey>> src) { Iterator<Entry<String, List<ForeignKey>>> items = src.entrySet().iterator(); while ( items.hasNext() ) { Entry<String, List<ForeignKey>> element = items.next(); List<ForeignKey> existing = dest.get( element.getKey() ); if(existing == null) { dest.put( element.getKey(), element.getValue() ); } else { existing.addAll(element.getValue()); } } } static class NoopProgressListener implements ProgressListener { public void startSubTask(String name) { // noop }; } } public List<Table> readDatabaseSchema(DatabaseCollector dbs, String catalog, String schema) { return readDatabaseSchema(dbs, catalog, schema, new NoopProgressListener()); } public Set<String> readSequences(String sql) { Set<String> sequences = new HashSet<String>(); 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; } }