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/10/03 12:29:44 UTC

[2/2] cayenne git commit: CAY-2115 DbLoader - allow loading DataMap without Obj layer

CAY-2115 DbLoader - allow loading DataMap without Obj layer

* refactoring EntityMergeSupport


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

Branch: refs/heads/master
Commit: ac04c11b09f6f314765e5848c864390175548745
Parents: e8e2f73
Author: Andrus Adamchik <an...@objectstyle.com>
Authored: Mon Oct 3 14:04:40 2016 +0300
Committer: Andrus Adamchik <an...@objectstyle.com>
Committed: Mon Oct 3 15:25:50 2016 +0300

----------------------------------------------------------------------
 .../apache/cayenne/dbsync/merge/DbMerger.java   | 704 ++++++++++---------
 .../dbsync/merge/EntityMergeSupport.java        | 117 ++-
 .../cayenne/dbsync/merge/MergerContext.java     |   5 +-
 .../cayenne/dbsync/reverse/db/DbLoader.java     |  88 +--
 .../dbsync/merge/EntityMergeSupportIT.java      |   2 +-
 .../cayenne/dbsync/reverse/db/DbLoaderIT.java   |  47 +-
 .../tools/dbimport/DbImportConfiguration.java   |  21 +-
 .../tools/dbimport/DefaultDbImportAction.java   |   1 +
 .../dbimport/DefaultDbImportActionTest.java     | 251 +++----
 .../modeler/action/CreateObjEntityAction.java   |  35 +-
 .../modeler/action/DbEntitySyncAction.java      |   2 +-
 .../cayenne/modeler/action/MigrateAction.java   |   6 +-
 .../modeler/dialog/db/DbLoaderHelper.java       |  20 +-
 .../modeler/dialog/db/ModelerDbLoader.java      |   7 +-
 .../dialog/objentity/EntitySyncController.java  |  51 +-
 15 files changed, 668 insertions(+), 689 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cayenne/blob/ac04c11b/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 41ff7c8..f89dd00 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
@@ -22,6 +22,7 @@ import org.apache.cayenne.CayenneRuntimeException;
 import org.apache.cayenne.access.DataNode;
 import org.apache.cayenne.dba.DbAdapter;
 import org.apache.cayenne.dbsync.merge.factory.MergerTokenFactory;
+import org.apache.cayenne.dbsync.naming.DefaultObjectNameGenerator;
 import org.apache.cayenne.dbsync.reverse.db.DbLoader;
 import org.apache.cayenne.dbsync.reverse.db.DbLoaderConfiguration;
 import org.apache.cayenne.dbsync.reverse.db.LoggingDbLoaderDelegate;
@@ -52,354 +53,363 @@ import java.util.Set;
  * Traverse a {@link DataNode} and a {@link DataMap} and create a group of
  * {@link MergerToken}s to alter the {@link DataNode} data store to match the
  * {@link DataMap}.
