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/09/30 14:23:49 UTC
[3/9] cayenne git commit: CAY-2115 DbLoader - allow loading DataMap
without Obj layer
http://git-wip-us.apache.org/repos/asf/cayenne/blob/cf172fc9/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
new file mode 100644
index 0000000..e43c15a
--- /dev/null
+++ b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/db/DbLoader.java
@@ -0,0 +1,702 @@
+/*****************************************************************
+ * 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.merge.EntityMergeSupport;
+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.DbRelationshipDetected;
+import org.apache.cayenne.map.ObjEntity;
+import org.apache.cayenne.map.Procedure;
+import org.apache.cayenne.map.ProcedureParameter;
+import org.apache.cayenne.map.naming.DefaultUniqueNameGenerator;
+import org.apache.cayenne.map.naming.ExportedKey;
+import org.apache.cayenne.map.naming.LegacyNameGenerator;
+import org.apache.cayenne.map.naming.NameCheckers;
+import org.apache.cayenne.map.naming.ObjectNameGenerator;
+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.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
+
+/**
+ * Performs reverse engineering of the database. It can create
+ * DataMaps using database meta data obtained via JDBC driver.
+ *
+ * @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 boolean creatingMeaningfulPK;
+
+ /**
+ * Strategy for choosing names for entities, attributes and relationships
+ */
+ private ObjectNameGenerator nameGenerator;
+
+ private DatabaseMetaData metaData;
+
+
+ /**
+ * Creates new DbLoader.
+ */
+ public DbLoader(Connection connection, DbAdapter adapter, DbLoaderDelegate delegate) {
+ this(connection, adapter, delegate, new LegacyNameGenerator());
+ }
+
+ /**
+ * Creates new DbLoader with specified naming strategy.
+ *
+ * @since 3.0
+ */
+ public DbLoader(Connection connection, DbAdapter adapter, DbLoaderDelegate delegate, ObjectNameGenerator strategy) {
+ this.adapter = adapter;
+ this.connection = connection;
+ this.delegate = delegate == null ? new DefaultDbLoaderDelegate() : delegate;
+
+ setNameGenerator(strategy);
+ }
+
+ private static List<String> getStrings(ResultSet rs) throws SQLException {
+ List<String> strings = new ArrayList<String>();
+
+ while (rs.next()) {
+ strings.add(rs.getString(1));
+ }
+
+ return strings;
+ }
+
+ private static Collection<ObjEntity> loadObjEntities(DataMap map, DbLoaderConfiguration config,
+ Collection<DbEntity> entities, ObjectNameGenerator nameGenerator) {
+ if (entities.isEmpty()) {
+ return Collections.emptyList();
+ }
+
+ Collection<ObjEntity> loadedEntities = new ArrayList<ObjEntity>(entities.size());
+
+ // doLoad empty ObjEntities for all the tables
+ for (DbEntity dbEntity : entities) {
+
+ // check if there are existing entities
+
+ // TODO: performance. This is an O(n^2) search and it shows on
+ // YourKit profiles. Pre-cache mapped entities perhaps (?)
+ Collection<ObjEntity> existing = map.getMappedEntities(dbEntity);
+ if (!existing.isEmpty()) {
+ loadedEntities.addAll(existing);
+ continue;
+ }
+
+ String objEntityName = DefaultUniqueNameGenerator.generate(NameCheckers.objEntity, map,
+ nameGenerator.createObjEntityName(dbEntity));
+
+ ObjEntity objEntity = new ObjEntity(objEntityName);
+ objEntity.setDbEntity(dbEntity);
+ objEntity.setClassName(config.getGenericClassName() != null ? config.getGenericClassName() : map
+ .getNameWithDefaultPackage(objEntity.getName()));
+
+ map.addObjEntity(objEntity);
+ loadedEntities.add(objEntity);
+ }
+
+ return loadedEntities;
+ }
+
+ /**
+ * Flattens many-to-many relationships in the generated model.
+ */
+ public static void flattenManyToManyRelationships(DataMap map, Collection<ObjEntity> loadedObjEntities,
+ ObjectNameGenerator objectNameGenerator) {
+ if (loadedObjEntities.isEmpty()) {
+ return;
+ }
+ Collection<ObjEntity> entitiesForDelete = new LinkedList<ObjEntity>();
+
+ for (ObjEntity curEntity : loadedObjEntities) {
+ ManyToManyCandidateEntity entity = ManyToManyCandidateEntity.build(curEntity);
+
+ if (entity != null) {
+ entity.optimizeRelationships(objectNameGenerator);
+ entitiesForDelete.add(curEntity);
+ }
+ }
+
+ // remove needed entities
+ for (ObjEntity curDeleteEntity : entitiesForDelete) {
+ map.removeObjEntity(curDeleteEntity.getName(), true);
+ }
+ loadedObjEntities.removeAll(entitiesForDelete);
+ }
+
+ 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;
+ }
+ }
+
+ /**
+ * Returns DatabaseMetaData object associated with this DbLoader.
+ */
+ private DatabaseMetaData getMetaData() throws SQLException {
+ if (metaData == null) {
+ metaData = connection.getMetaData();
+ }
+ return metaData;
+ }
+
+ /**
+ * Check if database support schemas.
+ */
+ protected boolean supportSchemas() throws SQLException {
+ if (metaData == null) {
+ metaData = connection.getMetaData();
+ }
+ return metaData.supportsSchemasInTableDefinitions();
+ }
+
+ /**
+ * Check if database support catalogs.
+ */
+ protected boolean supportCatalogs() throws SQLException {
+ if (metaData == null) {
+ metaData = connection.getMetaData();
+ }
+ return metaData.supportsCatalogsInTableDefinitions();
+ }
+
+ /**
+ * @since 3.0
+ */
+ public void setCreatingMeaningfulPK(boolean creatingMeaningfulPK) {
+ this.creatingMeaningfulPK = creatingMeaningfulPK;
+ }
+
+ /**
+ * Retrieves catalogs for the database associated with this DbLoader.
+ *
+ * @return List with the catalog names, empty Array if none found.
+ */
+ public List<String> loadCatalogs() throws SQLException {
+ try (ResultSet rs = getMetaData().getCatalogs()) {
+ return getStrings(rs);
+ }
+ }
+
+ /**
+ * Retrieves the schemas for the database.
+ *
+ * @return List with the schema names, empty Array if none found.
+ */
+ public List<String> loadSchemas() throws SQLException {
+
+ try (ResultSet rs = getMetaData().getSchemas()) {
+ return getStrings(rs);
+ }
+ }
+
+ /**
+ * Creates an ObjEntity for each DbEntity in the map.
+ */
+ Collection<ObjEntity> loadObjEntities(DataMap map, DbLoaderConfiguration config,
+ Collection<DbEntity> entities) {
+ Collection<ObjEntity> loadedEntities = DbLoader.loadObjEntities(map, config, entities, nameGenerator);
+
+ createEntityMerger(map).synchronizeWithDbEntities(loadedEntities);
+
+ return loadedEntities;
+ }
+
+ /**
+ * @since 4.0
+ */
+ protected EntityMergeSupport createEntityMerger(DataMap map) {
+ return new EntityMergeSupport(map, nameGenerator, !creatingMeaningfulPK);
+ }
+
+ 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.pkCatalog)
+ .append(pkEntity.getSchema(), key.pkSchema).append(fkEntity.getCatalog(), key.fkCatalog)
+ .append(fkEntity.getSchema(), key.fkSchema).isEquals()) {
+
+ LOGGER.info("Skip relation: '" + key + "' because it related to objects from other catalog/schema");
+ LOGGER.info(" relation primary key: '" + key.pkCatalog + "." + key.pkSchema + "'");
+ LOGGER.info(" primary key entity: '" + pkEntity.getCatalog() + "." + pkEntity.getSchema() + "'");
+ LOGGER.info(" relation foreign key: '" + key.fkCatalog + "." + key.fkSchema + "'");
+ LOGGER.info(" foreign key entity: '" + fkEntity.getCatalog() + "." + fkEntity.getSchema() + "'");
+ continue;
+ }
+
+ // forwardRelationship is a reference from table with primary key
+ DbRelationship forwardRelationship = new DbRelationship(generateName(pkEntity, key, true));
+ forwardRelationship.setSourceEntity(pkEntity);
+ forwardRelationship.setTargetEntityName(fkEntity);
+
+ // forwardRelationship is a reference from table with foreign key,
+ // it is what exactly we load from db
+ DbRelationshipDetected reverseRelationship = new DbRelationshipDetected(generateName(fkEntity, key, false));
+ 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);
+ forwardRelationship.setName(generateName(pkEntity, key, !isOneToOne));
+
+ 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, DbRelationshipDetected 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");
+ }
+
+ private String generateName(DbEntity entity, ExportedKey key, boolean toMany) {
+ String forwardPreferredName = nameGenerator.createDbRelationshipName(key, toMany);
+ return DefaultUniqueNameGenerator.generate(NameCheckers.dbRelationship, entity, forwardPreferredName);
+ }
+
+ private void fireObjEntitiesAddedEvents(Collection<ObjEntity> loadedObjEntities) {
+ for (ObjEntity curEntity : loadedObjEntities) {
+ // notify delegate
+ if (delegate != null) {
+ delegate.objEntityAdded(curEntity);
+ }
+ }
+ }
+
+ 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 the specified config and
+ * fills the specified DataMap object with DB and object mapping info.
+ *
+ * @since 4.0
+ */
+ public void load(DataMap dataMap, DbLoaderConfiguration config) throws SQLException {
+ LOGGER.info("Schema loading...");
+
+ String[] types = getTableTypes(config);
+
+ for (CatalogFilter catalog : config.getFiltersConfig().catalogs) {
+ for (SchemaFilter schema : catalog.schemas) {
+
+ List<DbEntity> entities = createTableLoader(catalog.name, schema.name, schema.tables).loadDbEntities(
+ dataMap, config, types);
+
+ if (entities != null) {
+ loadDbRelationships(config, catalog.name, schema.name, entities);
+
+ prepareObjLayer(dataMap, config, 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));
+ }
+
+ private void prepareObjLayer(DataMap dataMap, DbLoaderConfiguration config, Collection<DbEntity> entities) {
+ Collection<ObjEntity> loadedObjEntities = loadObjEntities(dataMap, config, entities);
+ flattenManyToManyRelationships(dataMap, loadedObjEntities, nameGenerator);
+ fireObjEntitiesAddedEvents(loadedObjEntities);
+ }
+
+ /**
+ * Performs database reverse engineering to match the specified catalog,
+ * schema, table name and table type patterns and fills the specified
+ * DataMap object with DB and object mapping info.
+ *
+ * @since 4.0
+ */
+ public DataMap load(DbLoaderConfiguration config) throws SQLException {
+
+ DataMap dataMap = new DataMap();
+ load(dataMap, config);
+ loadProcedures(dataMap, config);
+
+ return dataMap;
+ }
+
+ /**
+ * 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
+ */
+ public 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().catalogs) {
+ 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 s = columnsRS.getString("PROCEDURE_SCHEM");
+ String name = columnsRS.getString("PROCEDURE_NAME");
+ String key = (s == null ? "" : s + '.') + 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.catalogs) {
+ 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()).isInclude(
+ 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;
+ }
+
+ /**
+ * Sets new naming strategy for reverse engineering
+ *
+ * @since 3.0
+ */
+ public void setNameGenerator(ObjectNameGenerator strategy) {
+ if (strategy == null) {
+ LOGGER.warn("Attempt to set null into NameGenerator. LegacyNameGenerator will be used.");
+ this.nameGenerator = new LegacyNameGenerator();
+ } else {
+ this.nameGenerator = strategy;
+ }
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/cayenne/blob/cf172fc9/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
new file mode 100644
index 0000000..f3adbd1
--- /dev/null
+++ b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/db/DbLoaderConfiguration.java
@@ -0,0 +1,128 @@
+/*****************************************************************
+ * 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 {
+
+ /**
+ * Returns a name of a generic class that should be used for all
+ * ObjEntities. The most common generic class is
+ * {@link org.apache.cayenne.CayenneDataObject}. If generic class name is
+ * null (which is the default), DbLoader will assign each entity a unique
+ * class name derived from the table name.
+ *
+ */
+ private String genericClassName;
+
+ /**
+ * Java class implementing org.apache.cayenne.map.naming.NamingStrategy.
+ * This is used to specify how ObjEntities will be mapped from the imported
+ * DB schema.
+ */
+ private String namingStrategy;
+
+ private Boolean skipRelationshipsLoading;
+
+ private Boolean skipPrimaryKeyLoading;
+
+ private String[] tableTypes;
+
+ private FiltersConfig filtersConfig;
+
+ public String getGenericClassName() {
+ return genericClassName;
+ }
+
+ public void setGenericClassName(String genericClassName) {
+ this.genericClassName = genericClassName;
+ }
+
+ public String[] getTableTypes() {
+ return tableTypes;
+ }
+
+ public void setTableTypes(String[] tableTypes) {
+ this.tableTypes = tableTypes;
+ }
+
+ public String getNamingStrategy() {
+ return namingStrategy;
+ }
+
+ public void setNamingStrategy(String namingStrategy) {
+ this.namingStrategy = namingStrategy;
+ }
+
+ 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 Boolean getSkipRelationshipsLoading() {
+ return skipRelationshipsLoading;
+ }
+
+ public void setSkipRelationshipsLoading(Boolean skipRelationshipsLoading) {
+ this.skipRelationshipsLoading = skipRelationshipsLoading;
+ }
+
+ public void setSkipPrimaryKeyLoading(Boolean skipPrimaryKeyLoading) {
+ this.skipPrimaryKeyLoading = skipPrimaryKeyLoading;
+ }
+
+ public boolean getSkipPrimaryKeyLoading() {
+ return skipPrimaryKeyLoading;
+ }
+
+ public boolean isSkipPrimaryKeyLoading() {
+ return skipPrimaryKeyLoading != null && 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/cf172fc9/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
new file mode 100644
index 0000000..e85059f
--- /dev/null
+++ b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/db/DbLoaderDelegate.java
@@ -0,0 +1,58 @@
+/*****************************************************************
+ * 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;
+
+/**
+ * DbLoaderDelegate defines API that allows to control the behavior of DbLoader
+ * during the database reverse-engineering. Delegate is also notified of the
+ * progress of reverse-engineering.
+ */
+public interface DbLoaderDelegate {
+
+ void dbEntityAdded(DbEntity entity);
+
+ void dbEntityRemoved(DbEntity entity);
+
+ /**
+ * Called before relationship loading for db-entity
+ * @param entity
+ *
+ * @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);
+
+ void objEntityAdded(ObjEntity entity);
+
+ void objEntityRemoved(ObjEntity entity);
+}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/cf172fc9/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
new file mode 100644
index 0000000..d1230bd
--- /dev/null
+++ b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/db/DbTableLoader.java
@@ -0,0 +1,195 @@
+/*****************************************************************
+ * 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 java.sql.DatabaseMetaData;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.List;
+
+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.cayenne.map.ObjEntity;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * @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.
+ * @return
+ * @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<DbEntity>();
+ for (DbEntity dbEntity : tables) {
+ DbEntity oldEnt = map.getDbEntity(dbEntity.getName());
+ if (oldEnt != null) {
+ Collection<ObjEntity> oldObjEnt = map.getMappedEntities(oldEnt);
+ if (!oldObjEnt.isEmpty()) {
+ for (ObjEntity objEntity : oldObjEnt) {
+ LOGGER.debug("Delete ObjEntity: " + objEntity.getName());
+ map.removeObjEntity(objEntity.getName(), true);
+ delegate.objEntityRemoved(objEntity);
+ }
+ }
+
+ 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/cf172fc9/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
new file mode 100644
index 0000000..cf336df
--- /dev/null
+++ b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/db/DefaultDbLoaderDelegate.java
@@ -0,0 +1,59 @@
+/*****************************************************************
+ * 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;
+
+/**
+ * @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;
+ }
+
+ @Override
+ public void objEntityAdded(ObjEntity entity) {
+
+ }
+
+ @Override
+ public void objEntityRemoved(ObjEntity entity) {
+
+ }
+}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/cf172fc9/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
new file mode 100644
index 0000000..3a9a905
--- /dev/null
+++ b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/db/LoggingDbLoaderDelegate.java
@@ -0,0 +1,76 @@
+/*
+ * 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;
+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;
+ }
+
+ @Override
+ public void objEntityAdded(ObjEntity entity) {
+ if (logger.isDebugEnabled()) {
+ logger.debug(" Class: " + entity.getName());
+ }
+ }
+
+ @Override
+ public void objEntityRemoved(ObjEntity entity) {
+ if (logger.isDebugEnabled()) {
+ logger.debug(" Class removed: " + entity.getName());
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/cf172fc9/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/db/ManyToManyCandidateEntity.java
----------------------------------------------------------------------
diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/db/ManyToManyCandidateEntity.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/db/ManyToManyCandidateEntity.java
new file mode 100644
index 0000000..f185619
--- /dev/null
+++ b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/db/ManyToManyCandidateEntity.java
@@ -0,0 +1,143 @@
+/*****************************************************************
+ * 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;
+import org.apache.cayenne.map.ObjEntity;
+import org.apache.cayenne.map.ObjRelationship;
+import org.apache.cayenne.map.naming.DefaultUniqueNameGenerator;
+import org.apache.cayenne.map.naming.ExportedKey;
+import org.apache.cayenne.map.naming.NameCheckers;
+import org.apache.cayenne.map.naming.ObjectNameGenerator;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Class represent ObjEntity that may be optimized using flattened relationships
+ * as many to many table
+ */
+class ManyToManyCandidateEntity {
+
+ private static final Log LOG = LogFactory.getLog(ManyToManyCandidateEntity.class);
+
+ private final ObjEntity joinEntity;
+
+ private final DbRelationship dbRel1;
+ private final DbRelationship dbRel2;
+
+ private final ObjEntity entity1;
+ private final ObjEntity entity2;
+
+ private final DbRelationship reverseRelationship1;
+ private final DbRelationship reverseRelationship2;
+
+ private ManyToManyCandidateEntity(ObjEntity entityValue, List<ObjRelationship> relationships) {
+ joinEntity = entityValue;
+
+ ObjRelationship rel1 = relationships.get(0);
+ ObjRelationship rel2 = relationships.get(1);
+
+ dbRel1 = rel1.getDbRelationships().get(0);
+ dbRel2 = rel2.getDbRelationships().get(0);
+
+ reverseRelationship1 = dbRel1.getReverseRelationship();
+ reverseRelationship2 = dbRel2.getReverseRelationship();
+
+ entity1 = rel1.getTargetEntity();
+ entity2 = rel2.getTargetEntity();
+ }
+
+ /**
+ * Method check - if current entity represent many to many temporary table
+ *
+ * @return true if current entity is represent many to many table; otherwise returns false
+ */
+ public static ManyToManyCandidateEntity build(ObjEntity joinEntity) {
+ ArrayList<ObjRelationship> relationships = new ArrayList<ObjRelationship>(joinEntity.getRelationships());
+ if (relationships.size() != 2 || (relationships.get(0).getDbRelationships().isEmpty() || relationships.get(1).getDbRelationships().isEmpty())) {
+ return null;
+ }
+
+ ManyToManyCandidateEntity candidateEntity = new ManyToManyCandidateEntity(joinEntity, relationships);
+ if (candidateEntity.isManyToMany()) {
+ return candidateEntity;
+ }
+
+ return null;
+ }
+
+ private boolean isManyToMany() {
+ boolean isNotHaveAttributes = joinEntity.getAttributes().size() == 0;
+
+ return isNotHaveAttributes
+ && reverseRelationship1 != null && reverseRelationship1.isToDependentPK()
+ && reverseRelationship2 != null && reverseRelationship2.isToDependentPK()
+ && entity1 != null && entity2 != null;
+ }
+
+ private void addFlattenedRelationship(ObjectNameGenerator nameGenerator, ObjEntity srcEntity, ObjEntity dstEntity,
+ DbRelationship rel1, DbRelationship rel2) {
+
+ if (rel1.getSourceAttributes().isEmpty() && rel2.getTargetAttributes().isEmpty()) {
+ LOG.warn("Wrong call ManyToManyCandidateEntity.addFlattenedRelationship(... , " + srcEntity.getName()
+ + ", " + dstEntity.getName() + ", ...)");
+
+ return;
+ }
+
+ ExportedKey key = new ExportedKey(
+ rel1.getSourceEntity().getName(),
+ rel1.getSourceAttributes().iterator().next().getName(),
+ null,
+ rel2.getTargetEntity().getName(),
+ rel2.getTargetAttributes().iterator().next().getName(),
+ null,
+ (short) 1);
+
+ ObjRelationship newRelationship = new ObjRelationship();
+ newRelationship.setName(DefaultUniqueNameGenerator.generate(NameCheckers.objRelationship, srcEntity,
+ nameGenerator.createDbRelationshipName(key, true)));
+
+ newRelationship.setSourceEntity(srcEntity);
+ newRelationship.setTargetEntityName(dstEntity);
+
+ newRelationship.addDbRelationship(rel1);
+ newRelationship.addDbRelationship(rel2);
+
+ srcEntity.addRelationship(newRelationship);
+ }
+
+ /**
+ * Method make direct relationships between 2 entities and remove relationships to
+ * many to many entity
+ *
+ * @param nameGenerator
+ */
+ public void optimizeRelationships(ObjectNameGenerator nameGenerator) {
+ entity1.removeRelationship(reverseRelationship1.getName());
+ entity2.removeRelationship(reverseRelationship2.getName());
+
+ addFlattenedRelationship(nameGenerator, entity1, entity2, reverseRelationship1, dbRel2);
+ addFlattenedRelationship(nameGenerator, entity2, entity1, reverseRelationship2, dbRel1);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/cf172fc9/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/DbMergerTest.java
----------------------------------------------------------------------
diff --git a/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/DbMergerTest.java b/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/DbMergerTest.java
index 8cb03dd..a695d2d 100644
--- a/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/DbMergerTest.java
+++ b/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/DbMergerTest.java
@@ -20,7 +20,7 @@ package org.apache.cayenne.dbsync.merge;
import org.apache.cayenne.dbsync.merge.builders.DbEntityBuilder;
import org.apache.cayenne.dbsync.merge.factory.HSQLMergerTokenFactory;
-import org.apache.cayenne.dbsync.reverse.DbLoaderConfiguration;
+import org.apache.cayenne.dbsync.reverse.db.DbLoaderConfiguration;
import org.apache.cayenne.map.DataMap;
import org.apache.cayenne.map.DbEntity;
import org.junit.Test;
http://git-wip-us.apache.org/repos/asf/cayenne/blob/cf172fc9/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/MergeCase.java
----------------------------------------------------------------------
diff --git a/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/MergeCase.java b/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/MergeCase.java
index c2357b0..ac68802 100644
--- a/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/MergeCase.java
+++ b/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/MergeCase.java
@@ -23,7 +23,7 @@ import org.apache.cayenne.configuration.server.ServerRuntime;
import org.apache.cayenne.dba.DbAdapter;
import org.apache.cayenne.dbsync.merge.factory.MergerTokenFactory;
import org.apache.cayenne.dbsync.merge.factory.MergerTokenFactoryProvider;
-import org.apache.cayenne.dbsync.reverse.DbLoaderConfiguration;
+import org.apache.cayenne.dbsync.reverse.db.DbLoaderConfiguration;
import org.apache.cayenne.dbsync.reverse.filters.FiltersConfig;
import org.apache.cayenne.dbsync.reverse.filters.PatternFilter;
import org.apache.cayenne.dbsync.reverse.filters.TableFilter;
http://git-wip-us.apache.org/repos/asf/cayenne/blob/cf172fc9/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/reverse/DbLoaderIT.java
----------------------------------------------------------------------
diff --git a/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/reverse/DbLoaderIT.java b/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/reverse/DbLoaderIT.java
deleted file mode 100644
index 93539ed..0000000
--- a/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/reverse/DbLoaderIT.java
+++ /dev/null
@@ -1,434 +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;
-
-import org.apache.cayenne.dbsync.reverse.filters.FiltersConfig;
-import org.apache.cayenne.dbsync.reverse.filters.PatternFilter;
-import org.apache.cayenne.dbsync.reverse.filters.TableFilter;
-import org.apache.cayenne.configuration.server.ServerRuntime;
-import org.apache.cayenne.dba.DbAdapter;
-import org.apache.cayenne.dba.TypesMapping;
-import org.apache.cayenne.di.Inject;
-import org.apache.cayenne.map.*;
-import org.apache.cayenne.unit.UnitDbAdapter;
-import org.apache.cayenne.unit.di.server.CayenneProjects;
-import org.apache.cayenne.unit.di.server.ServerCase;
-import org.apache.cayenne.unit.di.server.ServerCaseDataSourceFactory;
-import org.apache.cayenne.unit.di.server.UseServerRuntime;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-
-import java.sql.Connection;
-import java.sql.Types;
-import java.util.Collection;
-import java.util.List;
-
-import static org.junit.Assert.*;
-
-@UseServerRuntime(CayenneProjects.TESTMAP_PROJECT)
-public class DbLoaderIT extends ServerCase {
-
- public static final DbLoaderConfiguration CONFIG = new DbLoaderConfiguration();
- @Inject
- private ServerRuntime runtime;
-
- @Inject
- private DbAdapter adapter;
-
- @Inject
- private ServerCaseDataSourceFactory dataSourceFactory;
-
- @Inject
- private UnitDbAdapter accessStackAdapter;
-
- private DbLoader loader;
-
- private Connection connection;
-
- private static String msgForTypeMismatch(DbAttribute origAttr, DbAttribute newAttr) {
- return msgForTypeMismatch(origAttr.getType(), newAttr);
- }
-
- private static String msgForTypeMismatch(int origType, DbAttribute newAttr) {
- String nt = TypesMapping.getSqlNameByType(newAttr.getType());
- String ot = TypesMapping.getSqlNameByType(origType);
- return attrMismatch(newAttr.getName(), "expected type: <" + ot + ">, but was <" + nt + ">");
- }
-
- private static String attrMismatch(String attrName, String msg) {
- return "[Error loading attribute '" + attrName + "': " + msg + "]";
- }
-
- @Before
- public void before() throws Exception {
- this.connection = dataSourceFactory.getSharedDataSource().getConnection();
- this.loader = new DbLoader(connection, adapter, null);
- }
-
- @After
- public void after() throws Exception {
- connection.close();
- }
-
- @Test
- public void testGetTableTypes() throws Exception {
-
- List<?> tableTypes = loader.getTableTypes();
-
- assertNotNull(tableTypes);
-
- String tableLabel = adapter.tableTypeForTable();
- if (tableLabel != null) {
- assertTrue("Missing type for table '" + tableLabel + "' - " + tableTypes, tableTypes.contains(tableLabel));
- }
-
- String viewLabel = adapter.tableTypeForView();
- if (viewLabel != null) {
- assertTrue("Missing type for view '" + viewLabel + "' - " + tableTypes, tableTypes.contains(viewLabel));
- }
- }
-
- @Test
- public void testGetTables() throws Exception {
-
- String tableLabel = adapter.tableTypeForTable();
-
- List<DetectedDbEntity> tables = loader.createTableLoader(null, null, TableFilter.everything())
- .getDbEntities(TableFilter.everything(), new String[]{tableLabel});
-
- assertNotNull(tables);
-
- boolean foundArtist = false;
-
- for (DetectedDbEntity table : tables) {
- if ("ARTIST".equalsIgnoreCase(table.getName())) {
- foundArtist = true;
- break;
- }
- }
-
- assertTrue("'ARTIST' is missing from the table list: " + tables, foundArtist);
- }
-
- @Test
- public void testGetTablesWithWrongCatalog() throws Exception {
-
- DbLoaderConfiguration config = new DbLoaderConfiguration();
- config.setFiltersConfig(
- FiltersConfig.create("WRONG", null, TableFilter.everything(), PatternFilter.INCLUDE_NOTHING));
- List<DetectedDbEntity> tables = loader
- .createTableLoader("WRONG", null, TableFilter.everything())
- .getDbEntities(TableFilter.everything(), new String[]{adapter.tableTypeForTable()});
-
- assertNotNull(tables);
- assertTrue(tables.isEmpty());
- }
-
- @Test
- public void testGetTablesWithWrongSchema() throws Exception {
-
- DbLoaderConfiguration config = new DbLoaderConfiguration();
- config.setFiltersConfig(
- FiltersConfig.create(null, "WRONG", TableFilter.everything(), PatternFilter.INCLUDE_NOTHING));
- List<DetectedDbEntity> tables = loader
- .createTableLoader(null, "WRONG", TableFilter.everything())
- .getDbEntities(TableFilter.everything(), new String[]{adapter.tableTypeForTable()});
-
- assertNotNull(tables);
- assertTrue(tables.isEmpty());
- }
-
- @Test
- public void testLoadWithMeaningfulPK() throws Exception {
-
- DataMap map = new DataMap();
- String[] tableLabel = {adapter.tableTypeForTable()};
-
- loader.setCreatingMeaningfulPK(true);
-
- List<DbEntity> entities = loader
- .createTableLoader(null, null, TableFilter.everything())
- .loadDbEntities(map, CONFIG, tableLabel);
-
- loader.loadObjEntities(map, CONFIG, entities);
-
- ObjEntity artist = map.getObjEntity("Artist");
- assertNotNull(artist);
-
- ObjAttribute id = artist.getAttribute("artistId");
- assertNotNull(id);
- }
-
- /**
- * DataMap loading is in one big test method, since breaking it in
- * individual tests would require multiple reads of metatdata which is
- * extremely slow on some RDBMS (Sybase).
- */
- @Test
- public void testLoad() throws Exception {
-
- boolean supportsUnique = runtime.getDataDomain().getDataNodes().iterator().next().getAdapter()
- .supportsUniqueConstraints();
- boolean supportsLobs = accessStackAdapter.supportsLobs();
- boolean supportsFK = accessStackAdapter.supportsFKConstraints();
-
- DataMap map = new DataMap();
- map.setDefaultPackage("foo.x");
-
- String tableLabel = adapter.tableTypeForTable();
-
- // *** TESTING THIS ***
- List<DbEntity> entities = loader
- .createTableLoader(null, null, TableFilter.everything())
- .loadDbEntities(map, CONFIG, new String[]{adapter.tableTypeForTable()});
-
-
- assertDbEntities(map);
-
- if (supportsLobs) {
- assertLobDbEntities(map);
- }
-
- // *** TESTING THIS ***
- loader.loadDbRelationships(CONFIG, null, null, entities);
-
- if (supportsFK) {
- Collection<DbRelationship> rels = getDbEntity(map, "ARTIST").getRelationships();
- assertNotNull(rels);
- assertTrue(!rels.isEmpty());
-
- // test one-to-one
- rels = getDbEntity(map, "PAINTING").getRelationships();
- assertNotNull(rels);
-
- // find relationship to PAINTING_INFO
- DbRelationship oneToOne = null;
- for (DbRelationship rel : rels) {
- if ("PAINTING_INFO".equalsIgnoreCase(rel.getTargetEntityName())) {
- oneToOne = rel;
- break;
- }
- }
-
- assertNotNull("No relationship to PAINTING_INFO", oneToOne);
- assertFalse("Relationship to PAINTING_INFO must be to-one", oneToOne.isToMany());
- assertTrue("Relationship to PAINTING_INFO must be to-one", oneToOne.isToDependentPK());
-
- // test UNIQUE only if FK is supported...
- if (supportsUnique) {
- assertUniqueConstraintsInRelationships(map);
- }
- }
-
- // *** TESTING THIS ***
- loader.setCreatingMeaningfulPK(false);
- loader.loadObjEntities(map, CONFIG, entities);
-
- assertObjEntities(map);
-
- // now when the map is loaded, test
- // various things
- // selectively check how different types were processed
- if (accessStackAdapter.supportsColumnTypeReengineering()) {
- checkTypes(map);
- }
- }
-
- private void assertUniqueConstraintsInRelationships(DataMap map) {
- // unfortunately JDBC metadata doesn't provide info for UNIQUE
- // constraints....
- // cant reengineer them...
-
- // find rel to TO_ONEFK1
- /*
- * Iterator it = getDbEntity(map,
- * "TO_ONEFK2").getRelationships().iterator(); DbRelationship rel =
- * (DbRelationship) it.next(); assertEquals("TO_ONEFK1",
- * rel.getTargetEntityName());
- * assertFalse("UNIQUE constraint was ignored...", rel.isToMany());
- */
- }
-
- private void assertDbEntities(DataMap map) {
- DbEntity dae = getDbEntity(map, "ARTIST");
- assertNotNull("Null 'ARTIST' entity, other DbEntities: " + map.getDbEntityMap(), dae);
- assertEquals("ARTIST", dae.getName().toUpperCase());
-
- DbAttribute a = getDbAttribute(dae, "ARTIST_ID");
- assertNotNull(a);
- assertTrue(a.isPrimaryKey());
- assertFalse(a.isGenerated());
-
- if (adapter.supportsGeneratedKeys()) {
- DbEntity bag = getDbEntity(map, "GENERATED_COLUMN_TEST");
- DbAttribute id = getDbAttribute(bag, "GENERATED_COLUMN");
- assertTrue(id.isPrimaryKey());
- assertTrue(id.isGenerated());
- }
- }
-
- private void assertObjEntities(DataMap map) {
-
- boolean supportsLobs = accessStackAdapter.supportsLobs();
- boolean supportsFK = accessStackAdapter.supportsFKConstraints();
-
- ObjEntity ae = map.getObjEntity("Artist");
- assertNotNull(ae);
- assertEquals("Artist", ae.getName());
-
- // assert primary key is not an attribute
- assertNull(ae.getAttribute("artistId"));
-
- if (supportsLobs) {
- assertLobObjEntities(map);
- }
-
- if (supportsFK) {
- Collection<?> rels1 = ae.getRelationships();
- assertNotNull(rels1);
- assertTrue(rels1.size() > 0);
- }
-
- assertEquals("foo.x.Artist", ae.getClassName());
- }
-
- private void assertLobDbEntities(DataMap map) {
- DbEntity blobEnt = getDbEntity(map, "BLOB_TEST");
- assertNotNull(blobEnt);
- DbAttribute blobAttr = getDbAttribute(blobEnt, "BLOB_COL");
- assertNotNull(blobAttr);
- assertTrue(msgForTypeMismatch(Types.BLOB, blobAttr), Types.BLOB == blobAttr.getType()
- || Types.LONGVARBINARY == blobAttr.getType());
-
- DbEntity clobEnt = getDbEntity(map, "CLOB_TEST");
- assertNotNull(clobEnt);
- DbAttribute clobAttr = getDbAttribute(clobEnt, "CLOB_COL");
- assertNotNull(clobAttr);
- assertTrue(msgForTypeMismatch(Types.CLOB, clobAttr), Types.CLOB == clobAttr.getType()
- || Types.LONGVARCHAR == clobAttr.getType());
-
-/*
- DbEntity nclobEnt = getDbEntity(map, "NCLOB_TEST");
- assertNotNull(nclobEnt);
- DbAttribute nclobAttr = getDbAttribute(nclobEnt, "NCLOB_COL");
- assertNotNull(nclobAttr);
- assertTrue(msgForTypeMismatch(Types.NCLOB, nclobAttr), Types.NCLOB == nclobAttr.getType()
- || Types.LONGVARCHAR == nclobAttr.getType());
-*/
- }
-
- private void assertLobObjEntities(DataMap map) {
- ObjEntity blobEnt = map.getObjEntity("BlobTest");
- assertNotNull(blobEnt);
- // BLOBs should be mapped as byte[]
- ObjAttribute blobAttr = blobEnt.getAttribute("blobCol");
- assertNotNull("BlobTest.blobCol failed to doLoad", blobAttr);
- assertEquals("byte[]", blobAttr.getType());
-
-
- ObjEntity clobEnt = map.getObjEntity("ClobTest");
- assertNotNull(clobEnt);
- // CLOBs should be mapped as Strings by default
- ObjAttribute clobAttr = clobEnt.getAttribute("clobCol");
- assertNotNull(clobAttr);
- assertEquals(String.class.getName(), clobAttr.getType());
-
-
- ObjEntity nclobEnt = map.getObjEntity("NclobTest");
- assertNotNull(nclobEnt);
- // CLOBs should be mapped as Strings by default
- ObjAttribute nclobAttr = nclobEnt.getAttribute("nclobCol");
- assertNotNull(nclobAttr);
- assertEquals(String.class.getName(), nclobAttr.getType());
- }
-
- private DbEntity getDbEntity(DataMap map, String name) {
- DbEntity de = map.getDbEntity(name);
- // sometimes table names get converted to lowercase
- if (de == null) {
- de = map.getDbEntity(name.toLowerCase());
- }
-
- return de;
- }
-
- private DbAttribute getDbAttribute(DbEntity ent, String name) {
- DbAttribute da = ent.getAttribute(name);
- // sometimes table names get converted to lowercase
- if (da == null) {
- da = ent.getAttribute(name.toLowerCase());
- }
-
- return da;
- }
-
- private DataMap originalMap() {
- return runtime.getDataDomain().getDataNodes().iterator().next().getDataMaps().iterator().next();
- }
-
- /**
- * Selectively check how different types were processed.
- */
- public void checkTypes(DataMap map) {
- DbEntity dbe = getDbEntity(map, "PAINTING");
- DbEntity floatTest = getDbEntity(map, "FLOAT_TEST");
- DbEntity smallintTest = getDbEntity(map, "SMALLINT_TEST");
- DbAttribute integerAttr = getDbAttribute(dbe, "PAINTING_ID");
- DbAttribute decimalAttr = getDbAttribute(dbe, "ESTIMATED_PRICE");
- DbAttribute varcharAttr = getDbAttribute(dbe, "PAINTING_TITLE");
- DbAttribute floatAttr = getDbAttribute(floatTest, "FLOAT_COL");
- DbAttribute smallintAttr = getDbAttribute(smallintTest, "SMALLINT_COL");
-
- // check decimal
- assertTrue(msgForTypeMismatch(Types.DECIMAL, decimalAttr), Types.DECIMAL == decimalAttr.getType()
- || Types.NUMERIC == decimalAttr.getType());
- assertEquals(2, decimalAttr.getScale());
-
- // check varchar
- assertEquals(msgForTypeMismatch(Types.VARCHAR, varcharAttr), Types.VARCHAR, varcharAttr.getType());
- assertEquals(255, varcharAttr.getMaxLength());
- // check integer
- assertEquals(msgForTypeMismatch(Types.INTEGER, integerAttr), Types.INTEGER, integerAttr.getType());
- // check float
- assertTrue(msgForTypeMismatch(Types.FLOAT, floatAttr), Types.FLOAT == floatAttr.getType()
- || Types.DOUBLE == floatAttr.getType() || Types.REAL == floatAttr.getType());
-
- // check smallint
- assertTrue(msgForTypeMismatch(Types.SMALLINT, smallintAttr), Types.SMALLINT == smallintAttr.getType()
- || Types.INTEGER == smallintAttr.getType());
- }
-
- public void checkAllDBEntities(DataMap map) {
-
- for (DbEntity origEnt : originalMap().getDbEntities()) {
- DbEntity newEnt = map.getDbEntity(origEnt.getName());
- for (DbAttribute origAttr : origEnt.getAttributes()) {
- DbAttribute newAttr = newEnt.getAttribute(origAttr.getName());
- assertNotNull("No matching DbAttribute for '" + origAttr.getName(), newAttr);
- assertEquals(msgForTypeMismatch(origAttr, newAttr), origAttr.getType(), newAttr.getType());
- // length and precision doesn't have to be the same
- // it must be greater or equal
- assertTrue(origAttr.getMaxLength() <= newAttr.getMaxLength());
- assertTrue(origAttr.getScale() <= newAttr.getScale());
- }
- }
- }
-}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/cf172fc9/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/reverse/FiltersConfigBuilderTest.java
----------------------------------------------------------------------
diff --git a/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/reverse/FiltersConfigBuilderTest.java b/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/reverse/FiltersConfigBuilderTest.java
index 6d945a5..5b1a0e7 100644
--- a/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/reverse/FiltersConfigBuilderTest.java
+++ b/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/reverse/FiltersConfigBuilderTest.java
@@ -272,7 +272,7 @@ public class FiltersConfigBuilderTest {
/*@Test
public void testEmptyDbEntitiesFilters() throws Exception {
ReverseEngineering engineering = new ReverseEngineering();
- FiltersConfig executions = new FiltersConfigBuilder(engineering).filtersConfig();
+ FiltersConfig executions = new FiltersConfigBuilder(engineering).build();
assertEquals("If nothing was configured we have to import everything. Filter %/%/% true/true/true",
new FiltersConfig(eFilters(path(), TRUE, TRUE, NULL)),
@@ -283,7 +283,7 @@ public class FiltersConfigBuilderTest {
public void testOnlyOneCatalogDbEntitiesFilters() throws Exception {
ReverseEngineering engineering = new ReverseEngineering();
engineering.addCatalog(new Catalog("catalog_01"));
- FiltersConfig executions = new FiltersConfigBuilder(engineering).filtersConfig();
+ FiltersConfig executions = new FiltersConfigBuilder(engineering).build();
assertEquals(new FiltersConfig(eFilters(path("catalog_01", null), TRUE, TRUE, NULL)),
@@ -301,7 +301,7 @@ public class FiltersConfigBuilderTest {
engineering.addCatalog(new Catalog("catalog_03").schema(new Schema("schema_01")));
engineering.addCatalog(new Catalog("catalog_03").schema(new Schema("schema_01")));
engineering.addCatalog(new Catalog("catalog_03").schema(new Schema("schema_01")));
- FiltersConfig executions = new FiltersConfigBuilder(engineering).filtersConfig();
+ FiltersConfig executions = new FiltersConfigBuilder(engineering).build();
assertEquals(new FiltersConfig(
@@ -320,7 +320,7 @@ public class FiltersConfigBuilderTest {
engineering.addSchema(new Schema("schema_01"));
engineering.addSchema(new Schema("schema_02"));
engineering.addSchema(new Schema("schema_03"));
- FiltersConfig executions = new FiltersConfigBuilder(engineering).filtersConfig();
+ FiltersConfig executions = new FiltersConfigBuilder(engineering).build();
assertEquals(new FiltersConfig(
@@ -341,7 +341,7 @@ public class FiltersConfigBuilderTest {
engineering.addExcludeColumn(new ExcludeColumn("ExcludeColumn"));
engineering.addExcludeProcedure(new ExcludeProcedure("ExcludeProcedure"));
- FiltersConfig executions = new FiltersConfigBuilder(engineering).filtersConfig();
+ FiltersConfig executions = new FiltersConfigBuilder(engineering).build();
assertEquals(new FiltersConfig(
eFilters(path(),
@@ -367,7 +367,7 @@ public class FiltersConfigBuilderTest {
ReverseEngineering engineering = new ReverseEngineering();
engineering.addCatalog(catalog);
- FiltersConfig executions = new FiltersConfigBuilder(engineering).filtersConfig();
+ FiltersConfig executions = new FiltersConfigBuilder(engineering).build();
assertEquals(new FiltersConfig(
eFilters(path("catalog", "schema"), include("table"), NULL, NULL),
@@ -385,7 +385,7 @@ public class FiltersConfigBuilderTest {
builder.add(new EntityFilters(path, NULL, NULL, NULL));
builder.add(new EntityFilters(path, NULL, NULL, NULL));
- EntityFilters filter = builder.filtersConfig().filter(path);
+ EntityFilters filter = builder.build().filter(path);
assertFalse(filter.isEmpty());
}*/
}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/cayenne/blob/cf172fc9/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/reverse/ManyToManyCandidateEntityTest.java
----------------------------------------------------------------------
diff --git a/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/reverse/ManyToManyCandidateEntityTest.java b/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/reverse/ManyToManyCandidateEntityTest.java
deleted file mode 100644
index edecdfc..0000000
--- a/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/reverse/ManyToManyCandidateEntityTest.java
+++ /dev/null
@@ -1,113 +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;
-
-import org.apache.cayenne.configuration.ConfigurationNameMapper;
-import org.apache.cayenne.configuration.ConfigurationTree;
-import org.apache.cayenne.configuration.DataChannelDescriptor;
-import org.apache.cayenne.configuration.DataMapLoader;
-import org.apache.cayenne.configuration.DefaultConfigurationNameMapper;
-import org.apache.cayenne.configuration.XMLDataChannelDescriptorLoader;
-import org.apache.cayenne.configuration.XMLDataMapLoader;
-import org.apache.cayenne.di.AdhocObjectFactory;
-import org.apache.cayenne.di.Binder;
-import org.apache.cayenne.di.ClassLoaderManager;
-import org.apache.cayenne.di.DIBootstrap;
-import org.apache.cayenne.di.Injector;
-import org.apache.cayenne.di.Module;
-import org.apache.cayenne.di.spi.DefaultAdhocObjectFactory;
-import org.apache.cayenne.di.spi.DefaultClassLoaderManager;
-import org.apache.cayenne.map.DataMap;
-import org.apache.cayenne.map.ObjEntity;
-import org.apache.cayenne.map.Relationship;
-import org.apache.cayenne.map.naming.LegacyNameGenerator;
-import org.apache.cayenne.resource.URLResource;
-import org.junit.Before;
-import org.junit.Test;
-
-import java.net.URL;
-import java.util.ArrayList;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-
-public class ManyToManyCandidateEntityTest {
-
- private DataMap map;
-
- @Before
- public void setUp() throws Exception {
- Module testModule = new Module() {
-
- public void configure(Binder binder) {
- binder.bind(ClassLoaderManager.class).to(DefaultClassLoaderManager.class);
- binder.bind(AdhocObjectFactory.class).to(DefaultAdhocObjectFactory.class);
- binder.bind(DataMapLoader.class).to(XMLDataMapLoader.class);
- binder.bind(ConfigurationNameMapper.class).to(DefaultConfigurationNameMapper.class);
- }
- };
-
- Injector injector = DIBootstrap.createInjector(testModule);
-
- // create and initialize loader instance to test
- XMLDataChannelDescriptorLoader loader = new XMLDataChannelDescriptorLoader();
- injector.injectMembers(loader);
-
- String testConfigName = "relationship-optimisation";
- URL url = getClass().getResource("cayenne-" + testConfigName + ".xml");
-
- ConfigurationTree<DataChannelDescriptor> tree = loader.load(new URLResource(url));
-
- map = tree.getRootNode().getDataMap(testConfigName);
- }
-
- @Test
- public void testMatchingForManyToManyEntity() throws Exception {
- ObjEntity manyToManyEntity = map.getObjEntity("Table1Table2");
-
- assertNotNull(ManyToManyCandidateEntity.build(manyToManyEntity));
- }
-
- @Test
- public void testMatchingForNotManyToManyEntity() throws Exception {
- ObjEntity entity = map.getObjEntity("Table1");
-
- assertNull(ManyToManyCandidateEntity.build(entity));
- }
-
- @Test
- public void testOptimisationForManyToManyEntity() {
- ObjEntity manyToManyEntity = map.getObjEntity("Table1Table2");
-
- ManyToManyCandidateEntity.build(manyToManyEntity).optimizeRelationships(new LegacyNameGenerator());
-
- ObjEntity table1Entity = map.getObjEntity("Table1");
- ObjEntity table2Entity = map.getObjEntity("Table2");
-
- assertEquals(1, table1Entity.getRelationships().size());
- assertEquals(table2Entity, new ArrayList<Relationship>(table1Entity.getRelationships()).get(0)
- .getTargetEntity());
-
- assertEquals(1, table2Entity.getRelationships().size());
- assertEquals(table1Entity, new ArrayList<Relationship>(table2Entity.getRelationships()).get(0)
- .getTargetEntity());
- }
-
-}