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 <stari...@gmail.com> Authored: Thu Dec 8 18:09:29 2016 +0300 Committer: Nikita Timofeev <stari...@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; + } +}