- * 
  */
 public class DbMerger {
 
-	private static final Log LOGGER = LogFactory.getLog(DbMerger.class);
-
-	private final MergerTokenFactory factory;
-
-	private final ValueForNullProvider valueForNull;
-
-	public DbMerger(MergerTokenFactory factory) {
-		this(factory, null);
-	}
-
-	public DbMerger(MergerTokenFactory factory, ValueForNullProvider valueForNull) {
-		this.factory = factory;
-		this.valueForNull = valueForNull == null ? new EmptyValueForNullProvider() : valueForNull;
-	}
-
-	/**
-	 * Create and return a {@link List} of {@link MergerToken}s to alter the
-	 * given {@link DataNode} to match the given {@link DataMap}
-	 */
-	public List<MergerToken> createMergeTokens(DataSource dataSource, DbAdapter adapter, DataMap existingDataMap,
-			DbLoaderConfiguration config) {
-		return createMergeTokens(existingDataMap, loadDataMapFromDb(dataSource, adapter, config), config);
-	}
-
-	/**
-	 * Create and return a {@link List} of {@link MergerToken}s to alter the
-	 * given {@link DataNode} to match the given {@link DataMap}
-	 */
-	public List<MergerToken> createMergeTokens(DataMap existing, DataMap loadedFomDb, DbLoaderConfiguration config) {
-
-		loadedFomDb.setQuotingSQLIdentifiers(existing.isQuotingSQLIdentifiers());
-
-		List<MergerToken> tokens = createMergeTokens(filter(existing, config.getFiltersConfig()),
-				loadedFomDb.getDbEntities(), config);
-
-		// sort. use a custom Comparator since only toDb tokens are comparable
-		// by now
-		Collections.sort(tokens, new Comparator<MergerToken>() {
-
-			public int compare(MergerToken o1, MergerToken o2) {
-				if (o1 instanceof AbstractToDbToken && o2 instanceof AbstractToDbToken) {
-
-					return ((AbstractToDbToken) o1).compareTo(o2);
-				}
-				return 0;
-			}
-		});
-
-		return tokens;
-	}
-
-	private Collection<DbEntity> filter(DataMap existing, FiltersConfig filtersConfig) {
-		Collection<DbEntity> existingFiltered = new LinkedList<DbEntity>();
-		for (DbEntity entity : existing.getDbEntities()) {
-			if (filtersConfig.tableFilter(entity.getCatalog(), entity.getSchema()).isIncludeTable(entity.getName()) != null) {
-				existingFiltered.add(entity);
-			}
-		}
-		return existingFiltered;
-	}
-
-	private DataMap loadDataMapFromDb(DataSource dataSource, DbAdapter adapter, DbLoaderConfiguration config) {
-		try (Connection conn = dataSource.getConnection();) {
-
-			return new DbLoader(conn, adapter, new LoggingDbLoaderDelegate(LOGGER)).load(config);
-		} catch (SQLException e) {
-			throw new CayenneRuntimeException("Can't doLoad dataMap from db.", e);
-		}
-	}
-
-	public List<MergerToken> createMergeTokens(Collection<DbEntity> existing, Collection<DbEntity> loadedFromDb,
-			DbLoaderConfiguration config) {
-		Collection<DbEntity> dbEntitiesToDrop = new LinkedList<DbEntity>(loadedFromDb);
-
-		List<MergerToken> tokens = new LinkedList<MergerToken>();
-		for (DbEntity dbEntity : existing) {
-			String tableName = dbEntity.getName();
-
-			// look for table
-			DbEntity detectedEntity = findDbEntity(loadedFromDb, tableName);
-			if (detectedEntity == null) {
-				tokens.add(factory.createCreateTableToDb(dbEntity));
-				// TODO: does this work properly with createReverse?
-				for (DbRelationship rel : dbEntity.getRelationships()) {
-					tokens.add(factory.createAddRelationshipToDb(dbEntity, rel));
-				}
-				continue;
-			}
-
-			dbEntitiesToDrop.remove(detectedEntity);
-
-			tokens.addAll(checkRelationshipsToDrop(dbEntity, detectedEntity));
-			if (!config.isSkipRelationshipsLoading()) {
-				tokens.addAll(checkRelationshipsToAdd(dbEntity, detectedEntity));
-			}
-			tokens.addAll(checkRows(dbEntity, detectedEntity));
-
-			if (!config.isSkipPrimaryKeyLoading()) {
-				MergerToken token = checkPrimaryKeyChange(dbEntity, detectedEntity);
-				if (token != null) {
-					tokens.add(token);
-				}
-			}
-		}
-
-		// drop table
-		// TODO: support drop table. currently, too many tables are marked for
-		// drop
-		for (DbEntity e : dbEntitiesToDrop) {
-			tokens.add(factory.createDropTableToDb(e));
-			for (DbRelationship relationship : e.getRelationships()) {
-				DbEntity detectedEntity = findDbEntity(existing, relationship.getTargetEntityName());
-				if (detectedEntity != null) {
-					tokens.add(factory.createDropRelationshipToDb(detectedEntity, relationship.getReverseRelationship()));
-				}
-			}
-		}
-
-		return tokens;
-	}
-
-	private List<MergerToken> checkRows(DbEntity existing, DbEntity loadedFromDb) {
-		List<MergerToken> tokens = new LinkedList<MergerToken>();
-
-		// columns to drop
-		for (DbAttribute detected : loadedFromDb.getAttributes()) {
-			if (findDbAttribute(existing, detected.getName()) == null) {
-				tokens.add(factory.createDropColumnToDb(existing, detected));
-			}
-		}
-
-		// columns to add or modify
-		for (DbAttribute attr : existing.getAttributes()) {
-			String columnName = attr.getName().toUpperCase();
-
-			DbAttribute detected = findDbAttribute(loadedFromDb, columnName);
-
-			if (detected == null) {
-				tokens.add(factory.createAddColumnToDb(existing, attr));
-				if (attr.isMandatory()) {
-					if (valueForNull.hasValueFor(existing, attr)) {
-						tokens.add(factory.createSetValueForNullToDb(existing, attr, valueForNull));
-					}
-					tokens.add(factory.createSetNotNullToDb(existing, attr));
-				}
-				continue;
-			}
-
-			// check for not null
-			if (attr.isMandatory() != detected.isMandatory()) {
-				if (attr.isMandatory()) {
-					if (valueForNull.hasValueFor(existing, attr)) {
-						tokens.add(factory.createSetValueForNullToDb(existing, attr, valueForNull));
-					}
-					tokens.add(factory.createSetNotNullToDb(existing, attr));
-				} else {
-					tokens.add(factory.createSetAllowNullToDb(existing, attr));
-				}
-			}
-
-			// TODO: check more types than char/varchar
-			// TODO: psql report VARCHAR for text column, not clob
-			switch (detected.getType()) {
-			case Types.VARCHAR:
-			case Types.CHAR:
-				if (attr.getMaxLength() != detected.getMaxLength()) {
-					tokens.add(factory.createSetColumnTypeToDb(existing, detected, attr));
-				}
-				break;
-			}
-		}
-
-		return tokens;
-	}
-
-	private List<MergerToken> checkRelationshipsToDrop(DbEntity dbEntity, DbEntity detectedEntity) {
-		List<MergerToken> tokens = new LinkedList<MergerToken>();
-
-		// relationships to drop
-		for (DbRelationship detected : detectedEntity.getRelationships()) {
-			if (findDbRelationship(dbEntity, detected) == null) {
-
-				// alter detected relationship to match entity and attribute
-				// names.
-				// (case sensitively)
-
-				DbEntity targetEntity = findDbEntity(dbEntity.getDataMap().getDbEntities(),
-						detected.getTargetEntityName());
-				if (targetEntity == null) {
-					continue;
-				}
-
-				detected.setSourceEntity(dbEntity);
-				detected.setTargetEntityName(targetEntity);
-
-				// manipulate the joins to match the DbAttributes in the model
-				for (DbJoin join : detected.getJoins()) {
-					DbAttribute sattr = findDbAttribute(dbEntity, join.getSourceName());
-					if (sattr != null) {
-						join.setSourceName(sattr.getName());
-					}
-					DbAttribute tattr = findDbAttribute(targetEntity, join.getTargetName());
-					if (tattr != null) {
-						join.setTargetName(tattr.getName());
-					}
-				}
-
-				MergerToken token = factory.createDropRelationshipToDb(dbEntity, detected);
-				if (detected.isToMany()) {
-					// default toModel as we can not do drop a toMany in the db.
-					// only
-					// toOne are represented using foreign key
-					token = token.createReverse(factory);
-				}
-				tokens.add(token);
-			}
-		}
-
-		return tokens;
-	}
-
-	private List<MergerToken> checkRelationshipsToAdd(DbEntity dbEntity, DbEntity detectedEntity) {
-
-		List<MergerToken> tokens = new LinkedList<MergerToken>();
-
-		for (DbRelationship rel : dbEntity.getRelationships()) {
-			if (findDbRelationship(detectedEntity, rel) == null) {
-				AddRelationshipToDb token = (AddRelationshipToDb) factory.createAddRelationshipToDb(dbEntity, rel);
-
-				if (token.shouldGenerateFkConstraint()) {
-					// TODO I guess we should add relationship always; in order
-					// to have ability
-					// TODO generate reverse relationship. If it doesn't have
-					// anything to execute it will be passed
-					// TODO through execution without any affect on db
-					tokens.add(token);
-				}
-			}
-		}
-
-		return tokens;
-	}
-
-	private MergerToken checkPrimaryKeyChange(DbEntity dbEntity, DbEntity detectedEntity) {
-		Collection<DbAttribute> primaryKeyOriginal = detectedEntity.getPrimaryKeys();
-		Collection<DbAttribute> primaryKeyNew = dbEntity.getPrimaryKeys();
-
-		String primaryKeyName = null;
-		if (detectedEntity instanceof DetectedDbEntity) {
-			primaryKeyName = ((DetectedDbEntity) detectedEntity).getPrimaryKeyName();
-		}
-
-		if (upperCaseEntityNames(primaryKeyOriginal).equals(upperCaseEntityNames(primaryKeyNew))) {
-			return null;
-		}
-
-		return factory.createSetPrimaryKeyToDb(dbEntity, primaryKeyOriginal, primaryKeyNew, primaryKeyName);
-	}
-
-	private Set<String> upperCaseEntityNames(Collection<? extends Attribute> attrs) {
-		Set<String> names = new HashSet<String>();
-		for (Attribute attr : attrs) {
-			names.add(attr.getName().toUpperCase());
-		}
-		return names;
-	}
-
-	/**
-	 * case insensitive search for a {@link DbEntity} in a {@link DataMap} by
-	 * name
-	 */
-	private DbEntity findDbEntity(Collection<DbEntity> dbEntities, String caseInsensitiveName) {
-		// TODO: create a Map with upper case keys?
-		for (DbEntity e : dbEntities) {
-			if (e.getName().equalsIgnoreCase(caseInsensitiveName)) {
-				return e;
-			}
-		}
-		return null;
-	}
-
-	/**
-	 * case insensitive search for a {@link DbAttribute} in a {@link DbEntity}
-	 * by name
-	 */
-	private DbAttribute findDbAttribute(DbEntity entity, String caseInsensitiveName) {
-		for (DbAttribute a : entity.getAttributes()) {
-			if (a.getName().equalsIgnoreCase(caseInsensitiveName)) {
-				return a;
-			}
-		}
-		return null;
-	}
-
-	/**
-	 * search for a {@link DbRelationship} like rel in the given
-	 * {@link DbEntity}
-	 */
-	private DbRelationship findDbRelationship(DbEntity entity, DbRelationship rel) {
-		for (DbRelationship candidate : entity.getRelationships()) {
-			if (equalDbJoinCollections(candidate.getJoins(), rel.getJoins())) {
-				return candidate;
-			}
-		}
-		return null;
-	}
-
-	/**
-	 * Return true if the two unordered {@link Collection}s of {@link DbJoin}s
-	 * are equal. Entity and Attribute names are compared case insensitively.
-	 *
-	 * TODO complexity n^2; sort both collection and go through them to compare
-	 * = 2*n*log(n) + n
-	 */
-	private static boolean equalDbJoinCollections(Collection<DbJoin> j1s, Collection<DbJoin> j2s) {
-		if (j1s.size() != j2s.size()) {
-			return false;
-		}
-
-		for (DbJoin j1 : j1s) {
-			if (!havePair(j2s, j1)) {
-				return false;
-			}
-		}
-
-		return true;
-	}
-
-	private static boolean havePair(Collection<DbJoin> j2s, DbJoin j1) {
-		for (DbJoin j2 : j2s) {
-			if (!isNull(j1.getSource()) && !isNull(j1.getTarget()) && !isNull(j2.getSource())
-					&& !isNull(j2.getTarget())
-					&& j1.getSource().getEntity().getName().equalsIgnoreCase(j2.getSource().getEntity().getName())
-					&& j1.getTarget().getEntity().getName().equalsIgnoreCase(j2.getTarget().getEntity().getName())
-					&& j1.getSourceName().equalsIgnoreCase(j2.getSourceName())
-					&& j1.getTargetName().equalsIgnoreCase(j2.getTargetName())) {
-
-				return true;
-			}
-		}
-		return false;
-	}
-
-	private static boolean isNull(DbAttribute attribute) {
-		return attribute == null || attribute.getEntity() == null;
-	}
+    private static final Log LOGGER = LogFactory.getLog(DbMerger.class);
+
+    private final MergerTokenFactory factory;
+
+    private final ValueForNullProvider valueForNull;
+
+    public DbMerger(MergerTokenFactory factory) {
+        this(factory, null);
+    }
+
+    public DbMerger(MergerTokenFactory factory, ValueForNullProvider valueForNull) {
+        this.factory = factory;
+        this.valueForNull = valueForNull == null ? new EmptyValueForNullProvider() : valueForNull;
+    }
+
+    /**
+     * Return true if the two unordered {@link Collection}s of {@link DbJoin}s
+     * are equal. Entity and Attribute names are compared case insensitively.
+     * <p>
+     * TODO complexity n^2; sort both collection and go through them to compare
+     * = 2*n*log(n) + n
+     */
+    private static boolean equalDbJoinCollections(Collection<DbJoin> j1s, Collection<DbJoin> j2s) {
+        if (j1s.size() != j2s.size()) {
+            return false;
+        }
+
+        for (DbJoin j1 : j1s) {
+            if (!havePair(j2s, j1)) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    private static boolean havePair(Collection<DbJoin> j2s, DbJoin j1) {
+        for (DbJoin j2 : j2s) {
+            if (!isNull(j1.getSource()) && !isNull(j1.getTarget()) && !isNull(j2.getSource())
+                    && !isNull(j2.getTarget())
+                    && j1.getSource().getEntity().getName().equalsIgnoreCase(j2.getSource().getEntity().getName())
+                    && j1.getTarget().getEntity().getName().equalsIgnoreCase(j2.getTarget().getEntity().getName())
+                    && j1.getSourceName().equalsIgnoreCase(j2.getSourceName())
+                    && j1.getTargetName().equalsIgnoreCase(j2.getTargetName())) {
+
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private static boolean isNull(DbAttribute attribute) {
+        return attribute == null || attribute.getEntity() == null;
+    }
+
+    /**
+     * Create and return a {@link List} of {@link MergerToken}s to alter the
+     * given {@link DataNode} to match the given {@link DataMap}
+     */
+    public List<MergerToken> createMergeTokens(DataSource dataSource, DbAdapter adapter, DataMap existingDataMap,
+                                               DbLoaderConfiguration config) {
+        return createMergeTokens(existingDataMap, loadDataMapFromDb(dataSource, adapter, config), config);
+    }
+
+    /**
+     * Create and return a {@link List} of {@link MergerToken}s to alter the
+     * given {@link DataNode} to match the given {@link DataMap}
+     */
+    public List<MergerToken> createMergeTokens(DataMap existing, DataMap loadedFomDb, DbLoaderConfiguration config) {
+
+        loadedFomDb.setQuotingSQLIdentifiers(existing.isQuotingSQLIdentifiers());
+
+        List<MergerToken> tokens = createMergeTokens(filter(existing, config.getFiltersConfig()),
+                loadedFomDb.getDbEntities(), config);
+
+        // sort. use a custom Comparator since only toDb tokens are comparable
+        // by now
+        Collections.sort(tokens, new Comparator<MergerToken>() {
+
+            public int compare(MergerToken o1, MergerToken o2) {
+                if (o1 instanceof AbstractToDbToken && o2 instanceof AbstractToDbToken) {
+
+                    return ((AbstractToDbToken) o1).compareTo(o2);
+                }
+                return 0;
+            }
+        });
+
+        return tokens;
+    }
+
+    private Collection<DbEntity> filter(DataMap existing, FiltersConfig filtersConfig) {
+        Collection<DbEntity> existingFiltered = new LinkedList<DbEntity>();
+        for (DbEntity entity : existing.getDbEntities()) {
+            if (filtersConfig.tableFilter(entity.getCatalog(), entity.getSchema()).isIncludeTable(entity.getName()) != null) {
+                existingFiltered.add(entity);
+            }
+        }
+        return existingFiltered;
+    }
+
+    protected EntityMergeSupport createEntityMergeSupport() {
+        return new EntityMergeSupport(new DefaultObjectNameGenerator(), true, true);
+    }
+
+    private DataMap loadDataMapFromDb(DataSource dataSource, DbAdapter adapter, DbLoaderConfiguration config) {
+
+
+        try (Connection conn = dataSource.getConnection();) {
+
+            return new DbLoader(conn,
+                    adapter,
+                    new LoggingDbLoaderDelegate(LOGGER),
+                    createEntityMergeSupport()).load(config);
+
+        } catch (SQLException e) {
+            throw new CayenneRuntimeException("Can't doLoad dataMap from db.", e);
+        }
+    }
+
+    public List<MergerToken> createMergeTokens(Collection<DbEntity> existing, Collection<DbEntity> loadedFromDb,
+                                               DbLoaderConfiguration config) {
+        Collection<DbEntity> dbEntitiesToDrop = new LinkedList<DbEntity>(loadedFromDb);
+
+        List<MergerToken> tokens = new LinkedList<MergerToken>();
+        for (DbEntity dbEntity : existing) {
+            String tableName = dbEntity.getName();
+
+            // look for table
+            DbEntity detectedEntity = findDbEntity(loadedFromDb, tableName);
+            if (detectedEntity == null) {
+                tokens.add(factory.createCreateTableToDb(dbEntity));
+                // TODO: does this work properly with createReverse?
+                for (DbRelationship rel : dbEntity.getRelationships()) {
+                    tokens.add(factory.createAddRelationshipToDb(dbEntity, rel));
+                }
+                continue;
+            }
+
+            dbEntitiesToDrop.remove(detectedEntity);
+
+            tokens.addAll(checkRelationshipsToDrop(dbEntity, detectedEntity));
+            if (!config.isSkipRelationshipsLoading()) {
+                tokens.addAll(checkRelationshipsToAdd(dbEntity, detectedEntity));
+            }
+            tokens.addAll(checkRows(dbEntity, detectedEntity));
+
+            if (!config.isSkipPrimaryKeyLoading()) {
+                MergerToken token = checkPrimaryKeyChange(dbEntity, detectedEntity);
+                if (token != null) {
+                    tokens.add(token);
+                }
+            }
+        }
+
+        // drop table
+        // TODO: support drop table. currently, too many tables are marked for
+        // drop
+        for (DbEntity e : dbEntitiesToDrop) {
+            tokens.add(factory.createDropTableToDb(e));
+            for (DbRelationship relationship : e.getRelationships()) {
+                DbEntity detectedEntity = findDbEntity(existing, relationship.getTargetEntityName());
+                if (detectedEntity != null) {
+                    tokens.add(factory.createDropRelationshipToDb(detectedEntity, relationship.getReverseRelationship()));
+                }
+            }
+        }
+
+        return tokens;
+    }
+
+    private List<MergerToken> checkRows(DbEntity existing, DbEntity loadedFromDb) {
+        List<MergerToken> tokens = new LinkedList<MergerToken>();
+
+        // columns to drop
+        for (DbAttribute detected : loadedFromDb.getAttributes()) {
+            if (findDbAttribute(existing, detected.getName()) == null) {
+                tokens.add(factory.createDropColumnToDb(existing, detected));
+            }
+        }
+
+        // columns to add or modify
+        for (DbAttribute attr : existing.getAttributes()) {
+            String columnName = attr.getName().toUpperCase();
+
+            DbAttribute detected = findDbAttribute(loadedFromDb, columnName);
+
+            if (detected == null) {
+                tokens.add(factory.createAddColumnToDb(existing, attr));
+                if (attr.isMandatory()) {
+                    if (valueForNull.hasValueFor(existing, attr)) {
+                        tokens.add(factory.createSetValueForNullToDb(existing, attr, valueForNull));
+                    }
+                    tokens.add(factory.createSetNotNullToDb(existing, attr));
+                }
+                continue;
+            }
+
+            // check for not null
+            if (attr.isMandatory() != detected.isMandatory()) {
+                if (attr.isMandatory()) {
+                    if (valueForNull.hasValueFor(existing, attr)) {
+                        tokens.add(factory.createSetValueForNullToDb(existing, attr, valueForNull));
+                    }
+                    tokens.add(factory.createSetNotNullToDb(existing, attr));
+                } else {
+                    tokens.add(factory.createSetAllowNullToDb(existing, attr));
+                }
+            }
+
+            // TODO: check more types than char/varchar
+            // TODO: psql report VARCHAR for text column, not clob
+            switch (detected.getType()) {
+                case Types.VARCHAR:
+                case Types.CHAR:
+                    if (attr.getMaxLength() != detected.getMaxLength()) {
+                        tokens.add(factory.createSetColumnTypeToDb(existing, detected, attr));
+                    }
+                    break;
+            }
+        }
+
+        return tokens;
+    }
+
+    private List<MergerToken> checkRelationshipsToDrop(DbEntity dbEntity, DbEntity detectedEntity) {
+        List<MergerToken> tokens = new LinkedList<MergerToken>();
+
+        // relationships to drop
+        for (DbRelationship detected : detectedEntity.getRelationships()) {
+            if (findDbRelationship(dbEntity, detected) == null) {
+
+                // alter detected relationship to match entity and attribute
+                // names.
+                // (case sensitively)
+
+                DbEntity targetEntity = findDbEntity(dbEntity.getDataMap().getDbEntities(),
+                        detected.getTargetEntityName());
+                if (targetEntity == null) {
+                    continue;
+                }
+
+                detected.setSourceEntity(dbEntity);
+                detected.setTargetEntityName(targetEntity);
+
+                // manipulate the joins to match the DbAttributes in the model
+                for (DbJoin join : detected.getJoins()) {
+                    DbAttribute sattr = findDbAttribute(dbEntity, join.getSourceName());
+                    if (sattr != null) {
+                        join.setSourceName(sattr.getName());
+                    }
+                    DbAttribute tattr = findDbAttribute(targetEntity, join.getTargetName());
+                    if (tattr != null) {
+                        join.setTargetName(tattr.getName());
+                    }
+                }
+
+                MergerToken token = factory.createDropRelationshipToDb(dbEntity, detected);
+                if (detected.isToMany()) {
+                    // default toModel as we can not do drop a toMany in the db.
+                    // only
+                    // toOne are represented using foreign key
+                    token = token.createReverse(factory);
+                }
+                tokens.add(token);
+            }
+        }
+
+        return tokens;
+    }
+
+    private List<MergerToken> checkRelationshipsToAdd(DbEntity dbEntity, DbEntity detectedEntity) {
+
+        List<MergerToken> tokens = new LinkedList<MergerToken>();
+
+        for (DbRelationship rel : dbEntity.getRelationships()) {
+            if (findDbRelationship(detectedEntity, rel) == null) {
+                AddRelationshipToDb token = (AddRelationshipToDb) factory.createAddRelationshipToDb(dbEntity, rel);
+
+                if (token.shouldGenerateFkConstraint()) {
+                    // TODO I guess we should add relationship always; in order
+                    // to have ability
+                    // TODO generate reverse relationship. If it doesn't have
+                    // anything to execute it will be passed
+                    // TODO through execution without any affect on db
+                    tokens.add(token);
+                }
+            }
+        }
+
+        return tokens;
+    }
+
+    private MergerToken checkPrimaryKeyChange(DbEntity dbEntity, DbEntity detectedEntity) {
+        Collection<DbAttribute> primaryKeyOriginal = detectedEntity.getPrimaryKeys();
+        Collection<DbAttribute> primaryKeyNew = dbEntity.getPrimaryKeys();
+
+        String primaryKeyName = null;
+        if (detectedEntity instanceof DetectedDbEntity) {
+            primaryKeyName = ((DetectedDbEntity) detectedEntity).getPrimaryKeyName();
+        }
+
+        if (upperCaseEntityNames(primaryKeyOriginal).equals(upperCaseEntityNames(primaryKeyNew))) {
+            return null;
+        }
+
+        return factory.createSetPrimaryKeyToDb(dbEntity, primaryKeyOriginal, primaryKeyNew, primaryKeyName);
+    }
+
+    private Set<String> upperCaseEntityNames(Collection<? extends Attribute> attrs) {
+        Set<String> names = new HashSet<String>();
+        for (Attribute attr : attrs) {
+            names.add(attr.getName().toUpperCase());
+        }
+        return names;
+    }
+
+    /**
+     * case insensitive search for a {@link DbEntity} in a {@link DataMap} by
+     * name
+     */
+    private DbEntity findDbEntity(Collection<DbEntity> dbEntities, String caseInsensitiveName) {
+        // TODO: create a Map with upper case keys?
+        for (DbEntity e : dbEntities) {
+            if (e.getName().equalsIgnoreCase(caseInsensitiveName)) {
+                return e;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * case insensitive search for a {@link DbAttribute} in a {@link DbEntity}
+     * by name
+     */
+    private DbAttribute findDbAttribute(DbEntity entity, String caseInsensitiveName) {
+        for (DbAttribute a : entity.getAttributes()) {
+            if (a.getName().equalsIgnoreCase(caseInsensitiveName)) {
+                return a;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * search for a {@link DbRelationship} like rel in the given
+     * {@link DbEntity}
+     */
+    private DbRelationship findDbRelationship(DbEntity entity, DbRelationship rel) {
+        for (DbRelationship candidate : entity.getRelationships()) {
+            if (equalDbJoinCollections(candidate.getJoins(), rel.getJoins())) {
+                return candidate;
+            }
+        }
+        return null;
+    }
 }

http://git-wip-us.apache.org/repos/asf/cayenne/blob/ac04c11b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/EntityMergeSupport.java
----------------------------------------------------------------------
diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/EntityMergeSupport.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/EntityMergeSupport.java
index 11f2ac4..bf54dcc 100644
--- a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/EntityMergeSupport.java
+++ b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/EntityMergeSupport.java
@@ -48,7 +48,7 @@ import java.util.Map;
  */
 public class EntityMergeSupport {
 
-    private static final Log LOG = LogFactory.getLog(EntityMergeSupport.class);
+    private static final Log LOGGER = LogFactory.getLog(EntityMergeSupport.class);
 
     private static final Map<String, String> CLASS_TO_PRIMITIVE;
 
@@ -63,41 +63,36 @@ public class EntityMergeSupport {
         CLASS_TO_PRIMITIVE.put(Integer.class.getName(), "int");
     }
 
-    private final DataMap map;
-    /**
-     * Strategy for choosing names for entities, attributes and relationships
-     */
     private final ObjectNameGenerator nameGenerator;
-    /**
-     * Listeners of merge process.
-     */
-    private final List<EntityMergeListener> listeners = new ArrayList<EntityMergeListener>();
-    protected boolean removeMeaningfulFKs;
-    protected boolean removeMeaningfulPKs;
-    protected boolean usePrimitives;
-    
-    /**
-     * @since 3.0
-     */
-    public EntityMergeSupport(DataMap map, ObjectNameGenerator nameGenerator, boolean removeMeaningfulPKs) {
-        this.map = map;
+    private final List<EntityMergeListener> listeners;
+    protected boolean removingMeaningfulFKs;
+    protected boolean removingMeaningfulPKs;
+    protected boolean usingPrimitives;
+
+    public EntityMergeSupport(ObjectNameGenerator nameGenerator, boolean removingMeaningfulPKs, boolean removingMeaningfulFKs) {
+        this.listeners = new ArrayList<>();
         this.nameGenerator = nameGenerator;
-        this.removeMeaningfulFKs = true;
-        this.removeMeaningfulPKs = removeMeaningfulPKs;
+        this.removingMeaningfulFKs = removingMeaningfulFKs;
+        this.removingMeaningfulPKs = removingMeaningfulPKs;
 
-        /**
-         * Adding a listener, so that all created ObjRelationships would have
-         * default delete rule
-         */
+        // will ensure that all created ObjRelationships would have
+        // default delete rule
         addEntityMergeListener(DeleteRuleUpdater.getEntityMergeListener());
     }
 
+    public boolean isRemovingMeaningfulFKs() {
+        return removingMeaningfulFKs;
+    }
+
+    public boolean isRemovingMeaningfulPKs() {
+        return removingMeaningfulPKs;
+    }
+
     /**
      * Updates each one of the collection of ObjEntities, adding attributes and
      * relationships based on the current state of its DbEntity.
      *
      * @return true if any ObjEntity has changed as a result of synchronization.
-     * @since 1.2 changed signature to use Collection instead of List.
      */
     public boolean synchronizeWithDbEntities(Iterable<ObjEntity> objEntities) {
         boolean changed = false;
@@ -114,14 +109,14 @@ public class EntityMergeSupport {
      * @since 4.0
      */
     protected boolean removePK(DbEntity dbEntity) {
-        return removeMeaningfulPKs;
+        return removingMeaningfulPKs;
     }
 
     /**
      * @since 4.0
      */
     protected boolean removeFK(DbEntity dbEntity) {
-        return removeMeaningfulFKs;
+        return removingMeaningfulFKs;
     }
 
     /**
@@ -143,18 +138,13 @@ public class EntityMergeSupport {
 
         boolean changed = false;
 
-        // synchronization on DataMap is some (weak) protection
-        // against simultaneous modification of the map (like double-clicking on sync button)
-        synchronized (map) {
-
-            if (removeFK(dbEntity)) {
-                changed = getRidOfAttributesThatAreNowSrcAttributesForRelationships(entity);
-            }
-
-            changed |= addMissingAttributes(entity);
-            changed |= addMissingRelationships(entity);
+        if (removeFK(dbEntity)) {
+            changed = getRidOfAttributesThatAreNowSrcAttributesForRelationships(entity);
         }
 
+        changed |= addMissingAttributes(entity);
+        changed |= addMissingRelationships(entity);
+
         return changed;
     }
 
@@ -249,15 +239,14 @@ public class EntityMergeSupport {
     }
 
     private void addMissingRelationship(ObjEntity entity, DbRelationship dbRelationship) {
-        DbEntity targetEntity = dbRelationship.getTargetEntity();
 
-        Collection<ObjEntity> mappedObjEntities = map.getMappedEntities(targetEntity);
-        if (!mappedObjEntities.isEmpty()) {
-            for (Entity mappedTarget : mappedObjEntities) {
-                createObjRelationship(entity, dbRelationship, mappedTarget.getName());
-            }
-        } else {
+        // getting DataMap from DbRelationship's source entity. This is the only object in our arguments that
+        // is guaranteed to be a part of the map....
+        DataMap dataMap = dbRelationship.getSourceEntity().getDataMap();
 
+        DbEntity targetEntity = dbRelationship.getTargetEntity();
+        Collection<ObjEntity> mappedObjEntities = dataMap.getMappedEntities(targetEntity);
+        if (mappedObjEntities.isEmpty()) {
             if (targetEntity == null) {
                 targetEntity = new DbEntity(dbRelationship.getTargetEntityName());
             }
@@ -266,10 +255,14 @@ public class EntityMergeSupport {
                 boolean needGeneratedEntity = createObjRelationship(entity, dbRelationship,
                         nameGenerator.objEntityName(targetEntity));
                 if (needGeneratedEntity) {
-                    LOG.warn("Can't find ObjEntity for " + dbRelationship.getTargetEntityName());
-                    LOG.warn("Db Relationship (" + dbRelationship + ") will have GUESSED Obj Relationship reflection. ");
+                    LOGGER.warn("Can't find ObjEntity for " + dbRelationship.getTargetEntityName());
+                    LOGGER.warn("Db Relationship (" + dbRelationship + ") will have GUESSED Obj Relationship reflection. ");
                 }
             }
+        } else {
+            for (Entity mappedTarget : mappedObjEntities) {
+                createObjRelationship(entity, dbRelationship, mappedTarget.getName());
+            }
         }
     }
 
@@ -281,7 +274,7 @@ public class EntityMergeSupport {
         oa.setEntity(entity);
 
         String type = TypesMapping.getJavaBySqlType(da.getType());
-        if (usePrimitives) {
+        if (usingPrimitives) {
             String primitive = CLASS_TO_PRIMITIVE.get(type);
             if (primitive != null) {
                 type = primitive;
@@ -313,7 +306,7 @@ public class EntityMergeSupport {
      * @since 1.2
      */
     public Collection<DbAttribute> getMeaningfulFKs(ObjEntity objEntity) {
-        List<DbAttribute> fks = new ArrayList<DbAttribute>(2);
+        List<DbAttribute> fks = new ArrayList<>(2);
 
         for (ObjAttribute property : objEntity.getAttributes()) {
             DbAttribute column = property.getDbAttribute();
@@ -334,7 +327,7 @@ public class EntityMergeSupport {
     protected List<DbAttribute> getAttributesToAdd(ObjEntity objEntity) {
         DbEntity dbEntity = objEntity.getDbEntity();
 
-        List<DbAttribute> missing = new ArrayList<DbAttribute>();
+        List<DbAttribute> missing = new ArrayList<>();
         Collection<DbRelationship> incomingRels = getIncomingRelationships(dbEntity);
 
         for (DbAttribute dba : dbEntity.getAttributes()) {
@@ -443,20 +436,6 @@ public class EntityMergeSupport {
     }
 
     /**
-     * @since 1.2
-     */
-    public boolean isRemoveMeaningfulFKs() {
-        return removeMeaningfulFKs;
-    }
-
-    /**
-     * @since 1.2
-     */
-    public void setRemoveMeaningfulFKs(boolean removeMeaningfulFKs) {
-        this.removeMeaningfulFKs = removeMeaningfulFKs;
-    }
-
-    /**
      * Registers new EntityMergeListener
      */
     public void addEntityMergeListener(EntityMergeListener listener) {
@@ -496,7 +475,7 @@ public class EntityMergeSupport {
     }
 
     /**
-     * @return naming strategy for reverse engineering
+     * @return a strategy for naming object layer artifacts based on their DB names.
      */
     public ObjectNameGenerator getNameGenerator() {
         return nameGenerator;
@@ -505,15 +484,15 @@ public class EntityMergeSupport {
     /**
      * @since 4.0
      */
-    public boolean isUsePrimitives() {
-        return usePrimitives;
+    public boolean isUsingPrimitives() {
+        return usingPrimitives;
     }
 
     /**
-     * @param usePrimitives
+     * @param usingPrimitives
      * @since 4.0
      */
-    public void setUsePrimitives(boolean usePrimitives) {
-        this.usePrimitives = usePrimitives;
+    public void setUsingPrimitives(boolean usingPrimitives) {
+        this.usingPrimitives = usingPrimitives;
     }
 }

http://git-wip-us.apache.org/repos/asf/cayenne/blob/ac04c11b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/MergerContext.java
----------------------------------------------------------------------
diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/MergerContext.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/MergerContext.java
index 77f9e61..57eab3e 100644
--- a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/MergerContext.java
+++ b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/MergerContext.java
@@ -135,9 +135,8 @@ public class MergerContext {
         }
 
         public Builder nameGenerator(ObjectNameGenerator nameGenerator) {
-            context.entityMergeSupport = new EntityMergeSupport(context.getDataMap(),
-                    Objects.requireNonNull(nameGenerator),
-                    true);  // should the last argument also be a part of the builder?
+            // should the last argument also be a part of the builder?
+            context.entityMergeSupport = new EntityMergeSupport(Objects.requireNonNull(nameGenerator), true, true);
             return this;
         }
 

http://git-wip-us.apache.org/repos/asf/cayenne/blob/ac04c11b/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
index 888d6d4..7220b97 100644
--- 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
@@ -22,12 +22,11 @@ import org.apache.cayenne.dba.DbAdapter;
 import org.apache.cayenne.dba.TypesMapping;
 import org.apache.cayenne.dbsync.merge.EntityMergeSupport;
 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.dbsync.naming.LegacyObjectNameGenerator;
-import org.apache.cayenne.dbsync.naming.ObjectNameGenerator;
 import org.apache.cayenne.map.DataMap;
 import org.apache.cayenne.map.DbAttribute;
 import org.apache.cayenne.map.DbEntity;
@@ -51,6 +50,7 @@ import java.util.HashMap;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Set;
 import java.util.TreeSet;
 
@@ -70,38 +70,18 @@ public class DbLoader {
     private final DbAdapter adapter;
     private final DbLoaderDelegate delegate;
 
-    private boolean creatingMeaningfulPK;
-
-    /**
-     * Strategy for choosing names for entities, attributes and relationships
-     */
-    private ObjectNameGenerator nameGenerator;
-
+    private EntityMergeSupport entityMergeSupport;
     private DatabaseMetaData metaData;
 
-
-    /**
-     * Creates new DbLoader.
-     */
-    public DbLoader(Connection connection, DbAdapter adapter, DbLoaderDelegate delegate) {
-        this(connection, adapter, delegate, new LegacyObjectNameGenerator());
-    }
-
-    /**
-     * 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;
+    public DbLoader(Connection connection, DbAdapter adapter, DbLoaderDelegate delegate, EntityMergeSupport entityMergeSupport) {
+        this.adapter = Objects.requireNonNull(adapter);
+        this.connection = Objects.requireNonNull(connection);
+        this.entityMergeSupport = Objects.requireNonNull(entityMergeSupport);
         this.delegate = delegate == null ? new DefaultDbLoaderDelegate() : delegate;
-
-        setNameGenerator(strategy);
     }
 
     private static List<String> getStrings(ResultSet rs) throws SQLException {
-        List<String> strings = new ArrayList<String>();
+        List<String> strings = new ArrayList<>();
 
         while (rs.next()) {
             strings.add(rs.getString(1));
@@ -110,8 +90,12 @@ public class DbLoader {
         return strings;
     }
 
-    private static Collection<ObjEntity> loadObjEntities(DataMap map, DbLoaderConfiguration config,
-                                                         Collection<DbEntity> entities, ObjectNameGenerator nameGenerator) {
+    private static Collection<ObjEntity> loadObjEntities(
+            DataMap map,
+            DbLoaderConfiguration config,
+            Collection<DbEntity> entities,
+            ObjectNameGenerator nameGenerator) {
+
         if (entities.isEmpty()) {
             return Collections.emptyList();
         }
@@ -156,7 +140,7 @@ public class DbLoader {
         if (loadedObjEntities.isEmpty()) {
             return;
         }
-        Collection<ObjEntity> entitiesForDelete = new LinkedList<ObjEntity>();
+        Collection<ObjEntity> entitiesForDelete = new LinkedList<>();
 
         for (ObjEntity curEntity : loadedObjEntities) {
             ManyToManyCandidateEntity entity = ManyToManyCandidateEntity.build(curEntity);
@@ -218,13 +202,6 @@ public class DbLoader {
     }
 
     /**
-     * @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.
@@ -252,19 +229,14 @@ public class DbLoader {
      */
     Collection<ObjEntity> loadObjEntities(DataMap map, DbLoaderConfiguration config,
                                           Collection<DbEntity> entities) {
-        Collection<ObjEntity> loadedEntities = DbLoader.loadObjEntities(map, config, entities, nameGenerator);
+        Collection<ObjEntity> loadedEntities = DbLoader
+                .loadObjEntities(map, config, entities, entityMergeSupport.getNameGenerator());
 
-        createEntityMerger(map).synchronizeWithDbEntities(loadedEntities);
+        entityMergeSupport.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 {
@@ -278,6 +250,8 @@ public class DbLoader {
             tablesMap.put(table.getName(), table);
         }
 
+        ObjectNameGenerator nameGenerator = entityMergeSupport.getNameGenerator();
+
         Map<String, Set<ExportedKey>> keys = loadExportedKeys(config, catalog, schema, tablesMap);
         for (Map.Entry<String, Set<ExportedKey>> entry : keys.entrySet()) {
             if (LOGGER.isDebugEnabled()) {
@@ -352,9 +326,9 @@ public class DbLoader {
 
             // TODO: can we avoid resetting the name twice? Do we need a placeholder name above?
             forwardRelationship.setName(NameBuilder
-                            .builder(forwardRelationship, pkEntity)
-                            .baseName(nameGenerator.dbRelationshipName(key, !isOneToOne))
-                            .name());
+                    .builder(forwardRelationship, pkEntity)
+                    .baseName(nameGenerator.dbRelationshipName(key, !isOneToOne))
+                    .name());
 
             if (delegate.dbRelationshipLoaded(fkEntity, reverseRelationship)) {
                 fkEntity.addRelationship(reverseRelationship);
@@ -519,7 +493,7 @@ public class DbLoader {
 
     private void prepareObjLayer(DataMap dataMap, DbLoaderConfiguration config, Collection<DbEntity> entities) {
         Collection<ObjEntity> loadedObjEntities = loadObjEntities(dataMap, config, entities);
-        flattenManyToManyRelationships(dataMap, loadedObjEntities, nameGenerator);
+        flattenManyToManyRelationships(dataMap, loadedObjEntities, entityMergeSupport.getNameGenerator());
         fireObjEntitiesAddedEvents(loadedObjEntities);
     }
 
@@ -697,18 +671,4 @@ public class DbLoader {
         }
         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. LegacyObjectNameGenerator will be used.");
-            this.nameGenerator = new LegacyObjectNameGenerator();
-        } else {
-            this.nameGenerator = strategy;
-        }
-    }
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cayenne/blob/ac04c11b/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/EntityMergeSupportIT.java
----------------------------------------------------------------------
diff --git a/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/EntityMergeSupportIT.java b/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/EntityMergeSupportIT.java
index 8d304da..bc24100 100644
--- a/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/EntityMergeSupportIT.java
+++ b/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/EntityMergeSupportIT.java
@@ -87,7 +87,7 @@ public class EntityMergeSupportIT extends MergeCase {
 		objEntity2.setDbEntity(dbEntity2);
 		map.addObjEntity(objEntity2);
 
-		assertTrue(new EntityMergeSupport(map, new DefaultObjectNameGenerator(), true)
+		assertTrue(new EntityMergeSupport(new DefaultObjectNameGenerator(), true, true)
 				.synchronizeWithDbEntities(Arrays.asList(objEntity1, objEntity2)));
 		assertNotNull(objEntity1.getAttribute("name"));
 		assertNotNull(objEntity1.getRelationship("rel1To2"));

http://git-wip-us.apache.org/repos/asf/cayenne/blob/ac04c11b/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/reverse/db/DbLoaderIT.java
----------------------------------------------------------------------
diff --git a/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/reverse/db/DbLoaderIT.java b/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/reverse/db/DbLoaderIT.java
index 930d696..fe46391 100644
--- a/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/reverse/db/DbLoaderIT.java
+++ b/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/reverse/db/DbLoaderIT.java
@@ -19,16 +19,22 @@
 
 package org.apache.cayenne.dbsync.reverse.db;
 
-import org.apache.cayenne.dbsync.reverse.db.DbLoader;
-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;
 import org.apache.cayenne.configuration.server.ServerRuntime;
 import org.apache.cayenne.dba.DbAdapter;
 import org.apache.cayenne.dba.TypesMapping;
+import org.apache.cayenne.dbsync.merge.EntityMergeSupport;
+import org.apache.cayenne.dbsync.naming.DefaultObjectNameGenerator;
+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.di.Inject;
-import org.apache.cayenne.map.*;
+import org.apache.cayenne.map.DataMap;
+import org.apache.cayenne.map.DbAttribute;
+import org.apache.cayenne.map.DbEntity;
+import org.apache.cayenne.map.DbRelationship;
+import org.apache.cayenne.map.DetectedDbEntity;
+import org.apache.cayenne.map.ObjAttribute;
+import org.apache.cayenne.map.ObjEntity;
 import org.apache.cayenne.unit.UnitDbAdapter;
 import org.apache.cayenne.unit.di.server.CayenneProjects;
 import org.apache.cayenne.unit.di.server.ServerCase;
@@ -43,7 +49,11 @@ import java.sql.Types;
 import java.util.Collection;
 import java.util.List;
 
-import static org.junit.Assert.*;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
 
 @UseServerRuntime(CayenneProjects.TESTMAP_PROJECT)
 public class DbLoaderIT extends ServerCase {
@@ -61,8 +71,6 @@ public class DbLoaderIT extends ServerCase {
     @Inject
     private UnitDbAdapter accessStackAdapter;
 
-    private DbLoader loader;
-
     private Connection connection;
 
     private static String msgForTypeMismatch(DbAttribute origAttr, DbAttribute newAttr) {
@@ -82,7 +90,14 @@ public class DbLoaderIT extends ServerCase {
     @Before
     public void before() throws Exception {
         this.connection = dataSourceFactory.getSharedDataSource().getConnection();
-        this.loader = new DbLoader(connection, adapter, null);
+    }
+
+    private DbLoader createDbLoader(boolean meaningfulPK, boolean meaningfulFK) {
+        EntityMergeSupport emSupport = new EntityMergeSupport(new DefaultObjectNameGenerator(),
+                !meaningfulPK,
+                !meaningfulFK);
+
+        return new DbLoader(connection, adapter, null, emSupport);
     }
 
     @After
@@ -95,6 +110,8 @@ public class DbLoaderIT extends ServerCase {
 
         String tableLabel = adapter.tableTypeForTable();
 
+        DbLoader loader = createDbLoader(false, false);
+
         List<DetectedDbEntity> tables = loader.createTableLoader(null, null, TableFilter.everything())
                 .getDbEntities(TableFilter.everything(), new String[]{tableLabel});
 
@@ -115,6 +132,8 @@ public class DbLoaderIT extends ServerCase {
     @Test
     public void testGetTablesWithWrongCatalog() throws Exception {
 
+        DbLoader loader = createDbLoader(false, false);
+
         DbLoaderConfiguration config = new DbLoaderConfiguration();
         config.setFiltersConfig(
                 FiltersConfig.create("WRONG", null, TableFilter.everything(), PatternFilter.INCLUDE_NOTHING));
@@ -129,6 +148,8 @@ public class DbLoaderIT extends ServerCase {
     @Test
     public void testGetTablesWithWrongSchema() throws Exception {
 
+        DbLoader loader = createDbLoader(false, false);
+
         DbLoaderConfiguration config = new DbLoaderConfiguration();
         config.setFiltersConfig(
                 FiltersConfig.create(null, "WRONG", TableFilter.everything(), PatternFilter.INCLUDE_NOTHING));
@@ -143,11 +164,11 @@ public class DbLoaderIT extends ServerCase {
     @Test
     public void testLoadWithMeaningfulPK() throws Exception {
 
+        DbLoader loader = createDbLoader(true, false);
+
         DataMap map = new DataMap();
         String[] tableLabel = {adapter.tableTypeForTable()};
 
-        loader.setCreatingMeaningfulPK(true);
-
         List<DbEntity> entities = loader
                 .createTableLoader(null, null, TableFilter.everything())
                 .loadDbEntities(map, CONFIG, tableLabel);
@@ -168,6 +189,7 @@ public class DbLoaderIT extends ServerCase {
      */
     @Test
     public void testLoad() throws Exception {
+        DbLoader loader = createDbLoader(false, false);
 
         boolean supportsUnique = runtime.getDataDomain().getDataNodes().iterator().next().getAdapter()
                 .supportsUniqueConstraints();
@@ -223,7 +245,6 @@ public class DbLoaderIT extends ServerCase {
         }
 
         // *** TESTING THIS ***
-        loader.setCreatingMeaningfulPK(false);
         loader.loadObjEntities(map, CONFIG, entities);
 
         assertObjEntities(map);

http://git-wip-us.apache.org/repos/asf/cayenne/blob/ac04c11b/cayenne-tools/src/main/java/org/apache/cayenne/tools/dbimport/DbImportConfiguration.java
----------------------------------------------------------------------
diff --git a/cayenne-tools/src/main/java/org/apache/cayenne/tools/dbimport/DbImportConfiguration.java b/cayenne-tools/src/main/java/org/apache/cayenne/tools/dbimport/DbImportConfiguration.java
index b5c2fa7..c371ee6 100644
--- a/cayenne-tools/src/main/java/org/apache/cayenne/tools/dbimport/DbImportConfiguration.java
+++ b/cayenne-tools/src/main/java/org/apache/cayenne/tools/dbimport/DbImportConfiguration.java
@@ -146,27 +146,16 @@ public class DbImportConfiguration {
         final NameFilter meaningfulPkFilter = NamePatternMatcher.build(logger, getMeaningfulPkTables(),
                 getMeaningfulPkTables() != null ? null : "*");
 
-        DbLoader loader = new DbLoader(connection, adapter, loaderDelegate) {
+        EntityMergeSupport emSupport = new EntityMergeSupport(getNameGenerator(), true, true) {
 
             @Override
-            protected EntityMergeSupport createEntityMerger(DataMap map) {
-                EntityMergeSupport emSupport = new EntityMergeSupport(map, DbImportConfiguration.this.getNameGenerator(), true) {
-
-                    @Override
-                    protected boolean removePK(DbEntity dbEntity) {
-                        return !meaningfulPkFilter.isIncluded(dbEntity.getName());
-                    }
-                };
-
-                emSupport.setUsePrimitives(DbImportConfiguration.this.isUsePrimitives());
-                return emSupport;
+            protected boolean removePK(DbEntity dbEntity) {
+                return !meaningfulPkFilter.isIncluded(dbEntity.getName());
             }
         };
 
-
-        loader.setNameGenerator(getNameGenerator());
-
-        return loader;
+        emSupport.setUsingPrimitives(isUsePrimitives());
+        return new DbLoader(connection, adapter, loaderDelegate, emSupport);
     }
 
     public ObjectNameGenerator getNameGenerator() {

http://git-wip-us.apache.org/repos/asf/cayenne/blob/ac04c11b/cayenne-tools/src/main/java/org/apache/cayenne/tools/dbimport/DefaultDbImportAction.java
----------------------------------------------------------------------
diff --git a/cayenne-tools/src/main/java/org/apache/cayenne/tools/dbimport/DefaultDbImportAction.java b/cayenne-tools/src/main/java/org/apache/cayenne/tools/dbimport/DefaultDbImportAction.java
index 8f5ef79..6ec486e 100644
--- a/cayenne-tools/src/main/java/org/apache/cayenne/tools/dbimport/DefaultDbImportAction.java
+++ b/cayenne-tools/src/main/java/org/apache/cayenne/tools/dbimport/DefaultDbImportAction.java
@@ -110,6 +110,7 @@ public class DefaultDbImportAction implements DbImportAction {
         return reverse;
     }
 
+    @Override
     public void execute(DbImportConfiguration config) throws Exception {
 
         if (logger.isDebugEnabled()) {

http://git-wip-us.apache.org/repos/asf/cayenne/blob/ac04c11b/cayenne-tools/src/test/java/org/apache/cayenne/tools/dbimport/DefaultDbImportActionTest.java
----------------------------------------------------------------------
diff --git a/cayenne-tools/src/test/java/org/apache/cayenne/tools/dbimport/DefaultDbImportActionTest.java b/cayenne-tools/src/test/java/org/apache/cayenne/tools/dbimport/DefaultDbImportActionTest.java
index 98842a3..affe74e 100644
--- a/cayenne-tools/src/test/java/org/apache/cayenne/tools/dbimport/DefaultDbImportActionTest.java
+++ b/cayenne-tools/src/test/java/org/apache/cayenne/tools/dbimport/DefaultDbImportActionTest.java
@@ -29,6 +29,7 @@ import org.apache.cayenne.dbsync.merge.AddRelationshipToDb;
 import org.apache.cayenne.dbsync.merge.CreateTableToDb;
 import org.apache.cayenne.dbsync.merge.CreateTableToModel;
 import org.apache.cayenne.dbsync.merge.DefaultModelMergeDelegate;
+import org.apache.cayenne.dbsync.merge.EntityMergeSupport;
 import org.apache.cayenne.dbsync.merge.factory.MergerTokenFactoryProvider;
 import org.apache.cayenne.dbsync.merge.MergerToken;
 import org.apache.cayenne.dbsync.merge.builders.DataMapBuilder;
@@ -48,6 +49,7 @@ import org.apache.cayenne.resource.URLResource;
 import org.apache.cayenne.tools.configuration.ToolsModule;
 import org.apache.cayenne.util.Util;
 import org.apache.commons.logging.Log;
+import org.junit.Before;
 import org.junit.Test;
 import org.xml.sax.InputSource;
 
@@ -87,32 +89,45 @@ public class DefaultDbImportActionTest {
             return true;
         }
 
-		@Override
-		public boolean canRead() {
-			return true;
-		}
-	};
+        @Override
+        public boolean canRead() {
+            return true;
+        }
+    };
+
+    private DbAdapter mockAdapter;
+    private Connection mockConnection;
+    private DbLoaderDelegate mockDelegate;
+    private EntityMergeSupport mockEmSupport;
+
+    @Before
+    public void before() {
+        mockAdapter = mock(DbAdapter.class);
+        mockConnection = mock(Connection.class);
+        mockDelegate = mock(DbLoaderDelegate.class);
+        mockEmSupport = mock(EntityMergeSupport.class);
+    }
 
-	@Test
-	public void testNewDataMapImport() throws Exception {
+    @Test
+    public void testNewDataMapImport() throws Exception {
 
-		DbLoader dbLoader = new DbLoader(null, null, null) {
-			@Override
-			public void load(DataMap dataMap, DbLoaderConfiguration config) throws SQLException {
-				new DataMapBuilder(dataMap).withDbEntities(2).build();
-			}
-		};
+        DbLoader dbLoader = new DbLoader(mockConnection, mockAdapter, mockDelegate, mockEmSupport) {
+            @Override
+            public void load(DataMap dataMap, DbLoaderConfiguration config) throws SQLException {
+                new DataMapBuilder(dataMap).withDbEntities(2).build();
+            }
+        };
 
         DbImportConfiguration params = mock(DbImportConfiguration.class);
         when(params.createLoader(any(DbAdapter.class), any(Connection.class), any(DbLoaderDelegate.class)))
                 .thenReturn(dbLoader);
 
-		when(params.createDataMap()).thenReturn(new DataMap("testImport"));
-		when(params.createMergeDelegate()).thenReturn(new DefaultModelMergeDelegate());
-		when(params.getDbLoaderConfig()).thenReturn(new DbLoaderConfiguration());
+        when(params.createDataMap()).thenReturn(new DataMap("testImport"));
+        when(params.createMergeDelegate()).thenReturn(new DefaultModelMergeDelegate());
+        when(params.getDbLoaderConfig()).thenReturn(new DbLoaderConfiguration());
 
-		final DataMap DATA_MAP = new DataMap();
-		when(params.initializeDataMap(any(DataMap.class))).thenReturn(DATA_MAP);
+        final DataMap DATA_MAP = new DataMap();
+        when(params.initializeDataMap(any(DataMap.class))).thenReturn(DATA_MAP);
 
         final boolean[] haveWeTriedToSave = {false};
         DefaultDbImportAction action = buildDbImportAction(new FileProjectSaver() {
@@ -120,19 +135,19 @@ public class DefaultDbImportActionTest {
             public void save(Project project) {
                 haveWeTriedToSave[0] = true;
 
-				// Validation phase
-				assertEquals(DATA_MAP, project.getRootNode());
-			}
-		}, null);
+                // Validation phase
+                assertEquals(DATA_MAP, project.getRootNode());
+            }
+        }, null);
 
-		action.execute(params);
+        action.execute(params);
 
-		assertTrue("We should try to save.", haveWeTriedToSave[0]);
-	}
+        assertTrue("We should try to save.", haveWeTriedToSave[0]);
+    }
 
     @Test
     public void testImportWithFieldChanged() throws Exception {
-        DbLoader dbLoader = new DbLoader(null, null, null) {
+        DbLoader dbLoader = new DbLoader(mockConnection, mockAdapter, mockDelegate, mockEmSupport) {
             @Override
             public void load(DataMap dataMap, DbLoaderConfiguration config) throws SQLException {
                 new DataMapBuilder(dataMap).with(
@@ -146,17 +161,17 @@ public class DefaultDbImportActionTest {
                                 objAttr("name").type(String.class).dbPath("NAME")
                         ));
             }
-		};
+        };
 
         DbImportConfiguration params = mock(DbImportConfiguration.class);
         when(params.createLoader(any(DbAdapter.class), any(Connection.class), any(DbLoaderDelegate.class)))
                 .thenReturn(dbLoader);
 
-		when(params.createDataMap()).thenReturn(new DataMap("testImport"));
-		when(params.getDataMapFile()).thenReturn(FILE_STUB);
-		when(params.createMergeDelegate()).thenReturn(new DefaultModelMergeDelegate());
-		when(params.getDbLoaderConfig()).thenReturn(new DbLoaderConfiguration());
-		when(params.getNameGenerator()).thenReturn(new DefaultObjectNameGenerator());
+        when(params.createDataMap()).thenReturn(new DataMap("testImport"));
+        when(params.getDataMapFile()).thenReturn(FILE_STUB);
+        when(params.createMergeDelegate()).thenReturn(new DefaultModelMergeDelegate());
+        when(params.getDbLoaderConfig()).thenReturn(new DbLoaderConfiguration());
+        when(params.getNameGenerator()).thenReturn(new DefaultObjectNameGenerator());
 
         final boolean[] haveWeTriedToSave = {false};
         DefaultDbImportAction action = buildDbImportAction(new FileProjectSaver() {
@@ -164,17 +179,17 @@ public class DefaultDbImportActionTest {
             public void save(Project project) {
                 haveWeTriedToSave[0] = true;
 
-				// Validation phase
-				DataMap rootNode = (DataMap) project.getRootNode();
-				assertEquals(1, rootNode.getObjEntities().size());
-				assertEquals(1, rootNode.getDbEntityMap().size());
+                // Validation phase
+                DataMap rootNode = (DataMap) project.getRootNode();
+                assertEquals(1, rootNode.getObjEntities().size());
+                assertEquals(1, rootNode.getDbEntityMap().size());
 
-				DbEntity entity = rootNode.getDbEntity("ARTGROUP");
-				assertNotNull(entity);
-				assertEquals(4, entity.getAttributes().size());
-				assertNotNull(entity.getAttribute("NAME_01"));
-			}
-		}, new MapLoader() {
+                DbEntity entity = rootNode.getDbEntity("ARTGROUP");
+                assertNotNull(entity);
+                assertEquals(4, entity.getAttributes().size());
+                assertNotNull(entity.getAttribute("NAME_01"));
+            }
+        }, new MapLoader() {
 
             @Override
             public synchronized DataMap loadDataMap(InputSource src) throws CayenneRuntimeException {
@@ -190,14 +205,14 @@ public class DefaultDbImportActionTest {
             }
         });
 
-		action.execute(params);
+        action.execute(params);
 
-		assertTrue("We should try to save.", haveWeTriedToSave[0]);
-	}
+        assertTrue("We should try to save.", haveWeTriedToSave[0]);
+    }
 
     @Test
     public void testImportWithoutChanges() throws Exception {
-        DbLoader dbLoader = new DbLoader(null, null, null) {
+        DbLoader dbLoader = new DbLoader(mockConnection, mockAdapter, mockDelegate, mockEmSupport) {
             @Override
             public void load(DataMap dataMap, DbLoaderConfiguration config) throws SQLException {
                 new DataMapBuilder(dataMap).with(
@@ -205,23 +220,23 @@ public class DefaultDbImportActionTest {
                                 dbAttr("NAME").typeVarchar(100).mandatory()
                         ));
             }
-		};
+        };
 
         DbImportConfiguration params = mock(DbImportConfiguration.class);
         when(params.createLoader(any(DbAdapter.class), any(Connection.class), any(DbLoaderDelegate.class)))
                 .thenReturn(dbLoader);
 
-		when(params.createDataMap()).thenReturn(new DataMap("testImport"));
-		when(params.getDataMapFile()).thenReturn(FILE_STUB);
-		when(params.createMergeDelegate()).thenReturn(new DefaultModelMergeDelegate());
-		when(params.getDbLoaderConfig()).thenReturn(new DbLoaderConfiguration());
+        when(params.createDataMap()).thenReturn(new DataMap("testImport"));
+        when(params.getDataMapFile()).thenReturn(FILE_STUB);
+        when(params.createMergeDelegate()).thenReturn(new DefaultModelMergeDelegate());
+        when(params.getDbLoaderConfig()).thenReturn(new DbLoaderConfiguration());
 
-		Log log = mock(Log.class);
-		when(log.isDebugEnabled()).thenReturn(false);
-		when(log.isInfoEnabled()).thenReturn(false);
+        Log log = mock(Log.class);
+        when(log.isDebugEnabled()).thenReturn(false);
+        when(log.isInfoEnabled()).thenReturn(false);
 
-		FileProjectSaver projectSaver = mock(FileProjectSaver.class);
-		doNothing().when(projectSaver).save(any(Project.class));
+        FileProjectSaver projectSaver = mock(FileProjectSaver.class);
+        doNothing().when(projectSaver).save(any(Project.class));
 
         MapLoader mapLoader = mock(MapLoader.class);
         stub(mapLoader.loadDataMap(any(InputSource.class))).toReturn(new DataMapBuilder().with(
@@ -231,108 +246,108 @@ public class DefaultDbImportActionTest {
 
         DefaultDbImportAction action = buildDbImportAction(log, projectSaver, mapLoader);
 
-		action.execute(params);
+        action.execute(params);
 
-		verify(projectSaver, never()).save(any(Project.class));
-		verify(mapLoader, times(1)).loadDataMap(any(InputSource.class));
-	}
+        verify(projectSaver, never()).save(any(Project.class));
+        verify(mapLoader, times(1)).loadDataMap(any(InputSource.class));
+    }
 
-	@Test
-	public void testImportWithDbError() throws Exception {
-		DbLoader dbLoader = mock(DbLoader.class);
-		doThrow(new SQLException()).when(dbLoader).load(any(DataMap.class), any(DbLoaderConfiguration.class));
+    @Test
+    public void testImportWithDbError() throws Exception {
+        DbLoader dbLoader = mock(DbLoader.class);
+        doThrow(new SQLException()).when(dbLoader).load(any(DataMap.class), any(DbLoaderConfiguration.class));
 
         DbImportConfiguration params = mock(DbImportConfiguration.class);
         when(params.createLoader(any(DbAdapter.class), any(Connection.class), any(DbLoaderDelegate.class)))
                 .thenReturn(dbLoader);
 
-		FileProjectSaver projectSaver = mock(FileProjectSaver.class);
-		doNothing().when(projectSaver).save(any(Project.class));
+        FileProjectSaver projectSaver = mock(FileProjectSaver.class);
+        doNothing().when(projectSaver).save(any(Project.class));
 
-		MapLoader mapLoader = mock(MapLoader.class);
-		when(mapLoader.loadDataMap(any(InputSource.class))).thenReturn(null);
+        MapLoader mapLoader = mock(MapLoader.class);
+        when(mapLoader.loadDataMap(any(InputSource.class))).thenReturn(null);
 
         DefaultDbImportAction action = buildDbImportAction(projectSaver, mapLoader);
 
-		try {
-			action.execute(params);
-			fail();
-		} catch (SQLException e) {
-			// expected
-		}
+        try {
+            action.execute(params);
+            fail();
+        } catch (SQLException e) {
+            // expected
+        }
 
-		verify(projectSaver, never()).save(any(Project.class));
-		verify(mapLoader, never()).loadDataMap(any(InputSource.class));
-	}
+        verify(projectSaver, never()).save(any(Project.class));
+        verify(mapLoader, never()).loadDataMap(any(InputSource.class));
+    }
 
     private DefaultDbImportAction buildDbImportAction(FileProjectSaver projectSaver, MapLoader mapLoader) throws Exception {
         Log log = mock(Log.class);
         when(log.isDebugEnabled()).thenReturn(true);
         when(log.isInfoEnabled()).thenReturn(true);
 
-		return buildDbImportAction(log, projectSaver, mapLoader);
-	}
+        return buildDbImportAction(log, projectSaver, mapLoader);
+    }
 
     private DefaultDbImportAction buildDbImportAction(Log log, FileProjectSaver projectSaver, MapLoader mapLoader) throws Exception {
         DbAdapter dbAdapter = mock(DbAdapter.class);
 
-		DbAdapterFactory adapterFactory = mock(DbAdapterFactory.class);
-		when(adapterFactory.createAdapter(any(DataNodeDescriptor.class), any(DataSource.class))).thenReturn(dbAdapter);
+        DbAdapterFactory adapterFactory = mock(DbAdapterFactory.class);
+        when(adapterFactory.createAdapter(any(DataNodeDescriptor.class), any(DataSource.class))).thenReturn(dbAdapter);
 
-		DataSourceFactory dataSourceFactory = mock(DataSourceFactory.class);
-		DataSource mock = mock(DataSource.class);
-		when(dataSourceFactory.getDataSource(any(DataNodeDescriptor.class))).thenReturn(mock);
+        DataSourceFactory dataSourceFactory = mock(DataSourceFactory.class);
+        DataSource mock = mock(DataSource.class);
+        when(dataSourceFactory.getDataSource(any(DataNodeDescriptor.class))).thenReturn(mock);
 
-		MergerTokenFactoryProvider mergerTokenFactoryProvider = mock(MergerTokenFactoryProvider.class);
-		when(mergerTokenFactoryProvider.get(any(DbAdapter.class))).thenReturn(new DefaultMergerTokenFactory());
+        MergerTokenFactoryProvider mergerTokenFactoryProvider = mock(MergerTokenFactoryProvider.class);
+        when(mergerTokenFactoryProvider.get(any(DbAdapter.class))).thenReturn(new DefaultMergerTokenFactory());
 
         return new DefaultDbImportAction(log, projectSaver, dataSourceFactory, adapterFactory, mapLoader, mergerTokenFactoryProvider);
     }
 
-	@Test
-	public void testSaveLoaded() throws Exception {
-		Log log = mock(Log.class);
-		Injector i = DIBootstrap.createInjector(new CayenneDbSyncModule(), new ToolsModule(log), new DbImportModule());
+    @Test
+    public void testSaveLoaded() throws Exception {
+        Log log = mock(Log.class);
+        Injector i = DIBootstrap.createInjector(new CayenneDbSyncModule(), new ToolsModule(log), new DbImportModule());
 
         DefaultDbImportAction action = (DefaultDbImportAction) i.getInstance(DbImportAction.class);
 
-		String packagePath = getClass().getPackage().getName().replace('.', '/');
-		URL packageUrl = getClass().getClassLoader().getResource(packagePath);
-		assertNotNull(packageUrl);
-		URL outUrl = new URL(packageUrl, "dbimport/testSaveLoaded1.map.xml");
+        String packagePath = getClass().getPackage().getName().replace('.', '/');
+        URL packageUrl = getClass().getClassLoader().getResource(packagePath);
+        assertNotNull(packageUrl);
+        URL outUrl = new URL(packageUrl, "dbimport/testSaveLoaded1.map.xml");
 
-		File out = new File(outUrl.toURI());
-		out.delete();
-		assertFalse(out.isFile());
+        File out = new File(outUrl.toURI());
+        out.delete();
+        assertFalse(out.isFile());
 
-		DataMap map = new DataMap("testSaveLoaded1");
-		map.setConfigurationSource(new URLResource(outUrl));
+        DataMap map = new DataMap("testSaveLoaded1");
+        map.setConfigurationSource(new URLResource(outUrl));
 
-		action.saveLoaded(map);
+        action.saveLoaded(map);
 
-		assertTrue(out.isFile());
+        assertTrue(out.isFile());
 
-		String contents = Util.stringFromFile(out);
-		assertTrue("Has no project version saved", contents.contains("project-version=\""));
-	}
+        String contents = Util.stringFromFile(out);
+        assertTrue("Has no project version saved", contents.contains("project-version=\""));
+    }
 
-	@Test
-	public void testMergeTokensSorting() {
-		LinkedList<MergerToken> tokens = new LinkedList<MergerToken>();
-		tokens.add(new AddColumnToDb(null, null));
-		tokens.add(new AddRelationshipToDb(null, null));
-		tokens.add(new CreateTableToDb(null));
-		tokens.add(new CreateTableToModel(null));
+    @Test
+    public void testMergeTokensSorting() {
+        LinkedList<MergerToken> tokens = new LinkedList<MergerToken>();
+        tokens.add(new AddColumnToDb(null, null));
+        tokens.add(new AddRelationshipToDb(null, null));
+        tokens.add(new CreateTableToDb(null));
+        tokens.add(new CreateTableToModel(null));
 
         assertEquals(asList("AddColumnToDb", "CreateTableToDb", "CreateTableToModel", "AddRelationshipToDb"),
                 toClasses(DefaultDbImportAction.sort(tokens)));
     }
 
-	private List<String> toClasses(List<MergerToken> sort) {
-		LinkedList<String> res = new LinkedList<String>();
-		for (MergerToken mergerToken : sort) {
-			res.add(mergerToken.getClass().getSimpleName());
-		}
-		return res;
-	}
+    private List<String> toClasses(List<MergerToken> sort) {
+        LinkedList<String> res = new LinkedList<String>();
+        for (MergerToken mergerToken : sort) {
+            res.add(mergerToken.getClass().getSimpleName());
+        }
+        return res;
+    }
 }

http://git-wip-us.apache.org/repos/asf/cayenne/blob/ac04c11b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/action/CreateObjEntityAction.java
----------------------------------------------------------------------
diff --git a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/action/CreateObjEntityAction.java b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/action/CreateObjEntityAction.java
index b5a95d7..af43556 100644
--- a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/action/CreateObjEntityAction.java
+++ b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/action/CreateObjEntityAction.java
@@ -1,22 +1,21 @@
-/*****************************************************************
- *   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
+/*
+ *    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
+ *      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.
- ****************************************************************/
-
+ *    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.modeler.action;
 
 import org.apache.cayenne.configuration.ConfigurationNode;
@@ -113,7 +112,7 @@ public class CreateObjEntityAction extends CayenneAction {
         dataMap.addObjEntity(entity);
 
         // perform the merge
-        EntityMergeSupport merger = new EntityMergeSupport(dataMap, new DefaultObjectNameGenerator(), true);
+        EntityMergeSupport merger = new EntityMergeSupport(new DefaultObjectNameGenerator(), true, true);
         merger.addEntityMergeListener(DeleteRuleUpdater.getEntityMergeListener());
         merger.synchronizeWithDbEntity(entity);
 

http://git-wip-us.apache.org/repos/asf/cayenne/blob/ac04c11b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/action/DbEntitySyncAction.java
----------------------------------------------------------------------
diff --git a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/action/DbEntitySyncAction.java b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/action/DbEntitySyncAction.java
index 65294b5..5a5f346 100644
--- a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/action/DbEntitySyncAction.java
+++ b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/action/DbEntitySyncAction.java
@@ -89,7 +89,7 @@ public class DbEntitySyncAction extends CayenneAction {
 
 				merger.addEntityMergeListener(listener);
 
-				if (merger.isRemoveMeaningfulFKs()) {
+				if (merger.isRemovingMeaningfulFKs()) {
 					undoableEdit.addEdit(undoableEdit.new MeaningfulFKsUndoableEdit(entity, merger
 							.getMeaningfulFKs(entity)));
 				}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/ac04c11b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/action/MigrateAction.java
----------------------------------------------------------------------
diff --git a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/action/MigrateAction.java b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/action/MigrateAction.java
index b8e7383..a629477 100644
--- a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/action/MigrateAction.java
+++ b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/action/MigrateAction.java
@@ -20,7 +20,9 @@
 package org.apache.cayenne.modeler.action;
 
 import org.apache.cayenne.dba.DbAdapter;
+import org.apache.cayenne.dbsync.merge.EntityMergeSupport;
 import org.apache.cayenne.dbsync.merge.factory.MergerTokenFactoryProvider;
+import org.apache.cayenne.dbsync.naming.DefaultObjectNameGenerator;
 import org.apache.cayenne.dbsync.reverse.db.DbLoader;
 import org.apache.cayenne.map.DataMap;
 import org.apache.cayenne.modeler.Application;
@@ -105,6 +107,8 @@ public class MigrateAction extends DBWizardAction {
                 .makeAdapter(getApplication().getClassLoadingService());
         DataSource dataSource = connectWizard.getConnectionInfo()
                 .makeDataSource(getApplication().getClassLoadingService());
-        return new DbLoader(dataSource.getConnection(), dbAdapter, null).loadSchemas();
+
+        EntityMergeSupport emSupport = new EntityMergeSupport(new DefaultObjectNameGenerator(), true, true);
+        return new DbLoader(dataSource.getConnection(), dbAdapter, null, emSupport).loadSchemas();
     }
 }

http://git-wip-us.apache.org/repos/asf/cayenne/blob/ac04c11b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/db/DbLoaderHelper.java
----------------------------------------------------------------------
diff --git a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/db/DbLoaderHelper.java b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/db/DbLoaderHelper.java
index 7ee114c..5a3bbfb 100644
--- a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/db/DbLoaderHelper.java
+++ b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/db/DbLoaderHelper.java
@@ -62,10 +62,11 @@ import java.util.List;
 public class DbLoaderHelper {
 
     // TODO: this is a temp hack... need to delegate to DbAdapter, or
-    // configurable in
-    // preferences...
+    // configurable in preferences...
     private static final Collection<String> EXCLUDED_TABLES = Arrays.asList("AUTO_PK_SUPPORT", "auto_pk_support");
-    private static Log logObj = LogFactory.getLog(DbLoaderHelper.class);
+
+    private static Log LOGGER = LogFactory.getLog(DbLoaderHelper.class);
+
     protected boolean stoppingReverseEngineering;
     protected boolean existingMap;
 
@@ -91,7 +92,7 @@ public class DbLoaderHelper {
         try {
             this.dbCatalog = connection.getCatalog();
         } catch (SQLException e) {
-            logObj.warn("Error getting catalog.", e);
+            LOGGER.warn("Error getting catalog.", e);
         }
         this.adapter = adapter;
         this.reverseEngineering = reverseEngineering;
@@ -105,7 +106,7 @@ public class DbLoaderHelper {
         try {
             this.dbCatalog = connection.getCatalog();
         } catch (SQLException e) {
-            logObj.warn("Error getting catalog.", e);
+            LOGGER.warn("Error getting catalog.", e);
         }
         try {
             this.loader = config.createLoader(adapter, connection, new LoaderDelegate());
@@ -152,15 +153,12 @@ public class DbLoaderHelper {
             return;
         }
 
-        this.loader.setCreatingMeaningfulPK(true);
-
         LongRunningTask loadDataMapTask = new LoadDataMapTask(Application.getFrame(), "Reengineering DB");
         loadDataMapTask.startAndWait();
-
     }
 
     protected void processException(final Throwable th, final String message) {
-        logObj.info("Exception on reverse engineering", Util.unwindException(th));
+        LOGGER.info("Exception on reverse engineering", Util.unwindException(th));
         SwingUtilities.invokeLater(new Runnable() {
 
             public void run() {
@@ -369,11 +367,11 @@ public class DbLoaderHelper {
             config.getDbLoaderConfig().setFiltersConfig(filtersConfigBuilder.build());
 
 
-            ModelerDbImportAction importAction = new ModelerDbImportAction(logObj, DbLoaderHelper.this);
+            ModelerDbImportAction importAction = new ModelerDbImportAction(LOGGER, DbLoaderHelper.this);
 
             // TODO: we can keep all these things in the Modeler Injector instead of creating a new one?
             // we already have CayenneDbSyncModule in there
-            Injector injector = DIBootstrap.createInjector(new CayenneDbSyncModule(), new ToolsModule(logObj), new DbImportModule());
+            Injector injector = DIBootstrap.createInjector(new CayenneDbSyncModule(), new ToolsModule(LOGGER), new DbImportModule());
             injector.injectMembers(importAction);
             try {
                 importAction.execute(config);

http://git-wip-us.apache.org/repos/asf/cayenne/blob/ac04c11b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/db/ModelerDbLoader.java
----------------------------------------------------------------------
diff --git a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/db/ModelerDbLoader.java b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/db/ModelerDbLoader.java
index 6ca30af..4a5f53e 100644
--- a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/db/ModelerDbLoader.java
+++ b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/db/ModelerDbLoader.java
@@ -18,6 +18,8 @@
  ****************************************************************/
 package org.apache.cayenne.modeler.dialog.db;
 
+import org.apache.cayenne.dbsync.merge.EntityMergeSupport;
+import org.apache.cayenne.dbsync.naming.DefaultObjectNameGenerator;
 import org.apache.cayenne.dbsync.reverse.db.DbLoader;
 import org.apache.cayenne.dbsync.reverse.db.DbLoaderConfiguration;
 import org.apache.cayenne.dbsync.reverse.db.DefaultDbLoaderDelegate;
@@ -52,7 +54,10 @@ class ModelerDbLoader extends DbLoader {
     private ReverseEngineeringController reverseEngineeringController;
 
     public ModelerDbLoader(ReverseEngineeringController reverseEngineeringController, TreeEditor treeEditor, Connection connection) {
-        super(connection, reverseEngineeringController.adapter, new DefaultDbLoaderDelegate());
+        super(connection,
+                reverseEngineeringController.adapter,
+                new DefaultDbLoaderDelegate(),
+                new EntityMergeSupport(new DefaultObjectNameGenerator(), true, true));
         this.treeEditor = treeEditor;
         this.reverseEngineeringController = reverseEngineeringController;
     }