You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cayenne.apache.org by aa...@apache.org on 2016/12/14 18:29:52 UTC

[3/9] cayenne git commit: New DbLoader for dbsync utils - loading process split in independent small steps - DbLoader always return new DataMap, it doesn't accept it from outside

New DbLoader for dbsync utils
 - loading process split in independent small steps
 - DbLoader always return new DataMap, it doesn't accept it from outside


Project: http://git-wip-us.apache.org/repos/asf/cayenne/repo
Commit: http://git-wip-us.apache.org/repos/asf/cayenne/commit/8c273022
Tree: http://git-wip-us.apache.org/repos/asf/cayenne/tree/8c273022
Diff: http://git-wip-us.apache.org/repos/asf/cayenne/diff/8c273022

Branch: refs/heads/master
Commit: 8c273022330129b87f502a30864e94b0281afc4b
Parents: 1e01dc8
Author: Nikita Timofeev <st...@gmail.com>
Authored: Thu Dec 8 18:09:29 2016 +0300
Committer: Nikita Timofeev <st...@gmail.com>
Committed: Thu Dec 8 18:09:29 2016 +0300

----------------------------------------------------------------------
 .../apache/cayenne/dbsync/merge/DbMerger.java   |   2 +-
 .../dbsync/merge/DropRelationshipToDb.java      |   2 +-
 .../reverse/db/DbAttributesBaseLoader.java      | 107 ----
 .../dbsync/reverse/db/DbAttributesLoader.java   |  43 --
 .../reverse/db/DbAttributesPerSchemaLoader.java | 131 -----
 .../cayenne/dbsync/reverse/db/DbLoader.java     | 534 -------------------
 .../reverse/db/DbLoaderConfiguration.java       |  84 ---
 .../dbsync/reverse/db/DbLoaderDelegate.java     |  63 ---
 .../reverse/db/DbRelationshipDetected.java      |  53 --
 .../dbsync/reverse/db/DbTableLoader.java        | 183 -------
 .../reverse/db/DefaultDbLoaderDelegate.java     |  63 ---
 .../cayenne/dbsync/reverse/db/ExportedKey.java  | 233 --------
 .../reverse/db/LoggingDbLoaderDelegate.java     |  61 ---
 .../dbsync/reverse/dbload/AbstractLoader.java   |  43 ++
 .../dbsync/reverse/dbload/AttributeLoader.java  | 137 +++++
 .../dbsync/reverse/dbload/DbLoadDataStore.java  |  88 +++
 .../cayenne/dbsync/reverse/dbload/DbLoader.java | 118 ++++
 .../reverse/dbload/DbLoaderConfiguration.java   |  85 +++
 .../dbsync/reverse/dbload/DbLoaderDelegate.java |  63 +++
 .../reverse/dbload/DbRelationshipDetected.java  |  54 ++
 .../reverse/dbload/DefaultDbLoaderDelegate.java |  65 +++
 .../dbsync/reverse/dbload/EntityLoader.java     | 106 ++++
 .../dbsync/reverse/dbload/ExportedKey.java      | 220 ++++++++
 .../reverse/dbload/ExportedKeyLoader.java       |  74 +++
 .../reverse/dbload/LoggingDbLoaderDelegate.java |  62 +++
 .../dbload/PerCatalogAndSchemaLoader.java       |  58 ++
 .../dbsync/reverse/dbload/PerEntityLoader.java  |  64 +++
 .../dbsync/reverse/dbload/PrimaryKeyLoader.java |  60 +++
 .../reverse/dbload/ProcedureColumnLoader.java   | 127 +++++
 .../dbsync/reverse/dbload/ProcedureLoader.java  |  77 +++
 .../reverse/dbload/RelationshipLoader.java      | 168 ++++++
 .../dbsync/reverse/filters/TableFilter.java     |   7 +-
 .../apache/cayenne/dbsync/merge/MergeCase.java  |  14 +-
 .../cayenne/dbsync/reverse/db/DbLoaderIT.java   | 408 --------------
 .../reverse/dbload/AttributeLoaderIT.java       | 154 ++++++
 .../dbsync/reverse/dbload/BaseLoaderIT.java     |  91 ++++
 .../dbsync/reverse/dbload/DbLoaderIT.java       | 105 ++++
 .../dbsync/reverse/dbload/EntityLoaderIT.java   |  98 ++++
 .../reverse/dbload/ExportedKeyLoaderIT.java     |  71 +++
 .../reverse/dbload/PrimaryKeyLoaderIT.java      |  60 +++
 .../reverse/dbload/RelationshipsLoaderIT.java   |  82 +++
 .../dbsync/reverse/filters/TableFilterTest.java |  81 ++-
 .../tools/dbimport/DbImportConfiguration.java   |   8 +-
 .../tools/dbimport/DefaultDbImportAction.java   |  26 +-
 .../dbimport/DefaultDbImportActionTest.java     | 139 ++---
 .../cayenne/modeler/action/MigrateAction.java   |   2 +-
 .../modeler/dialog/db/DbLoaderHelper.java       |  12 +-
 .../modeler/dialog/db/MergerOptions.java        |  15 +-
 .../dialog/db/ModelerDbImportAction.java        |  12 +-
 49 files changed, 2504 insertions(+), 2109 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cayenne/blob/8c273022/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/DbMerger.java
----------------------------------------------------------------------
diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/DbMerger.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/DbMerger.java
index 7adb0e5..102c3c4 100644
--- a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/DbMerger.java
+++ b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/DbMerger.java
@@ -121,7 +121,7 @@ public class DbMerger {
         Collection<DbEntity> existingFiltered = new LinkedList<>();
         for (DbEntity entity : existing.getDbEntities()) {
             TableFilter tableFilter = filtersConfig.tableFilter(entity.getCatalog(), entity.getSchema());
-            if (tableFilter != null && tableFilter.isIncludeTable(entity.getName()) != null) {
+            if (tableFilter != null && tableFilter.isIncludeTable(entity.getName())) {
                 existingFiltered.add(entity);
             }
         }

http://git-wip-us.apache.org/repos/asf/cayenne/blob/8c273022/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/DropRelationshipToDb.java
----------------------------------------------------------------------
diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/DropRelationshipToDb.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/DropRelationshipToDb.java
index 612c4a6..6f34c8e 100644
--- a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/DropRelationshipToDb.java
+++ b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/DropRelationshipToDb.java
@@ -23,7 +23,7 @@ import org.apache.cayenne.dba.QuotingStrategy;
 import org.apache.cayenne.dbsync.merge.factory.MergerTokenFactory;
 import org.apache.cayenne.map.DbEntity;
 import org.apache.cayenne.map.DbRelationship;
-import org.apache.cayenne.dbsync.reverse.db.DbRelationshipDetected;
+import org.apache.cayenne.dbsync.reverse.dbload.DbRelationshipDetected;
 
 import java.util.Collections;
 import java.util.List;

http://git-wip-us.apache.org/repos/asf/cayenne/blob/8c273022/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/db/DbAttributesBaseLoader.java
----------------------------------------------------------------------
diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/db/DbAttributesBaseLoader.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/db/DbAttributesBaseLoader.java
deleted file mode 100644
index 591d8b5..0000000
--- a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/db/DbAttributesBaseLoader.java
+++ /dev/null
@@ -1,107 +0,0 @@
-/*****************************************************************
- *   Licensed to the Apache Software Foundation (ASF) under one
- *  or more contributor license agreements.  See the NOTICE file
- *  distributed with this work for additional information
- *  regarding copyright ownership.  The ASF licenses this file
- *  to you under the Apache License, Version 2.0 (the
- *  "License"); you may not use this file except in compliance
- *  with the License.  You may obtain a copy of the License at
- *
- *    http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing,
- *  software distributed under the License is distributed on an
- *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- *  KIND, either express or implied.  See the License for the
- *  specific language governing permissions and limitations
- *  under the License.
- ****************************************************************/
-package org.apache.cayenne.dbsync.reverse.db;
-
-import org.apache.cayenne.dba.DbAdapter;
-import org.apache.cayenne.dba.TypesMapping;
-import org.apache.cayenne.map.DbAttribute;
-import org.apache.cayenne.map.DbEntity;
-
-import java.sql.DatabaseMetaData;
-import java.sql.ResultSet;
-import java.sql.SQLException;
-import java.util.List;
-import java.util.Set;
-
-/**
-* @since 4.0.
-*/
-public abstract class DbAttributesBaseLoader implements DbAttributesLoader {
-    private final String catalog;
-    private final String schema;
-
-    private final DatabaseMetaData metaData;
-    private final DbAdapter adapter;
-
-    public DbAttributesBaseLoader(String catalog, String schema, DatabaseMetaData metaData, DbAdapter adapter) {
-        this.catalog = catalog;
-        this.schema = schema;
-        this.metaData = metaData;
-        this.adapter = adapter;
-    }
-
-    protected DbAttribute loadDbAttribute(Set<String> columns, ResultSet rs) throws SQLException {
-
-        // gets attribute's (column's) information
-        int columnType = rs.getInt("DATA_TYPE");
-
-        // ignore precision of non-decimal columns
-        int decimalDigits = -1;
-        if (TypesMapping.isDecimal(columnType)) {
-            decimalDigits = rs.getInt("DECIMAL_DIGITS");
-            if (rs.wasNull()) {
-                decimalDigits = -1;
-            }
-        }
-
-        // create attribute delegating this task to adapter
-        DbAttribute attr = adapter.buildAttribute(
-                rs.getString("COLUMN_NAME"),
-                rs.getString("TYPE_NAME"),
-                columnType,
-                rs.getInt("COLUMN_SIZE"),
-                decimalDigits,
-                rs.getBoolean("NULLABLE"));
-
-        if (columns.contains("IS_AUTOINCREMENT")) {
-            String autoIncrement = rs.getString("IS_AUTOINCREMENT");
-            if ("YES".equals(autoIncrement)) {
-                attr.setGenerated(true);
-            }
-        }
-        return attr;
-    }
-
-    @Override
-    public void loadDbAttributes(DbEntity entity) {
-        for (DbAttribute attr : loadDbAttributes(entity.getName())) {
-            attr.setEntity(entity);
-
-            // override existing attributes if it comes again
-            if (entity.getAttribute(attr.getName()) != null) {
-                entity.removeAttribute(attr.getName());
-            }
-            entity.addAttribute(attr);
-        }
-    }
-
-    protected abstract List<DbAttribute> loadDbAttributes(String tableName);
-
-    protected String getCatalog() {
-        return catalog;
-    }
-
-    protected String getSchema() {
-        return schema;
-    }
-
-    protected DatabaseMetaData getMetaData() {
-        return metaData;
-    }
-}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/8c273022/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/db/DbAttributesLoader.java
----------------------------------------------------------------------
diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/db/DbAttributesLoader.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/db/DbAttributesLoader.java
deleted file mode 100644
index fb342c9..0000000
--- a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/db/DbAttributesLoader.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*****************************************************************
- *   Licensed to the Apache Software Foundation (ASF) under one
- *  or more contributor license agreements.  See the NOTICE file
- *  distributed with this work for additional information
- *  regarding copyright ownership.  The ASF licenses this file
- *  to you under the Apache License, Version 2.0 (the
- *  "License"); you may not use this file except in compliance
- *  with the License.  You may obtain a copy of the License at
- *
- *    http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing,
- *  software distributed under the License is distributed on an
- *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- *  KIND, either express or implied.  See the License for the
- *  specific language governing permissions and limitations
- *  under the License.
- ****************************************************************/
-package org.apache.cayenne.dbsync.reverse.db;
-
-import org.apache.cayenne.map.DbEntity;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-
-/**
- * Interface responsible for attributes loading. Several options possible here
- *  1) load attributes for each table separately
- *  2) load attributes for schema and group it by table names
- *
- *  here is a trade of between count of queries and amount af calculation.
- *
- *
- * @since 4.0
- */
-public interface DbAttributesLoader {
-
-    // TODO use instant field for logging
-    Log LOGGER = LogFactory.getLog(DbTableLoader.class);
-
-    void loadDbAttributes(DbEntity entity);
-
-}
-

http://git-wip-us.apache.org/repos/asf/cayenne/blob/8c273022/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/db/DbAttributesPerSchemaLoader.java
----------------------------------------------------------------------
diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/db/DbAttributesPerSchemaLoader.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/db/DbAttributesPerSchemaLoader.java
deleted file mode 100644
index 1ac0b07..0000000
--- a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/db/DbAttributesPerSchemaLoader.java
+++ /dev/null
@@ -1,131 +0,0 @@
-/*****************************************************************
- *   Licensed to the Apache Software Foundation (ASF) under one
- *  or more contributor license agreements.  See the NOTICE file
- *  distributed with this work for additional information
- *  regarding copyright ownership.  The ASF licenses this file
- *  to you under the Apache License, Version 2.0 (the
- *  "License"); you may not use this file except in compliance
- *  with the License.  You may obtain a copy of the License at
- *
- *    http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing,
- *  software distributed under the License is distributed on an
- *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- *  KIND, either express or implied.  See the License for the
- *  specific language governing permissions and limitations
- *  under the License.
- ****************************************************************/
-package org.apache.cayenne.dbsync.reverse.db;
-
-import org.apache.cayenne.dba.DbAdapter;
-import org.apache.cayenne.dbsync.reverse.filters.PatternFilter;
-import org.apache.cayenne.dbsync.reverse.filters.TableFilter;
-import org.apache.cayenne.map.DbAttribute;
-
-import java.sql.DatabaseMetaData;
-import java.sql.ResultSet;
-import java.sql.ResultSetMetaData;
-import java.sql.SQLException;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * Load all attributes for schema and return it for each table
- * */
-public class DbAttributesPerSchemaLoader extends DbAttributesBaseLoader {
-
-	private final TableFilter filter;
-
-	private Map<String, List<DbAttribute>> attributes;
-
-	public DbAttributesPerSchemaLoader(String catalog, String schema, DatabaseMetaData metaData, DbAdapter adapter,
-			TableFilter filter) {
-		super(catalog, schema, metaData, adapter);
-
-		this.filter = filter;
-	}
-
-	private Map<String, List<DbAttribute>> loadDbAttributes() throws SQLException {
-		Map<String, List<DbAttribute>> attributes = new HashMap<>();
-
-		try (ResultSet rs = getMetaData().getColumns(getCatalog(), getSchema(), "%", "%");) {
-			Set<String> columns = new HashSet<String>();
-
-			while (rs.next()) {
-				if (columns.isEmpty()) {
-					ResultSetMetaData rsMetaData = rs.getMetaData();
-					for (int i = 1; i <= rsMetaData.getColumnCount(); i++) {
-						columns.add(rsMetaData.getColumnLabel(i));
-					}
-				}
-
-				// for a reason not quiet apparent to me, Oracle sometimes
-				// returns duplicate record sets for the same table, messing up
-				// table
-				// names. E.g. for the system table "WK$_ATTR_MAPPING" columns
-				// are
-				// returned twice - as "WK$_ATTR_MAPPING" and
-				// "WK$$_ATTR_MAPPING"... Go figure
-				String tableName = rs.getString("TABLE_NAME");
-				String columnName = rs.getString("COLUMN_NAME");
-
-				// TODO: instead of elaborate filtering, can we just limit this to the tables that we already have?
-				PatternFilter columnFilter = filter.isIncludeTable(tableName);
-				/*
-				 * Here is possible optimization if filter will contain
-				 * map<tableName, columnFilter> we can replace it after tables
-				 * loading since already done pattern matching once and exactly
-				 * know all tables that we want to process
-				 */
-				if (columnFilter == null || !columnFilter.isIncluded(columnName)) {
-					if (LOGGER.isDebugEnabled()) {
-						LOGGER.debug("Skip column '" + tableName + "." + columnName + "' (Path: " + getCatalog() + "/"
-								+ getSchema() + "; Filter: " + columnFilter + ")");
-					}
-					continue;
-				}
-
-				List<DbAttribute> attrs = attributes.get(tableName);
-				if (attrs == null) {
-					attrs = new LinkedList<DbAttribute>();
-
-					attributes.put(tableName, attrs);
-				}
-
-				attrs.add(loadDbAttribute(columns, rs));
-			}
-		}
-
-		return attributes;
-	}
-
-	@Override
-	protected List<DbAttribute> loadDbAttributes(String tableName) {
-		Map<String, List<DbAttribute>> attributes = getAttributes();
-		if (attributes != null) {
-			List<DbAttribute> dbAttributes = attributes.get(tableName);
-			if (dbAttributes != null) {
-				return dbAttributes;
-			}
-		}
-
-		return new LinkedList<DbAttribute>();
-	}
-
-	public Map<String, List<DbAttribute>> getAttributes() {
-		if (attributes == null) {
-			try {
-				attributes = loadDbAttributes();
-			} catch (SQLException e) {
-				LOGGER.error(e);
-				attributes = new HashMap<>();
-			}
-		}
-		return attributes;
-	}
-}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/8c273022/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/db/DbLoader.java
----------------------------------------------------------------------
diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/db/DbLoader.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/db/DbLoader.java
deleted file mode 100644
index b171b5f..0000000
--- a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/db/DbLoader.java
+++ /dev/null
@@ -1,534 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- *    or more contributor license agreements.  See the NOTICE file
- *    distributed with this work for additional information
- *    regarding copyright ownership.  The ASF licenses this file
- *    to you under the Apache License, Version 2.0 (the
- *    "License"); you may not use this file except in compliance
- *    with the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing,
- *    software distributed under the License is distributed on an
- *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- *    KIND, either express or implied.  See the License for the
- *    specific language governing permissions and limitations
- *    under the License.
- */
-package org.apache.cayenne.dbsync.reverse.db;
-
-import org.apache.cayenne.dba.DbAdapter;
-import org.apache.cayenne.dba.TypesMapping;
-import org.apache.cayenne.dbsync.naming.NameBuilder;
-import org.apache.cayenne.dbsync.naming.ObjectNameGenerator;
-import org.apache.cayenne.dbsync.reverse.filters.CatalogFilter;
-import org.apache.cayenne.dbsync.reverse.filters.FiltersConfig;
-import org.apache.cayenne.dbsync.reverse.filters.SchemaFilter;
-import org.apache.cayenne.dbsync.reverse.filters.TableFilter;
-import org.apache.cayenne.map.DataMap;
-import org.apache.cayenne.map.DbAttribute;
-import org.apache.cayenne.map.DbEntity;
-import org.apache.cayenne.map.DbJoin;
-import org.apache.cayenne.map.DbRelationship;
-import org.apache.cayenne.map.Procedure;
-import org.apache.cayenne.map.ProcedureParameter;
-import org.apache.cayenne.util.EqualsBuilder;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-
-import java.sql.Connection;
-import java.sql.DatabaseMetaData;
-import java.sql.ResultSet;
-import java.sql.SQLException;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Set;
-import java.util.TreeSet;
-
-/**
- * Performs reverse engineering of the database, loading DB metadata in provided DataMap.
- *
- * @since 4.0
- */
-public class DbLoader {
-
-    private static final Log LOGGER = LogFactory.getLog(DbLoader.class);
-
-    private static final String WILDCARD = "%";
-
-    private final Connection connection;
-    private final DbAdapter adapter;
-    private final DbLoaderDelegate delegate;
-    private final ObjectNameGenerator nameGenerator;
-    private DatabaseMetaData metaData;
-
-    public DbLoader(Connection connection, DbAdapter adapter, DbLoaderDelegate delegate, ObjectNameGenerator nameGenerator) {
-        this.adapter = Objects.requireNonNull(adapter);
-        this.connection = Objects.requireNonNull(connection);
-        this.nameGenerator = Objects.requireNonNull(nameGenerator);
-        this.delegate = delegate == null ? new DefaultDbLoaderDelegate() : delegate;
-    }
-
-    private static List<String> getStrings(ResultSet rs) throws SQLException {
-        List<String> strings = new ArrayList<>();
-
-        while (rs.next()) {
-            strings.add(rs.getString(1));
-        }
-
-        return strings;
-    }
-
-    private static int getDirection(short type) {
-        switch (type) {
-            case DatabaseMetaData.procedureColumnIn:
-                return ProcedureParameter.IN_PARAMETER;
-            case DatabaseMetaData.procedureColumnInOut:
-                return ProcedureParameter.IN_OUT_PARAMETER;
-            case DatabaseMetaData.procedureColumnOut:
-                return ProcedureParameter.OUT_PARAMETER;
-            default:
-                return -1;
-        }
-    }
-
-    /**
-     * Retrieves catalogs for a given connection.
-     *
-     * @return List with the catalog names; empty list if none found.
-     */
-    // using a static method for catalog loading as we don't need a full DbLoader for this operation
-    public static List<String> loadCatalogs(Connection connection) throws SQLException {
-        try (ResultSet rs = connection.getMetaData().getCatalogs()) {
-            return getStrings(rs);
-        }
-    }
-
-    /**
-     * Retrieves the schemas for the given connection.
-     *
-     * @return List with the schema names; empty list if none found.
-     */
-    // using a static method for catalog loading as we don't need a full DbLoader for this operation
-    public static List<String> loadSchemas(Connection connection) throws SQLException {
-
-        try (ResultSet rs = connection.getMetaData().getSchemas()) {
-            return getStrings(rs);
-        }
-    }
-
-    /**
-     * Returns DatabaseMetaData object associated with this DbLoader.
-     */
-    private DatabaseMetaData getMetaData() throws SQLException {
-        if (metaData == null) {
-            metaData = connection.getMetaData();
-        }
-        return metaData;
-    }
-
-    protected void loadDbRelationships(DbLoaderConfiguration config, String catalog, String schema,
-                                       List<DbEntity> tables) throws SQLException {
-        if (config.isSkipRelationshipsLoading()) {
-            return;
-        }
-
-        // Get all the foreign keys referencing this table
-        Map<String, DbEntity> tablesMap = new HashMap<>();
-        for (DbEntity table : tables) {
-            tablesMap.put(table.getName(), table);
-        }
-
-        Map<String, Set<ExportedKey>> keys = loadExportedKeys(config, catalog, schema, tablesMap);
-        for (Map.Entry<String, Set<ExportedKey>> entry : keys.entrySet()) {
-            if (LOGGER.isDebugEnabled()) {
-                LOGGER.debug("Process keys for: " + entry.getKey());
-            }
-
-            Set<ExportedKey> exportedKeys = entry.getValue();
-            ExportedKey key = exportedKeys.iterator().next();
-            if (key == null) {
-                throw new IllegalStateException();
-            }
-
-            DbEntity pkEntity = tablesMap.get(key.getPKTableName());
-            if (pkEntity == null) {
-                skipRelationLog(key, key.getPKTableName());
-                continue;
-            }
-
-            DbEntity fkEntity = tablesMap.get(key.getFKTableName());
-            if (fkEntity == null) {
-                skipRelationLog(key, key.getFKTableName());
-                continue;
-            }
-
-            if (!new EqualsBuilder().append(pkEntity.getCatalog(), key.getPkCatalog())
-                    .append(pkEntity.getSchema(), key.getPkSchema()).append(fkEntity.getCatalog(), key.getFkCatalog())
-                    .append(fkEntity.getSchema(), key.getFkSchema()).isEquals()) {
-
-                LOGGER.info("Skip relation: '" + key + "' because it related to objects from other catalog/schema");
-                LOGGER.info("     relation primary key: '" + key.getPkCatalog() + "." + key.getPkSchema() + "'");
-                LOGGER.info("       primary key entity: '" + pkEntity.getCatalog() + "." + pkEntity.getSchema() + "'");
-                LOGGER.info("     relation foreign key: '" + key.getFkCatalog() + "." + key.getFkSchema() + "'");
-                LOGGER.info("       foreign key entity: '" + fkEntity.getCatalog() + "." + fkEntity.getSchema() + "'");
-                continue;
-            }
-
-            // forwardRelationship is a reference from table with primary key
-            DbRelationship forwardRelationship = new DbRelationship();
-
-            forwardRelationship.setSourceEntity(pkEntity);
-            forwardRelationship.setTargetEntityName(fkEntity);
-
-            // forwardRelationship is a reference from table with foreign key,
-            // it is what exactly we load from db
-
-            // TODO: dirty and non-transparent... using DbRelationshipDetected for the benefit of the merge package.
-            // This info is available from joins....
-            DbRelationshipDetected reverseRelationship = new DbRelationshipDetected();
-
-
-            reverseRelationship.setFkName(key.getFKName());
-            reverseRelationship.setSourceEntity(fkEntity);
-            reverseRelationship.setTargetEntityName(pkEntity);
-            reverseRelationship.setToMany(false);
-
-            createAndAppendJoins(exportedKeys, pkEntity, fkEntity, forwardRelationship, reverseRelationship);
-
-            boolean toDependentPK = isToDependentPK(forwardRelationship);
-            forwardRelationship.setToDependentPK(toDependentPK);
-
-            boolean isOneToOne = toDependentPK
-                    && fkEntity.getPrimaryKeys().size() == forwardRelationship.getJoins().size();
-
-            forwardRelationship.setToMany(!isOneToOne);
-
-            // set relationship names only after their joins are ready ... generator logic is based on relationship
-            // state...
-            forwardRelationship.setName(NameBuilder
-                    .builder(forwardRelationship, pkEntity)
-                    .baseName(nameGenerator.relationshipName(forwardRelationship))
-                    .name());
-
-            reverseRelationship.setName(NameBuilder
-                    .builder(reverseRelationship, fkEntity)
-                    .baseName(nameGenerator.relationshipName(reverseRelationship))
-                    .name());
-
-            if (delegate.dbRelationshipLoaded(fkEntity, reverseRelationship)) {
-                fkEntity.addRelationship(reverseRelationship);
-            }
-            if (delegate.dbRelationshipLoaded(pkEntity, forwardRelationship)) {
-                pkEntity.addRelationship(forwardRelationship);
-            }
-        }
-    }
-
-    private boolean isToDependentPK(DbRelationship forwardRelationship) {
-        for (DbJoin dbJoin : forwardRelationship.getJoins()) {
-            if (!dbJoin.getTarget().isPrimaryKey()) {
-                return false;
-            }
-        }
-
-        return true;
-    }
-
-    private void createAndAppendJoins(Set<ExportedKey> exportedKeys, DbEntity pkEntity, DbEntity fkEntity,
-                                      DbRelationship forwardRelationship, DbRelationship reverseRelationship) {
-        for (ExportedKey exportedKey : exportedKeys) {
-            // Create and append joins
-            String pkName = exportedKey.getPKColumnName();
-            String fkName = exportedKey.getFKColumnName();
-
-            // skip invalid joins...
-            DbAttribute pkAtt = pkEntity.getAttribute(pkName);
-            if (pkAtt == null) {
-                LOGGER.info("no attribute for declared primary key: " + pkName);
-                continue;
-            }
-
-            DbAttribute fkAtt = fkEntity.getAttribute(fkName);
-            if (fkAtt == null) {
-                LOGGER.info("no attribute for declared foreign key: " + fkName);
-                continue;
-            }
-
-            forwardRelationship.addJoin(new DbJoin(forwardRelationship, pkName, fkName));
-            reverseRelationship.addJoin(new DbJoin(reverseRelationship, fkName, pkName));
-        }
-    }
-
-    private Map<String, Set<ExportedKey>> loadExportedKeys(DbLoaderConfiguration config, String catalog, String schema,
-                                                           Map<String, DbEntity> tables) throws SQLException {
-        Map<String, Set<ExportedKey>> keys = new HashMap<>();
-
-        for (DbEntity dbEntity : tables.values()) {
-            if (!delegate.dbRelationship(dbEntity)) {
-                continue;
-            }
-
-            ResultSet rs;
-            try {
-                rs = getMetaData().getExportedKeys(catalog, schema, dbEntity.getName());
-            } catch (SQLException cay182Ex) {
-                // Sybase-specific - the line above blows on VIEWS, see CAY-182.
-                LOGGER.info("Error getting relationships for '" + catalog + "." + schema + "', ignoring. "
-                        + cay182Ex.getMessage(), cay182Ex);
-                return new HashMap<>();
-            }
-
-            try {
-                while (rs.next()) {
-                    ExportedKey key = ExportedKey.extractData(rs);
-
-                    DbEntity fkEntity = tables.get(key.getFKTableName());
-                    if (fkEntity == null) {
-                        skipRelationLog(key, key.getFKTableName());
-                        continue;
-                    }
-
-                    if (config.getFiltersConfig().tableFilter(fkEntity.getCatalog(), fkEntity.getSchema())
-                            .isIncludeTable(fkEntity.getName()) == null) {
-                        continue;
-                    }
-
-                    Set<ExportedKey> exportedKeys = keys.get(key.getStrKey());
-                    if (exportedKeys == null) {
-                        exportedKeys = new TreeSet<ExportedKey>();
-
-                        keys.put(key.getStrKey(), exportedKeys);
-                    }
-                    exportedKeys.add(key);
-                }
-
-            } finally {
-                rs.close();
-            }
-        }
-        return keys;
-    }
-
-    private void skipRelationLog(ExportedKey key, String tableName) {
-        LOGGER.info("Skip relation: '" + key + "' because table '" + tableName + "' not found");
-    }
-
-    protected String[] getTableTypes(DbLoaderConfiguration config) {
-
-        String[] configTypes = config.getTableTypes();
-        if (configTypes != null && configTypes.length > 0) {
-            return configTypes;
-        }
-
-        List<String> list = new ArrayList<>(2);
-
-        String viewType = adapter.tableTypeForView();
-        if (viewType != null) {
-            list.add(viewType);
-        }
-
-        String tableType = adapter.tableTypeForTable();
-        if (tableType != null) {
-            list.add(tableType);
-        }
-
-        return list.toArray(new String[list.size()]);
-    }
-
-    /**
-     * Performs database reverse engineering based on provided configuration. Stores the resulting {@link DbEntity}
-     * and {@link Procedure} objects in provided DataMap.
-     *
-     * @since 4.0
-     */
-    public void load(DataMap dataMap, DbLoaderConfiguration config) throws SQLException {
-        loadDbEntities(dataMap, config);
-        loadProcedures(dataMap, config);
-    }
-
-    protected void loadDbEntities(DataMap dataMap, DbLoaderConfiguration config) throws SQLException {
-
-        String[] types = getTableTypes(config);
-
-        for (CatalogFilter catalog : config.getFiltersConfig().getCatalogs()) {
-            for (SchemaFilter schema : catalog.schemas) {
-                List<DbEntity> entities = createTableLoader(catalog.name, schema.name, schema.tables).loadDbEntities(
-                        dataMap, config, types);
-                loadDbRelationships(config, catalog.name, schema.name, entities);
-            }
-        }
-    }
-
-    protected DbTableLoader createTableLoader(String catalog, String schema, TableFilter filter) throws SQLException {
-        return new DbTableLoader(catalog,
-                schema,
-                getMetaData(),
-                delegate,
-                new DbAttributesPerSchemaLoader(catalog, schema, getMetaData(), adapter, filter));
-    }
-
-
-    /**
-     * Loads database stored procedures into the DataMap.
-     * <p>
-     * <i>As of 1.1 there is no boolean property or delegate method to make
-     * procedure loading optional or to implement custom merging logic, so
-     * currently this method is NOT CALLED from "loadDataMapFromDB" and should
-     * be invoked explicitly by the user. </i>
-     * </p>
-     *
-     * @since 4.0
-     */
-    protected Map<String, Procedure> loadProcedures(DataMap dataMap, DbLoaderConfiguration config) throws SQLException {
-
-        Map<String, Procedure> procedures = loadProcedures(config);
-        if (procedures.isEmpty()) {
-            return procedures;
-        }
-
-        loadProceduresColumns(config, procedures);
-
-        for (Procedure procedure : procedures.values()) {
-            dataMap.addProcedure(procedure);
-        }
-
-        return procedures;
-    }
-
-    private void loadProceduresColumns(DbLoaderConfiguration config, Map<String, Procedure> procedures)
-            throws SQLException {
-
-        for (CatalogFilter catalog : config.getFiltersConfig().getCatalogs()) {
-            for (SchemaFilter schema : catalog.schemas) {
-                loadProceduresColumns(procedures, catalog.name, schema.name);
-            }
-        }
-    }
-
-    private void loadProceduresColumns(Map<String, Procedure> procedures, String catalog, String schema)
-            throws SQLException {
-
-        try (ResultSet columnsRS = getMetaData().getProcedureColumns(catalog, schema, null, null);) {
-            while (columnsRS.next()) {
-                String procSchema = columnsRS.getString("PROCEDURE_SCHEM");
-                String procCatalog = columnsRS.getString("PROCEDURE_CAT");
-                String name = columnsRS.getString("PROCEDURE_NAME");
-                String key = Procedure.generateFullyQualifiedName(procCatalog, procSchema, name);
-                Procedure procedure = procedures.get(key);
-                if (procedure == null) {
-                    continue;
-                }
-
-                ProcedureParameter column = loadProcedureParams(columnsRS, key, procedure);
-                if (column == null) {
-                    continue;
-                }
-                procedure.addCallParameter(column);
-            }
-        }
-    }
-
-    private ProcedureParameter loadProcedureParams(ResultSet columnsRS, String key, Procedure procedure)
-            throws SQLException {
-        String columnName = columnsRS.getString("COLUMN_NAME");
-
-        // skip ResultSet columns, as they are not described in Cayenne
-        // procedures yet...
-        short type = columnsRS.getShort("COLUMN_TYPE");
-        if (type == DatabaseMetaData.procedureColumnResult) {
-            LOGGER.debug("skipping ResultSet column: " + key + "." + columnName);
-        }
-
-        if (columnName == null) {
-            if (type == DatabaseMetaData.procedureColumnReturn) {
-                LOGGER.debug("null column name, assuming result column: " + key);
-                columnName = "_return_value";
-                procedure.setReturningValue(true);
-            } else {
-                LOGGER.info("invalid null column name, skipping column : " + key);
-                return null;
-            }
-        }
-
-        int columnType = columnsRS.getInt("DATA_TYPE");
-
-        // ignore precision of non-decimal columns
-        int decimalDigits = -1;
-        if (TypesMapping.isDecimal(columnType)) {
-            decimalDigits = columnsRS.getShort("SCALE");
-            if (columnsRS.wasNull()) {
-                decimalDigits = -1;
-            }
-        }
-
-        ProcedureParameter column = new ProcedureParameter(columnName);
-        int direction = getDirection(type);
-        if (direction != -1) {
-            column.setDirection(direction);
-        }
-
-        column.setType(columnType);
-        column.setMaxLength(columnsRS.getInt("LENGTH"));
-        column.setPrecision(decimalDigits);
-
-        column.setProcedure(procedure);
-        return column;
-    }
-
-    private Map<String, Procedure> loadProcedures(DbLoaderConfiguration config) throws SQLException {
-        Map<String, Procedure> procedures = new HashMap<>();
-
-        FiltersConfig filters = config.getFiltersConfig();
-        for (CatalogFilter catalog : filters.getCatalogs()) {
-            for (SchemaFilter schema : catalog.schemas) {
-                if (filters.proceduresFilter(catalog.name, schema.name).isEmpty()) {
-                    continue;
-                }
-
-                procedures.putAll(loadProcedures(filters, catalog.name, schema.name));
-            }
-        }
-
-        return procedures;
-    }
-
-    private Map<String, Procedure> loadProcedures(FiltersConfig filters, String catalog, String schema)
-            throws SQLException {
-        Map<String, Procedure> procedures = new HashMap<>();
-        // get procedures
-
-        try (ResultSet rs = getMetaData().getProcedures(catalog, schema, WILDCARD);) {
-            while (rs.next()) {
-
-                String name = rs.getString("PROCEDURE_NAME");
-                Procedure procedure = new Procedure(name);
-                procedure.setCatalog(rs.getString("PROCEDURE_CAT"));
-                procedure.setSchema(rs.getString("PROCEDURE_SCHEM"));
-
-                if (!filters.proceduresFilter(procedure.getCatalog(), procedure.getSchema()).isIncluded(
-                        procedure.getName())) {
-                    LOGGER.info("skipping Cayenne PK procedure: " + name);
-                    continue;
-                }
-
-                switch (rs.getShort("PROCEDURE_TYPE")) {
-                    case DatabaseMetaData.procedureNoResult:
-                    case DatabaseMetaData.procedureResultUnknown:
-                        procedure.setReturningValue(false);
-                        break;
-                    case DatabaseMetaData.procedureReturnsResult:
-                        procedure.setReturningValue(true);
-                        break;
-                }
-
-                procedures.put(procedure.getFullyQualifiedName(), procedure);
-            }
-        }
-        return procedures;
-    }
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cayenne/blob/8c273022/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/db/DbLoaderConfiguration.java
----------------------------------------------------------------------
diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/db/DbLoaderConfiguration.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/db/DbLoaderConfiguration.java
deleted file mode 100644
index e7c5e81..0000000
--- a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/db/DbLoaderConfiguration.java
+++ /dev/null
@@ -1,84 +0,0 @@
-/*****************************************************************
- *   Licensed to the Apache Software Foundation (ASF) under one
- *  or more contributor license agreements.  See the NOTICE file
- *  distributed with this work for additional information
- *  regarding copyright ownership.  The ASF licenses this file
- *  to you under the Apache License, Version 2.0 (the
- *  "License"); you may not use this file except in compliance
- *  with the License.  You may obtain a copy of the License at
- *
- *    http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing,
- *  software distributed under the License is distributed on an
- *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- *  KIND, either express or implied.  See the License for the
- *  specific language governing permissions and limitations
- *  under the License.
- ****************************************************************/
-package org.apache.cayenne.dbsync.reverse.db;
-
-import org.apache.cayenne.dbsync.reverse.filters.TableFilter;
-import org.apache.cayenne.dbsync.reverse.filters.FiltersConfig;
-import org.apache.cayenne.dbsync.reverse.filters.PatternFilter;
-
-/**
- * @since 4.0
- */
-public class DbLoaderConfiguration {
-
-    private Boolean skipRelationshipsLoading;
-    private Boolean skipPrimaryKeyLoading;
-    private String[] tableTypes;
-    private FiltersConfig filtersConfig;
-
-    public String[] getTableTypes() {
-        return tableTypes;
-    }
-
-    public void setTableTypes(String[] tableTypes) {
-        this.tableTypes = tableTypes;
-    }
-
-    public FiltersConfig getFiltersConfig() {
-        if (filtersConfig == null) {
-            // this case is used often in tests where config not initialized properly
-            return FiltersConfig.create(null, null, TableFilter.everything(), PatternFilter.INCLUDE_NOTHING);
-        }
-        return filtersConfig;
-    }
-
-    public void setFiltersConfig(FiltersConfig filtersConfig) {
-        this.filtersConfig = filtersConfig;
-    }
-
-    public boolean isSkipRelationshipsLoading() {
-        return skipRelationshipsLoading != null && skipRelationshipsLoading;
-    }
-
-    public void setSkipRelationshipsLoading(Boolean skipRelationshipsLoading) {
-        this.skipRelationshipsLoading = skipRelationshipsLoading;
-    }
-
-    public boolean isSkipPrimaryKeyLoading() {
-        return skipPrimaryKeyLoading != null && skipPrimaryKeyLoading;
-    }
-
-    public void setSkipPrimaryKeyLoading(Boolean skipPrimaryKeyLoading) {
-        this.skipPrimaryKeyLoading = skipPrimaryKeyLoading;
-    }
-
-    @Override
-    public String toString() {
-        String res = "EntitiesFilters: " + getFiltersConfig();
-        if (isSkipRelationshipsLoading()) {
-            res += "\n Skip Loading Relationships! \n";
-        }
-
-        if (isSkipPrimaryKeyLoading()) {
-            res += "\n Skip Loading PrimaryKeys! \n";
-        }
-
-        return res;
-    }
-}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/8c273022/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/db/DbLoaderDelegate.java
----------------------------------------------------------------------
diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/db/DbLoaderDelegate.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/db/DbLoaderDelegate.java
deleted file mode 100644
index 94705c6..0000000
--- a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/db/DbLoaderDelegate.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*****************************************************************
- *   Licensed to the Apache Software Foundation (ASF) under one
- *  or more contributor license agreements.  See the NOTICE file
- *  distributed with this work for additional information
- *  regarding copyright ownership.  The ASF licenses this file
- *  to you under the Apache License, Version 2.0 (the
- *  "License"); you may not use this file except in compliance
- *  with the License.  You may obtain a copy of the License at
- *
- *    http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing,
- *  software distributed under the License is distributed on an
- *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- *  KIND, either express or implied.  See the License for the
- *  specific language governing permissions and limitations
- *  under the License.
- ****************************************************************/
-
-package org.apache.cayenne.dbsync.reverse.db;
-
-import org.apache.cayenne.map.DbEntity;
-import org.apache.cayenne.map.DbRelationship;
-import org.apache.cayenne.map.ObjEntity;
-
-/**
- * Defines API for progress tracking and altering the folow of reverse-engineering.
- */
-public interface DbLoaderDelegate {
-
-    void dbEntityAdded(DbEntity entity);
-
-    void dbEntityRemoved(DbEntity entity);
-
-    /**
-     * Called before relationship loading for a {@link DbEntity}.
-     *
-     * @param entity DbEntity for which {@link DbRelationship} is about to be loaded.
-     * @return true in case you want process relationships for this entity, false otherwise.
-     */
-    boolean dbRelationship(DbEntity entity);
-
-    /**
-     * Called before relationship will be added into db-entity but after it was loaded from db
-     *
-     * @param entity
-     * @return true in case you want add this relationship into entity
-     * false otherwise
-     */
-    boolean dbRelationshipLoaded(DbEntity entity, DbRelationship relationship);
-
-    /**
-     * @deprecated since 4.0 no longer invoked as DbLoader does not deal with object layer anymore.
-     */
-    @Deprecated
-    void objEntityAdded(ObjEntity entity);
-
-    /**
-     * @deprecated since 4.0 no longer invoked as DbLoader does not deal with object layer anymore.
-     */
-    @Deprecated
-    void objEntityRemoved(ObjEntity entity);
-}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/8c273022/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/db/DbRelationshipDetected.java
----------------------------------------------------------------------
diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/db/DbRelationshipDetected.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/db/DbRelationshipDetected.java
deleted file mode 100644
index dfad14f..0000000
--- a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/db/DbRelationshipDetected.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*****************************************************************
- *   Licensed to the Apache Software Foundation (ASF) under one
- *  or more contributor license agreements.  See the NOTICE file
- *  distributed with this work for additional information
- *  regarding copyright ownership.  The ASF licenses this file
- *  to you under the Apache License, Version 2.0 (the
- *  "License"); you may not use this file except in compliance
- *  with the License.  You may obtain a copy of the License at
- *
- *    http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing,
- *  software distributed under the License is distributed on an
- *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- *  KIND, either express or implied.  See the License for the
- *  specific language governing permissions and limitations
- *  under the License.
- ****************************************************************/
-package org.apache.cayenne.dbsync.reverse.db;
-
-
-import org.apache.cayenne.map.DbRelationship;
-
-/**
- * A subclass of {@link DbRelationship} to hold some extra runtime information.
- */
-// TODO: dirty ... can we lookup fkName via joins?
-public class DbRelationshipDetected extends DbRelationship {
-
-    private String fkName;
-
-    public DbRelationshipDetected(String uniqueRelName) {
-        super(uniqueRelName);
-    }
-
-    public DbRelationshipDetected() {
-    }
-
-    /**
-     * Get the name of the underlying foreign key. Typically FK_NAME from jdbc metadata.
-     */
-    public String getFkName() {
-        return fkName;
-    }
-
-    /**
-     * Set the name of the underlying foreign key. Typically FK_NAME from jdbc metadata.
-     */
-    public void setFkName(String fkName) {
-        this.fkName = fkName;
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/8c273022/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/db/DbTableLoader.java
----------------------------------------------------------------------
diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/db/DbTableLoader.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/db/DbTableLoader.java
deleted file mode 100644
index f3476af..0000000
--- a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/db/DbTableLoader.java
+++ /dev/null
@@ -1,183 +0,0 @@
-/*****************************************************************
- *   Licensed to the Apache Software Foundation (ASF) under one
- *  or more contributor license agreements.  See the NOTICE file
- *  distributed with this work for additional information
- *  regarding copyright ownership.  The ASF licenses this file
- *  to you under the Apache License, Version 2.0 (the
- *  "License"); you may not use this file except in compliance
- *  with the License.  You may obtain a copy of the License at
- *
- *    http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing,
- *  software distributed under the License is distributed on an
- *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- *  KIND, either express or implied.  See the License for the
- *  specific language governing permissions and limitations
- *  under the License.
- ****************************************************************/
-package org.apache.cayenne.dbsync.reverse.db;
-
-import org.apache.cayenne.dbsync.reverse.filters.PatternFilter;
-import org.apache.cayenne.dbsync.reverse.filters.TableFilter;
-import org.apache.cayenne.map.DataMap;
-import org.apache.cayenne.map.DbAttribute;
-import org.apache.cayenne.map.DbEntity;
-import org.apache.cayenne.map.DetectedDbEntity;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-
-import java.sql.DatabaseMetaData;
-import java.sql.ResultSet;
-import java.sql.SQLException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.LinkedList;
-import java.util.List;
-
-/**
- * @since 4.0
- */
-public class DbTableLoader {
-
-	private static final Log LOGGER = LogFactory.getLog(DbTableLoader.class);
-
-	private static final String WILDCARD = "%";
-
-	private final String catalog;
-	private final String schema;
-
-	private final DatabaseMetaData metaData;
-	private final DbLoaderDelegate delegate;
-
-	private final DbAttributesLoader attributesLoader;
-
-	public DbTableLoader(String catalog, String schema, DatabaseMetaData metaData, DbLoaderDelegate delegate,
-			DbAttributesLoader attributesLoader) {
-		this.catalog = catalog;
-		this.schema = schema;
-		this.metaData = metaData;
-		this.delegate = delegate;
-
-		this.attributesLoader = attributesLoader;
-	}
-
-	/**
-	 * Returns all tables for given combination of the criteria. Tables returned
-	 * as DbEntities without any attributes or relationships.
-	 *
-	 * @param types
-	 *            The types of table names to retrieve, null returns all types.
-	 * @since 4.0
-	 */
-	public List<DetectedDbEntity> getDbEntities(TableFilter filters, String[] types) throws SQLException {
-		if (LOGGER.isDebugEnabled()) {
-			LOGGER.debug("Read tables: catalog=" + catalog + ", schema=" + schema + ", types=" + Arrays.toString(types));
-		}
-
-		List<DetectedDbEntity> tables = new LinkedList<DetectedDbEntity>();
-		try (ResultSet rs = metaData.getTables(catalog, schema, WILDCARD, types);) {
-			while (rs.next()) {
-				// Oracle 9i and newer has a nifty recycle bin feature... but we
-				// don't
-				// want dropped tables to be included here; in fact they may
-				// even result
-				// in errors on reverse engineering as their names have special
-				// chars like
-				// "/", etc. So skip them all together
-
-				String name = rs.getString("TABLE_NAME");
-				if (name == null) {
-					continue;
-				}
-
-				DetectedDbEntity table = new DetectedDbEntity(name);
-
-				String catalog = rs.getString("TABLE_CAT");
-				table.setCatalog(catalog);
-
-				String schema = rs.getString("TABLE_SCHEM");
-				table.setSchema(schema);
-				if (!(this.catalog == null || this.catalog.equals(catalog))
-						|| !(this.schema == null || this.schema.equals(schema))) {
-
-					LOGGER.error(catalog + "." + schema + "." + name + " wrongly loaded for catalog/schema : "
-							+ this.catalog + "." + this.schema);
-
-					continue;
-				}
-
-				PatternFilter includeTable = filters.isIncludeTable(table.getName());
-				if (includeTable != null) {
-					tables.add(table);
-				}
-			}
-		}
-		return tables;
-	}
-
-	/**
-	 * Loads dbEntities for the specified tables.
-	 * 
-	 * @param config
-	 * @param types
-	 */
-	public List<DbEntity> loadDbEntities(DataMap map, DbLoaderConfiguration config, String[] types) throws SQLException {
-		/** List of db entities to process. */
-
-		List<DetectedDbEntity> tables = getDbEntities(config.getFiltersConfig().tableFilter(catalog, schema), types);
-
-		List<DbEntity> dbEntities = new ArrayList<>();
-		for (DbEntity dbEntity : tables) {
-			DbEntity oldEnt = map.getDbEntity(dbEntity.getName());
-			if (oldEnt != null) {
-				LOGGER.debug("Overwrite DbEntity: " + oldEnt.getName());
-				map.removeDbEntity(oldEnt.getName(), true);
-				delegate.dbEntityRemoved(oldEnt);
-			}
-
-			map.addDbEntity(dbEntity);
-
-			delegate.dbEntityAdded(dbEntity);
-
-			// delegate might have thrown this entity out... so check if it is
-			// still
-			// around before continuing processing
-			if (map.getDbEntity(dbEntity.getName()) == dbEntity) {
-				dbEntities.add(dbEntity);
-				attributesLoader.loadDbAttributes(dbEntity);
-				if (!config.isSkipPrimaryKeyLoading()) {
-					loadPrimaryKey(dbEntity);
-				}
-			}
-		}
-
-		return dbEntities;
-	}
-
-	private void loadPrimaryKey(DbEntity dbEntity) throws SQLException {
-
-		try (ResultSet rs = metaData.getPrimaryKeys(dbEntity.getCatalog(), dbEntity.getSchema(), dbEntity.getName());) {
-			while (rs.next()) {
-				String columnName = rs.getString("COLUMN_NAME");
-				DbAttribute attribute = dbEntity.getAttribute(columnName);
-
-				if (attribute != null) {
-					attribute.setPrimaryKey(true);
-				} else {
-					// why an attribute might be null is not quiet clear
-					// but there is a bug report 731406 indicating that it is
-					// possible
-					// so just print the warning, and ignore
-					LOGGER.warn("Can't locate attribute for primary key: " + columnName);
-				}
-
-				String pkName = rs.getString("PK_NAME");
-				if (pkName != null && dbEntity instanceof DetectedDbEntity) {
-					((DetectedDbEntity) dbEntity).setPrimaryKeyName(pkName);
-				}
-
-			}
-		}
-	}
-}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/8c273022/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/db/DefaultDbLoaderDelegate.java
----------------------------------------------------------------------
diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/db/DefaultDbLoaderDelegate.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/db/DefaultDbLoaderDelegate.java
deleted file mode 100644
index fef024f..0000000
--- a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/db/DefaultDbLoaderDelegate.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*****************************************************************
- *   Licensed to the Apache Software Foundation (ASF) under one
- *  or more contributor license agreements.  See the NOTICE file
- *  distributed with this work for additional information
- *  regarding copyright ownership.  The ASF licenses this file
- *  to you under the Apache License, Version 2.0 (the
- *  "License"); you may not use this file except in compliance
- *  with the License.  You may obtain a copy of the License at
- *
- *    http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing,
- *  software distributed under the License is distributed on an
- *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- *  KIND, either express or implied.  See the License for the
- *  specific language governing permissions and limitations
- *  under the License.
- ****************************************************************/
-package org.apache.cayenne.dbsync.reverse.db;
-
-import org.apache.cayenne.map.DbEntity;
-import org.apache.cayenne.map.DbRelationship;
-import org.apache.cayenne.map.ObjEntity;
-
-/**
- * A noop {@link DbLoaderDelegate}.
- *
- * @since 4.0
- */
-public class DefaultDbLoaderDelegate implements DbLoaderDelegate {
-
-    @Override
-    public void dbEntityAdded(DbEntity entity) {
-
-    }
-
-    @Override
-    public void dbEntityRemoved(DbEntity entity) {
-
-    }
-
-    @Override
-    public boolean dbRelationship(DbEntity entity) {
-        return true;
-    }
-
-    @Override
-    public boolean dbRelationshipLoaded(DbEntity entity, DbRelationship relationship) {
-        return true;
-    }
-
-    @Deprecated
-    @Override
-    public void objEntityAdded(ObjEntity entity) {
-
-    }
-
-    @Deprecated
-    @Override
-    public void objEntityRemoved(ObjEntity entity) {
-
-    }
-}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/8c273022/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/db/ExportedKey.java
----------------------------------------------------------------------
diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/db/ExportedKey.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/db/ExportedKey.java
deleted file mode 100644
index 093cbf0..0000000
--- a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/db/ExportedKey.java
+++ /dev/null
@@ -1,233 +0,0 @@
-/*****************************************************************
- *   Licensed to the Apache Software Foundation (ASF) under one
- *  or more contributor license agreements.  See the NOTICE file
- *  distributed with this work for additional information
- *  regarding copyright ownership.  The ASF licenses this file
- *  to you under the Apache License, Version 2.0 (the
- *  "License"); you may not use this file except in compliance
- *  with the License.  You may obtain a copy of the License at
- *
- *    http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing,
- *  software distributed under the License is distributed on an
- *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- *  KIND, either express or implied.  See the License for the
- *  specific language governing permissions and limitations
- *  under the License.
- ****************************************************************/
-package org.apache.cayenne.dbsync.reverse.db;
-
-import org.apache.cayenne.util.EqualsBuilder;
-import org.apache.cayenne.util.HashCodeBuilder;
-import org.apache.commons.lang.builder.CompareToBuilder;
-
-import java.sql.ResultSet;
-import java.sql.SQLException;
-
-/**
- * A representation of relationship between two tables in database. It can be used for creating names
- * for relationships.
- *
- * @since 4.0
- */
-class ExportedKey implements Comparable {
-
-    private final String pkCatalog;
-    private final String pkSchema;
-    private final String pkTable;
-    private final String pkColumn;
-    private final String fkCatalog;
-    private final String fkSchema;
-    private final String fkTable;
-    private final String fkColumn;
-    private final String fkName;
-    private final String pkName;
-    private final short keySeq;
-
-    public ExportedKey(String pkTable, String pkColumn, String pkName,
-                       String fkTable, String fkColumn, String fkName, short keySeq) {
-        this(null, null, pkTable, pkColumn, pkName, null, null, fkTable, fkColumn, fkName, keySeq);
-    }
-
-    public ExportedKey(String pkCatalog, String pkSchema, String pkTable, String pkColumn, String pkName,
-                       String fkCatalog, String fkSchema, String fkTable, String fkColumn, String fkName, short keySeq) {
-        this.pkCatalog = pkCatalog;
-        this.pkSchema = pkSchema;
-        this.pkTable = pkTable;
-        this.pkColumn = pkColumn;
-        this.pkName = pkName;
-        this.fkCatalog = fkCatalog;
-        this.fkSchema = fkSchema;
-        this.fkTable = fkTable;
-        this.fkColumn = fkColumn;
-        this.fkName = fkName;
-        this.keySeq = keySeq;
-    }
-
-    /**
-     * Extracts data from a resultset pointing to a exported key to
-     * ExportedKey class instance
-     *
-     * @param rs ResultSet pointing to a exported key, fetched using
-     *           DataBaseMetaData.getExportedKeys(...)
-     */
-    public static ExportedKey extractData(ResultSet rs) throws SQLException {
-        return new ExportedKey(
-                rs.getString("PKTABLE_CAT"),
-                rs.getString("PKTABLE_SCHEM"),
-                rs.getString("PKTABLE_NAME"),
-                rs.getString("PKCOLUMN_NAME"),
-                rs.getString("PK_NAME"),
-                rs.getString("FKTABLE_CAT"),
-                rs.getString("FKTABLE_SCHEM"),
-                rs.getString("FKTABLE_NAME"),
-                rs.getString("FKCOLUMN_NAME"),
-                rs.getString("FK_NAME"),
-                rs.getShort("KEY_SEQ")
-        );
-    }
-
-
-    public String getPkCatalog() {
-        return pkCatalog;
-    }
-
-    public String getPkSchema() {
-        return pkSchema;
-    }
-
-    public String getFkCatalog() {
-        return fkCatalog;
-    }
-
-    public String getFkSchema() {
-        return fkSchema;
-    }
-
-    /**
-     * @return source table name
-     */
-    public String getPKTableName() {
-        return pkTable;
-    }
-
-    /**
-     * @return destination table name
-     */
-    public String getFKTableName() {
-        return fkTable;
-    }
-
-    /**
-     * @return source column name
-     */
-    public String getPKColumnName() {
-        return pkColumn;
-    }
-
-    /**
-     * @return destination column name
-     */
-    public String getFKColumnName() {
-        return fkColumn;
-    }
-
-    /**
-     * @return PK name
-     */
-    public String getPKName() {
-        return pkName;
-    }
-
-    /**
-     * @return FK name
-     */
-    public String getFKName() {
-        return fkName;
-    }
-
-    public short getKeySeq() {
-        return keySeq;
-    }
-
-
-    @Override
-    public boolean equals(Object obj) {
-        if (obj == null) {
-            return false;
-        }
-        if (obj == this) {
-            return true;
-        }
-        if (obj.getClass() != getClass()) {
-            return false;
-        }
-        ExportedKey rhs = (ExportedKey) obj;
-        return new EqualsBuilder()
-                .append(this.pkCatalog, rhs.pkCatalog)
-                .append(this.pkSchema, rhs.pkSchema)
-                .append(this.pkTable, rhs.pkTable)
-                .append(this.pkColumn, rhs.pkColumn)
-                .append(this.fkCatalog, rhs.fkCatalog)
-                .append(this.fkSchema, rhs.fkSchema)
-                .append(this.fkTable, rhs.fkTable)
-                .append(this.fkColumn, rhs.fkColumn)
-                .append(this.fkName, rhs.fkName)
-                .append(this.pkName, rhs.pkName)
-                .append(this.keySeq, rhs.keySeq)
-                .isEquals();
-    }
-
-    @Override
-    public int hashCode() {
-        return new HashCodeBuilder()
-                .append(pkCatalog)
-                .append(pkSchema)
-                .append(pkTable)
-                .append(pkColumn)
-                .append(fkCatalog)
-                .append(fkSchema)
-                .append(fkTable)
-                .append(fkColumn)
-                .append(fkName)
-                .append(pkName)
-                .append(keySeq)
-                .toHashCode();
-    }
-
-    @Override
-    public int compareTo(Object obj) {
-        if (obj == null || !obj.getClass().equals(getClass())) {
-            throw new IllegalArgumentException();
-        }
-        if (obj == this) {
-            return 0;
-        }
-
-        ExportedKey rhs = (ExportedKey) obj;
-        return new CompareToBuilder()
-                .append(pkCatalog, rhs.pkCatalog)
-                .append(pkSchema, rhs.pkSchema)
-                .append(pkTable, rhs.pkTable)
-                .append(pkName, rhs.pkName)
-                .append(fkCatalog, rhs.fkCatalog)
-                .append(fkSchema, rhs.fkSchema)
-                .append(fkTable, rhs.fkTable)
-                .append(fkName, rhs.fkName)
-                .append(keySeq, rhs.keySeq)
-                .append(pkColumn, rhs.pkColumn)
-                .append(fkColumn, rhs.fkColumn)
-                .toComparison();
-    }
-
-    @Override
-    public String toString() {
-        return getStrKey() + " # " + keySeq;
-    }
-
-    public String getStrKey() {
-        return pkCatalog + "." + pkSchema + "." + pkTable + "." + pkColumn
-                + " <- " + fkCatalog + "." + fkSchema + "." + fkTable + "." + fkColumn;
-    }
-}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/8c273022/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/db/LoggingDbLoaderDelegate.java
----------------------------------------------------------------------
diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/db/LoggingDbLoaderDelegate.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/db/LoggingDbLoaderDelegate.java
deleted file mode 100644
index 2632626..0000000
--- a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/db/LoggingDbLoaderDelegate.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.cayenne.dbsync.reverse.db;
-
-import org.apache.cayenne.map.DbEntity;
-import org.apache.cayenne.map.DbRelationship;
-import org.apache.commons.logging.Log;
-
-/**
- * @since 4.0
- */
-public class LoggingDbLoaderDelegate extends DefaultDbLoaderDelegate {
-
-    private final Log logger;
-
-    public LoggingDbLoaderDelegate(Log logger) {
-        this.logger = logger;
-    }
-
-    @Override
-    public void dbEntityAdded(DbEntity entity) {
-        logger.info("  Table: " + entity.getFullyQualifiedName());
-    }
-
-    @Override
-    public void dbEntityRemoved(DbEntity entity) {
-        logger.info("  Table removed: " + entity.getFullyQualifiedName());
-    }
-
-    @Override
-    public boolean dbRelationship(DbEntity entity) {
-        if (logger.isDebugEnabled()) {
-            logger.debug("    Relationships for " + entity.getFullyQualifiedName());
-        }
-
-        return true;
-    }
-
-    @Override
-    public boolean dbRelationshipLoaded(DbEntity entity, DbRelationship relationship) {
-        logger.info("    " + relationship);
-
-        return true;
-    }
-}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/8c273022/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/dbload/AbstractLoader.java
----------------------------------------------------------------------
diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/dbload/AbstractLoader.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/dbload/AbstractLoader.java
new file mode 100644
index 0000000..9c0cee4
--- /dev/null
+++ b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/dbload/AbstractLoader.java
@@ -0,0 +1,43 @@
+/*****************************************************************
+ *   Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ ****************************************************************/
+
+package org.apache.cayenne.dbsync.reverse.dbload;
+
+import java.sql.DatabaseMetaData;
+import java.sql.SQLException;
+import java.util.Objects;
+
+import org.apache.cayenne.dba.DbAdapter;
+
+public abstract class AbstractLoader {
+
+    static final String WILDCARD = "%";
+
+    protected DbAdapter adapter;
+    protected DbLoaderConfiguration config;
+    protected DbLoaderDelegate delegate;
+
+    AbstractLoader(DbAdapter adapter, DbLoaderConfiguration config, DbLoaderDelegate delegate) {
+        this.adapter = adapter;
+        this.config = Objects.requireNonNull(config);
+        this.delegate = Objects.requireNonNull(delegate);
+    }
+
+    public abstract void load(DatabaseMetaData metaData, DbLoadDataStore map) throws SQLException;
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/8c273022/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/dbload/AttributeLoader.java
----------------------------------------------------------------------
diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/dbload/AttributeLoader.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/dbload/AttributeLoader.java
new file mode 100644
index 0000000..25f64d2
--- /dev/null
+++ b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/dbload/AttributeLoader.java
@@ -0,0 +1,137 @@
+/*****************************************************************
+ *   Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ ****************************************************************/
+
+package org.apache.cayenne.dbsync.reverse.dbload;
+
+import java.sql.DatabaseMetaData;
+import java.sql.ResultSet;
+import java.sql.ResultSetMetaData;
+import java.sql.SQLException;
+
+import org.apache.cayenne.dba.DbAdapter;
+import org.apache.cayenne.dba.TypesMapping;
+import org.apache.cayenne.dbsync.reverse.filters.CatalogFilter;
+import org.apache.cayenne.dbsync.reverse.filters.PatternFilter;
+import org.apache.cayenne.dbsync.reverse.filters.SchemaFilter;
+import org.apache.cayenne.map.DbAttribute;
+import org.apache.cayenne.map.DbEntity;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+class AttributeLoader extends PerCatalogAndSchemaLoader {
+
+    private static final Log LOGGER = LogFactory.getLog(AttributeLoader.class);
+
+    private boolean firstRow;
+    private boolean supportAutoIncrement;
+
+    AttributeLoader(DbAdapter adapter, DbLoaderConfiguration config, DbLoaderDelegate delegate) {
+        super(adapter, config, delegate);
+        firstRow = true;
+        supportAutoIncrement = false;
+    }
+
+    protected ResultSet getResultSet(String catalogName, String schemaName, DatabaseMetaData metaData) throws SQLException {
+        return metaData.getColumns(catalogName, schemaName, WILDCARD, WILDCARD);
+    }
+
+    @Override
+    protected void processResultSet(CatalogFilter catalog, SchemaFilter schema, DbLoadDataStore map, ResultSet rs) throws SQLException {
+        if (firstRow) {
+            supportAutoIncrement = checkForAutoIncrement(rs);
+            firstRow = false;
+        }
+
+        // for a reason not quiet apparent to me, Oracle sometimes
+        // returns duplicate record sets for the same table, messing up
+        // table names. E.g. for the system table "WK$_ATTR_MAPPING" columns
+        // are returned twice - as "WK$_ATTR_MAPPING" and "WK$$_ATTR_MAPPING"...
+        // Go figure
+        String tableName = rs.getString("TABLE_NAME");
+        DbEntity entity = map.getDbEntity(tableName);
+        if(entity == null) {
+            return;
+        }
+
+        // Filter out columns by name
+        String columnName = rs.getString("COLUMN_NAME");
+        PatternFilter columnFilter = schema.tables.getIncludeTableColumnFilter(tableName);
+        if (columnFilter == null || !columnFilter.isIncluded(columnName)) {
+            if (LOGGER.isDebugEnabled()) {
+                LOGGER.debug("Skip column '" + tableName + "." + columnName +
+                        "' (Path: " + catalog.name + "/" + schema.name + "; Filter: " + columnFilter + ")");
+            }
+            return;
+        }
+
+        DbAttribute attribute = createDbAttribute(rs);
+        addToDbEntity(entity, attribute);
+    }
+
+    private boolean checkForAutoIncrement(ResultSet rs) throws SQLException {
+        ResultSetMetaData rsMetaData = rs.getMetaData();
+        for (int i = 1; i <= rsMetaData.getColumnCount(); i++) {
+            if("IS_AUTOINCREMENT".equals(rsMetaData.getColumnLabel(i))) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private void addToDbEntity(DbEntity entity, DbAttribute attribute) {
+        attribute.setEntity(entity);
+
+        // override existing attributes if it comes again
+        if (entity.getAttribute(attribute.getName()) != null) {
+            entity.removeAttribute(attribute.getName());
+        }
+        entity.addAttribute(attribute);
+    }
+
+    private DbAttribute createDbAttribute(ResultSet rs) throws SQLException {
+
+        // gets attribute's (column's) information
+        int columnType = rs.getInt("DATA_TYPE");
+
+        // ignore precision of non-decimal columns
+        int decimalDigits = -1;
+        if (TypesMapping.isDecimal(columnType)) {
+            decimalDigits = rs.getInt("DECIMAL_DIGITS");
+            if (rs.wasNull()) {
+                decimalDigits = -1;
+            }
+        }
+
+        // create attribute delegating this task to adapter
+        DbAttribute attr = adapter.buildAttribute(
+                rs.getString("COLUMN_NAME"),
+                rs.getString("TYPE_NAME"),
+                columnType,
+                rs.getInt("COLUMN_SIZE"),
+                decimalDigits,
+                rs.getBoolean("NULLABLE"));
+
+        if (supportAutoIncrement) {
+            if ("YES".equals(rs.getString("IS_AUTOINCREMENT"))) {
+                attr.setGenerated(true);
+            }
+        }
+        return attr;
+    }
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/8c273022/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/dbload/DbLoadDataStore.java
----------------------------------------------------------------------
diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/dbload/DbLoadDataStore.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/dbload/DbLoadDataStore.java
new file mode 100644
index 0000000..c0e79a8
--- /dev/null
+++ b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/dbload/DbLoadDataStore.java
@@ -0,0 +1,88 @@
+/*****************************************************************
+ *   Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ ****************************************************************/
+
+package org.apache.cayenne.dbsync.reverse.dbload;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
+
+import org.apache.cayenne.map.DataMap;
+import org.apache.cayenne.map.DbEntity;
+import org.apache.cayenne.map.DetectedDbEntity;
+import org.apache.cayenne.map.Procedure;
+
+/**
+ * Temporary storage for loaded from DB DbEntities and Procedures.
+ * DataMap is used but it's functionality is excessive and
+ * there can be unwanted side effects.
+ * To get rid of it parallel data structure for dbEntity, attributes,
+ * procedures etc.. must be created
+ */
+public class DbLoadDataStore extends DataMap {
+
+    private Map<String, Set<ExportedKey>> exportedKeys = new HashMap<>();
+
+    DbLoadDataStore() {
+        super("__generated_by_dbloader__");
+    }
+
+    @Override
+    public void addDbEntity(DbEntity entity) {
+        if(!(entity instanceof DetectedDbEntity)) {
+            throw new IllegalArgumentException("Only DetectedDbEntity can be inserted in this map");
+        }
+        super.addDbEntity(entity);
+    }
+
+    DbEntity addDbEntitySafe(DbEntity entity) {
+        if(!(entity instanceof DetectedDbEntity)) {
+            throw new IllegalArgumentException("Only DetectedDbEntity can be inserted in this map");
+        }
+        DbEntity old = getDbEntity(entity.getName());
+        if(old != null) {
+            removeDbEntity(old.getName());
+        }
+        addDbEntity(entity);
+        return old;
+    }
+
+    void addProcedureSafe(Procedure procedure) {
+        Procedure old = getProcedure(procedure.getName());
+        if(old != null) {
+            removeProcedure(old.getName());
+        }
+        addProcedure(procedure);
+    }
+
+    void addExportedKey(ExportedKey key) {
+        Set<ExportedKey> exportedKeys = this.exportedKeys.get(key.getStrKey());
+        if (exportedKeys == null) {
+            exportedKeys = new TreeSet<>();
+            this.exportedKeys.put(key.getStrKey(), exportedKeys);
+        }
+        exportedKeys.add(key);
+    }
+
+    Set<Map.Entry<String, Set<ExportedKey>>> getExportedKeysEntrySet() {
+        return exportedKeys.entrySet();
+    }
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/8c273022/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/dbload/DbLoader.java
----------------------------------------------------------------------
diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/dbload/DbLoader.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/dbload/DbLoader.java
new file mode 100644
index 0000000..5c1cfde
--- /dev/null
+++ b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/dbload/DbLoader.java
@@ -0,0 +1,118 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ *    or more contributor license agreements.  See the NOTICE file
+ *    distributed with this work for additional information
+ *    regarding copyright ownership.  The ASF licenses this file
+ *    to you under the Apache License, Version 2.0 (the
+ *    "License"); you may not use this file except in compliance
+ *    with the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing,
+ *    software distributed under the License is distributed on an
+ *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *    KIND, either express or implied.  See the License for the
+ *    specific language governing permissions and limitations
+ *    under the License.
+ */
+package org.apache.cayenne.dbsync.reverse.dbload;
+
+import java.sql.Connection;
+import java.sql.DatabaseMetaData;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+import org.apache.cayenne.dba.DbAdapter;
+import org.apache.cayenne.dbsync.naming.ObjectNameGenerator;
+import org.apache.cayenne.map.DataMap;
+
+/**
+ * Loader for data from DB.
+ * Creates DbEntities and Procedures based on DB meta data.
+ * Consists of list of loaders that iteratively loads small parts of data,
+ * e.g. Entity name, Attributes, Relationships...
+ * @see AbstractLoader and its children
+ */
+public class DbLoader {
+
+    private List<AbstractLoader> loaders = new ArrayList<>();
+
+    private final Connection connection;
+    private final DbAdapter adapter;
+    private final DbLoaderConfiguration config;
+    private final DbLoaderDelegate delegate;
+    private final ObjectNameGenerator nameGenerator;
+
+    public DbLoader(DbAdapter adapter, Connection connection, DbLoaderConfiguration config,
+                    DbLoaderDelegate delegate, ObjectNameGenerator nameGenerator) {
+        this.adapter = Objects.requireNonNull(adapter);
+        this.connection = Objects.requireNonNull(connection);
+        this.config = Objects.requireNonNull(config);
+        this.nameGenerator = Objects.requireNonNull(nameGenerator);
+        this.delegate = delegate == null ? new DefaultDbLoaderDelegate() : delegate;
+
+        createLoaders();
+    }
+
+    /**
+     * Order of loaders is important, as loader can rely on data previously loaded
+     */
+    private void createLoaders() {
+        loaders.add(new EntityLoader(adapter, config, delegate));
+        loaders.add(new AttributeLoader(adapter, config, delegate));
+        loaders.add(new PrimaryKeyLoader(config, delegate));
+        loaders.add(new ExportedKeyLoader(config, delegate));
+        loaders.add(new RelationshipLoader(config, delegate, nameGenerator));
+        loaders.add(new ProcedureLoader(adapter, config, delegate));
+        loaders.add(new ProcedureColumnLoader(adapter, config, delegate));
+    }
+
+    /**
+     * @return new DataMap with data loaded from DB
+     */
+    public DataMap load() throws SQLException {
+        DbLoadDataStore loadedData = new DbLoadDataStore();
+        DatabaseMetaData metaData = connection.getMetaData();
+
+        for(AbstractLoader loader : loaders) {
+            loader.load(metaData, loadedData);
+        }
+        return loadedData;
+    }
+
+    //// Utility methods that better be moved somewhere ////
+
+    /**
+     * Retrieves catalogs for a given connection.
+     * using a static method for catalog loading as we don't need a full DbLoader2 for this operation
+     * @return List with the catalog names; empty list if none found.
+     */
+    public static List<String> loadCatalogs(Connection connection) throws SQLException {
+        try (ResultSet rs = connection.getMetaData().getCatalogs()) {
+            return getStrings(rs);
+        }
+    }
+
+    /**
+     * Retrieves the schemas for the given connection.
+     * using a static method for catalog loading as we don't need a full DbLoader2 for this operation
+     * @return List with the schema names; empty list if none found.
+     */
+    public static List<String> loadSchemas(Connection connection) throws SQLException {
+        try (ResultSet rs = connection.getMetaData().getSchemas()) {
+            return getStrings(rs);
+        }
+    }
+
+    private static List<String> getStrings(ResultSet rs) throws SQLException {
+        List<String> strings = new ArrayList<>();
+        while (rs.next()) {
+            strings.add(rs.getString(1));
+        }
+        return strings;
+    }
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/8c273022/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/dbload/DbLoaderConfiguration.java
----------------------------------------------------------------------
diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/dbload/DbLoaderConfiguration.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/dbload/DbLoaderConfiguration.java
new file mode 100644
index 0000000..e37ae60
--- /dev/null
+++ b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/dbload/DbLoaderConfiguration.java
@@ -0,0 +1,85 @@
+/*****************************************************************
+ *   Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ ****************************************************************/
+
+package org.apache.cayenne.dbsync.reverse.dbload;
+
+import org.apache.cayenne.dbsync.reverse.filters.TableFilter;
+import org.apache.cayenne.dbsync.reverse.filters.FiltersConfig;
+import org.apache.cayenne.dbsync.reverse.filters.PatternFilter;
+
+/**
+ * @since 4.0
+ */
+public class DbLoaderConfiguration {
+
+    private Boolean skipRelationshipsLoading;
+    private Boolean skipPrimaryKeyLoading;
+    private String[] tableTypes;
+    private FiltersConfig filtersConfig;
+
+    public String[] getTableTypes() {
+        return tableTypes;
+    }
+
+    public void setTableTypes(String[] tableTypes) {
+        this.tableTypes = tableTypes;
+    }
+
+    public FiltersConfig getFiltersConfig() {
+        if (filtersConfig == null) {
+            // this case is used often in tests where config not initialized properly
+            return FiltersConfig.create(null, null, TableFilter.everything(), PatternFilter.INCLUDE_NOTHING);
+        }
+        return filtersConfig;
+    }
+
+    public void setFiltersConfig(FiltersConfig filtersConfig) {
+        this.filtersConfig = filtersConfig;
+    }
+
+    public boolean isSkipRelationshipsLoading() {
+        return skipRelationshipsLoading != null && skipRelationshipsLoading;
+    }
+
+    public void setSkipRelationshipsLoading(Boolean skipRelationshipsLoading) {
+        this.skipRelationshipsLoading = skipRelationshipsLoading;
+    }
+
+    public boolean isSkipPrimaryKeyLoading() {
+        return skipPrimaryKeyLoading != null && skipPrimaryKeyLoading;
+    }
+
+    public void setSkipPrimaryKeyLoading(Boolean skipPrimaryKeyLoading) {
+        this.skipPrimaryKeyLoading = skipPrimaryKeyLoading;
+    }
+
+    @Override
+    public String toString() {
+        String res = "EntitiesFilters: " + getFiltersConfig();
+        if (isSkipRelationshipsLoading()) {
+            res += "\n Skip Loading Relationships! \n";
+        }
+
+        if (isSkipPrimaryKeyLoading()) {
+            res += "\n Skip Loading PrimaryKeys! \n";
+        }
+
+        return res;
+    }
+}