You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cayenne.apache.org by sk...@apache.org on 2016/11/16 18:45:59 UTC

[4/9] cayenne git commit: Unified behavior in reverse engineering and db migration actions - Many to many relationships are flattened in db migration - All tokens are generated for forward and reverse relationships. This leads to proper token rever

Unified behavior in reverse engineering and db migration actions
   - Many to many relationships are flattened in db migration
   - All tokens are generated for forward and reverse relationships. This leads to proper token reverse logic.


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

Branch: refs/heads/master
Commit: 29d6439269cbe7d1c09e92a78c88093e32ae51ea
Parents: 8718ee9
Author: Nikita Timofeev <st...@gmail.com>
Authored: Mon Nov 14 20:19:27 2016 +0300
Committer: Nikita Timofeev <st...@gmail.com>
Committed: Mon Nov 14 20:19:27 2016 +0300

----------------------------------------------------------------------
 .../cayenne/dbsync/merge/AbstractToDbToken.java |   4 +
 .../dbsync/merge/AbstractToModelToken.java      |   4 +
 .../cayenne/dbsync/merge/AddColumnToDb.java     |   5 +-
 .../cayenne/dbsync/merge/AddColumnToModel.java  |   7 +-
 .../dbsync/merge/AddRelationshipToDb.java       |  12 +-
 .../dbsync/merge/AddRelationshipToModel.java    |   7 +-
 .../cayenne/dbsync/merge/CreateTableToDb.java   |   5 +-
 .../dbsync/merge/CreateTableToModel.java        |   7 +-
 .../apache/cayenne/dbsync/merge/DbMerger.java   |  20 +-
 .../cayenne/dbsync/merge/DropColumnToDb.java    |   5 +-
 .../cayenne/dbsync/merge/DropColumnToModel.java |   6 +-
 .../dbsync/merge/DropRelationshipToDb.java      |  24 +-
 .../dbsync/merge/DropRelationshipToModel.java   |   7 +-
 .../cayenne/dbsync/merge/DropTableToDb.java     |   6 +-
 .../cayenne/dbsync/merge/DropTableToModel.java  |   7 +-
 .../cayenne/dbsync/merge/DummyReverseToken.java |  12 +-
 .../cayenne/dbsync/merge/MergerToken.java       |   4 +-
 .../cayenne/dbsync/merge/SetAllowNullToDb.java  |   5 +-
 .../dbsync/merge/SetAllowNullToModel.java       |   7 +-
 .../cayenne/dbsync/merge/SetColumnTypeToDb.java |   5 +-
 .../dbsync/merge/SetColumnTypeToModel.java      |   7 +-
 .../cayenne/dbsync/merge/SetNotNullToDb.java    |   5 +-
 .../cayenne/dbsync/merge/SetNotNullToModel.java |   7 +-
 .../cayenne/dbsync/merge/SetPrimaryKeyToDb.java |  11 +-
 .../dbsync/merge/SetPrimaryKeyToModel.java      |   7 +-
 .../dbsync/merge/SetValueForNullToDb.java       |   6 +-
 .../dbsync/merge/AddColumnToModelIT.java        |   2 +-
 .../dbsync/merge/CreateTableToModelIT.java      |   2 +-
 .../dbsync/merge/DropColumnToModelIT.java       |   6 +-
 .../dbsync/merge/DropRelationshipToModelIT.java |   6 +-
 .../dbsync/merge/DropTableToModelIT.java        |   2 +-
 .../apache/cayenne/dbsync/merge/MergeCase.java  |  13 +-
 .../cayenne/dbsync/merge/TokensReverseTest.java |  26 +-
 .../tools/dbimport/DefaultDbImportAction.java   |   4 +-
 .../modeler/dialog/db/MergerOptions.java        | 400 +++++++++++--------
 .../db/MergerTokenSelectorController.java       |   4 +-
 36 files changed, 338 insertions(+), 329 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cayenne/blob/29d64392/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/AbstractToDbToken.java
----------------------------------------------------------------------
diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/AbstractToDbToken.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/AbstractToDbToken.java
index 1cc3092..88e056f 100644
--- a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/AbstractToDbToken.java
+++ b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/AbstractToDbToken.java
@@ -78,6 +78,10 @@ public abstract class AbstractToDbToken implements MergerToken, Comparable<Merge
 		return getTokenName() + ' ' + getTokenValue() + ' ' + getDirection();
 	}
 
+	public boolean isEmpty() {
+		return false;
+	}
+
 	public abstract List<String> createSql(DbAdapter adapter);
 
 	abstract static class Entity extends AbstractToDbToken {

http://git-wip-us.apache.org/repos/asf/cayenne/blob/29d64392/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/AbstractToModelToken.java
----------------------------------------------------------------------
diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/AbstractToModelToken.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/AbstractToModelToken.java
index 424c8dc..24cee4a 100644
--- a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/AbstractToModelToken.java
+++ b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/AbstractToModelToken.java
@@ -79,6 +79,10 @@ public abstract class AbstractToModelToken implements MergerToken {
         return getTokenName() + ' ' + getTokenValue() + ' ' + getDirection();
     }
 
+    public boolean isEmpty() {
+        return false;
+    }
+
     abstract static class Entity extends AbstractToModelToken {
 
         private final DbEntity entity;

http://git-wip-us.apache.org/repos/asf/cayenne/blob/29d64392/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/AddColumnToDb.java
----------------------------------------------------------------------
diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/AddColumnToDb.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/AddColumnToDb.java
index 7b32168..2a87b98 100644
--- a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/AddColumnToDb.java
+++ b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/AddColumnToDb.java
@@ -25,7 +25,6 @@ import org.apache.cayenne.dbsync.merge.factory.MergerTokenFactory;
 import org.apache.cayenne.map.DbAttribute;
 import org.apache.cayenne.map.DbEntity;
 
-import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
 
@@ -60,7 +59,7 @@ public class AddColumnToDb extends AbstractToDbToken.EntityAndColumn {
     }
 
     @Override
-    public Collection<MergerToken> createReverse(MergerTokenFactory factory) {
-        return Collections.singleton(factory.createDropColumnToModel(getEntity(), getColumn()));
+    public MergerToken createReverse(MergerTokenFactory factory) {
+        return factory.createDropColumnToModel(getEntity(), getColumn());
     }
 }

http://git-wip-us.apache.org/repos/asf/cayenne/blob/29d64392/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/AddColumnToModel.java
----------------------------------------------------------------------
diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/AddColumnToModel.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/AddColumnToModel.java
index 6928f6a..823a81d 100644
--- a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/AddColumnToModel.java
+++ b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/AddColumnToModel.java
@@ -23,9 +23,6 @@ import org.apache.cayenne.map.DbAttribute;
 import org.apache.cayenne.map.DbEntity;
 import org.apache.cayenne.map.ObjEntity;
 
-import java.util.Collection;
-import java.util.Collections;
-
 /**
  * A {@link MergerToken} to add a {@link DbAttribute} to a {@link DbEntity}. The
  * {@link EntityMergeSupport} will be used to update the mapped {@link ObjEntity}
@@ -37,8 +34,8 @@ public class AddColumnToModel extends AbstractToModelToken.EntityAndColumn {
     }
 
     @Override
-    public Collection<MergerToken> createReverse(MergerTokenFactory factory) {
-        return Collections.singleton(factory.createDropColumnToDb(getEntity(), getColumn()));
+    public MergerToken createReverse(MergerTokenFactory factory) {
+        return factory.createDropColumnToDb(getEntity(), getColumn());
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/cayenne/blob/29d64392/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/AddRelationshipToDb.java
----------------------------------------------------------------------
diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/AddRelationshipToDb.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/AddRelationshipToDb.java
index 5ca3b24..90d92b0 100644
--- a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/AddRelationshipToDb.java
+++ b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/AddRelationshipToDb.java
@@ -24,7 +24,6 @@ import org.apache.cayenne.dbsync.merge.factory.MergerTokenFactory;
 import org.apache.cayenne.map.DbEntity;
 import org.apache.cayenne.map.DbRelationship;
 
-import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
 
@@ -60,8 +59,8 @@ public class AddRelationshipToDb extends AbstractToDbToken.Entity {
     }
 
     @Override
-    public Collection<MergerToken> createReverse(MergerTokenFactory factory) {
-        return Collections.singleton(factory.createDropRelationshipToModel(getEntity(), relationship));
+    public MergerToken createReverse(MergerTokenFactory factory) {
+        return factory.createDropRelationshipToModel(getEntity(), relationship);
     }
 
     @Override
@@ -72,7 +71,12 @@ public class AddRelationshipToDb extends AbstractToDbToken.Entity {
             return "Skip. No sql representation.";
         }
     }
-    
+
+    @Override
+    public boolean isEmpty() {
+        return !shouldGenerateFkConstraint();
+    }
+
     @Override
     public int compareTo(MergerToken o) {
         // add all AddRelationshipToDb to the end.

http://git-wip-us.apache.org/repos/asf/cayenne/blob/29d64392/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/AddRelationshipToModel.java
----------------------------------------------------------------------
diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/AddRelationshipToModel.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/AddRelationshipToModel.java
index 2e7dbd4..05a3d6c 100644
--- a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/AddRelationshipToModel.java
+++ b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/AddRelationshipToModel.java
@@ -24,9 +24,6 @@ import org.apache.cayenne.map.DbJoin;
 import org.apache.cayenne.map.DbRelationship;
 import org.apache.cayenne.map.ObjEntity;
 
-import java.util.Collection;
-import java.util.Collections;
-
 public class AddRelationshipToModel extends AbstractToModelToken.Entity {
 
     public static final String COMMA_SEPARATOR = ", ";
@@ -55,8 +52,8 @@ public class AddRelationshipToModel extends AbstractToModelToken.Entity {
     }
 
     @Override
-    public Collection<MergerToken> createReverse(MergerTokenFactory factory) {
-        return Collections.singleton(factory.createDropRelationshipToDb(getEntity(), relationship));
+    public MergerToken createReverse(MergerTokenFactory factory) {
+        return factory.createDropRelationshipToDb(getEntity(), relationship);
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/cayenne/blob/29d64392/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/CreateTableToDb.java
----------------------------------------------------------------------
diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/CreateTableToDb.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/CreateTableToDb.java
index 81267c6..9584aaf 100644
--- a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/CreateTableToDb.java
+++ b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/CreateTableToDb.java
@@ -25,7 +25,6 @@ import org.apache.cayenne.map.DbEntity;
 import org.apache.cayenne.validation.SimpleValidationFailure;
 
 import java.util.ArrayList;
-import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
 
@@ -60,8 +59,8 @@ public class CreateTableToDb extends AbstractToDbToken.Entity {
         }
     }
 
-    public Collection<MergerToken> createReverse(MergerTokenFactory factory) {
-        return Collections.singleton(factory.createDropTableToModel(getEntity()));
+    public MergerToken createReverse(MergerTokenFactory factory) {
+        return factory.createDropTableToModel(getEntity());
     }
 
 }

http://git-wip-us.apache.org/repos/asf/cayenne/blob/29d64392/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/CreateTableToModel.java
----------------------------------------------------------------------
diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/CreateTableToModel.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/CreateTableToModel.java
index 7ad863c..8352a27 100644
--- a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/CreateTableToModel.java
+++ b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/CreateTableToModel.java
@@ -24,9 +24,6 @@ import org.apache.cayenne.map.DataMap;
 import org.apache.cayenne.map.DbEntity;
 import org.apache.cayenne.map.ObjEntity;
 
-import java.util.Collection;
-import java.util.Collections;
-
 /**
  * A {@link MergerToken} to add a {@link DbEntity} to a {@link DataMap}
  */
@@ -94,8 +91,8 @@ public class CreateTableToModel extends AbstractToModelToken.Entity {
         context.getDelegate().objEntityAdded(objEntity);
     }
 
-    public Collection<MergerToken> createReverse(MergerTokenFactory factory) {
-        return Collections.singleton(factory.createDropTableToDb(getEntity()));
+    public MergerToken createReverse(MergerTokenFactory factory) {
+        return factory.createDropTableToDb(getEntity());
     }
 
 }

http://git-wip-us.apache.org/repos/asf/cayenne/blob/29d64392/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 a1f7d56..e485a5e 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
@@ -274,13 +274,9 @@ public class DbMerger {
                     }
                 }
 
-                // There is only one FK in the database so we create
-                // DropRelationshipToDb token only for direct relationships
-                // and skip token for toMany relationships
-                if (!detected.isToMany()) {
-                    MergerToken token = tokenFactory.createDropRelationshipToDb(dbEntity, detected);
-                    tokens.add(token);
-                }
+                // Add all relationships. Tokens will decide whether or not to execute
+                MergerToken token = tokenFactory.createDropRelationshipToDb(dbEntity, detected);
+                tokens.add(token);
             }
         }
 
@@ -293,15 +289,9 @@ public class DbMerger {
 
         for (DbRelationship rel : dbEntity.getRelationships()) {
             if (findDbRelationship(detectedEntity, rel) == null) {
+                // Add all relationships. Tokens will decide whether or not to execute
                 AddRelationshipToDb token = (AddRelationshipToDb) tokenFactory.createAddRelationshipToDb(dbEntity, rel);
-
-                if (token.shouldGenerateFkConstraint()) {
-                    // TODO I guess we should add relationship always; in order
-                    // TODO to have ability generate reverse relationship.
-                    // TODO If it doesn't have anything to execute it will be
-                    // TODO passed through execution without any affect on db
-                    tokens.add(token);
-                }
+                tokens.add(token);
             }
         }
 

http://git-wip-us.apache.org/repos/asf/cayenne/blob/29d64392/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/DropColumnToDb.java
----------------------------------------------------------------------
diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/DropColumnToDb.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/DropColumnToDb.java
index f2cd725..19914f0 100644
--- a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/DropColumnToDb.java
+++ b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/DropColumnToDb.java
@@ -18,7 +18,6 @@
  ****************************************************************/
 package org.apache.cayenne.dbsync.merge;
 
-import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
 
@@ -46,8 +45,8 @@ public class DropColumnToDb extends AbstractToDbToken.EntityAndColumn {
         return Collections.singletonList(sqlBuffer.toString());
     }
 
-    public Collection<MergerToken> createReverse(MergerTokenFactory factory) {
-        return Collections.singleton(factory.createAddColumnToModel(getEntity(), getColumn()));
+    public MergerToken createReverse(MergerTokenFactory factory) {
+        return factory.createAddColumnToModel(getEntity(), getColumn());
     }
 
 }

http://git-wip-us.apache.org/repos/asf/cayenne/blob/29d64392/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/DropColumnToModel.java
----------------------------------------------------------------------
diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/DropColumnToModel.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/DropColumnToModel.java
index 8ca0a2a..e92df93 100644
--- a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/DropColumnToModel.java
+++ b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/DropColumnToModel.java
@@ -27,8 +27,6 @@ import org.apache.cayenne.map.ObjAttribute;
 import org.apache.cayenne.map.ObjEntity;
 
 import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
 import java.util.List;
 
 /**
@@ -42,8 +40,8 @@ public class DropColumnToModel extends AbstractToModelToken.EntityAndColumn {
     }
 
     @Override
-    public Collection<MergerToken> createReverse(MergerTokenFactory factory) {
-        return Collections.singleton(factory.createAddColumnToDb(getEntity(), getColumn()));
+    public MergerToken createReverse(MergerTokenFactory factory) {
+        return factory.createAddColumnToDb(getEntity(), getColumn());
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/cayenne/blob/29d64392/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/DropRelationshipToDb.java
----------------------------------------------------------------------
diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/DropRelationshipToDb.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/DropRelationshipToDb.java
index c94236b..0bc74c6 100644
--- a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/DropRelationshipToDb.java
+++ b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/DropRelationshipToDb.java
@@ -25,8 +25,6 @@ import org.apache.cayenne.map.DbEntity;
 import org.apache.cayenne.map.DbRelationship;
 import org.apache.cayenne.dbsync.reverse.db.DbRelationshipDetected;
 
-import java.util.ArrayList;
-import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
 
@@ -49,7 +47,7 @@ public class DropRelationshipToDb extends AbstractToDbToken.Entity {
     @Override
     public List<String> createSql(DbAdapter adapter) {
         String fkName = getFkName();
-        if (fkName == null) {
+        if (fkName == null || relationship.isToMany()) {
             return Collections.emptyList();
         }
 
@@ -58,20 +56,20 @@ public class DropRelationshipToDb extends AbstractToDbToken.Entity {
                 "ALTER TABLE " + context.quotedFullyQualifiedName(getEntity()) + " DROP CONSTRAINT " + fkName);
     }
 
-    public Collection<MergerToken> createReverse(MergerTokenFactory factory) {
-        Collection<MergerToken> result = new ArrayList<>();
-        result.add(factory.createAddRelationshipToModel(getEntity(), relationship));
-        DbRelationship reverse = relationship.getReverseRelationship();
-        if(reverse == null) {
-            reverse = relationship.createReverseRelationship();
-            // NB name will be set in AddRelationshipToModel.execute() call
-            result.add(factory.createAddRelationshipToModel(relationship.getTargetEntity(), reverse));
-        }
-        return result;
+    public MergerToken createReverse(MergerTokenFactory factory) {
+        return factory.createAddRelationshipToModel(getEntity(), relationship);
+    }
+
+    @Override
+    public boolean isEmpty() {
+        return relationship.isToMany();
     }
 
     @Override
     public String getTokenValue() {
+        if(relationship.isToMany()) {
+            return "Skip. No sql representation.";
+        }
         return relationship.getSourceEntity().getName() + "->" + relationship.getTargetEntityName();
     }
 }

http://git-wip-us.apache.org/repos/asf/cayenne/blob/29d64392/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/DropRelationshipToModel.java
----------------------------------------------------------------------
diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/DropRelationshipToModel.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/DropRelationshipToModel.java
index 3ef18fe..3908dbd 100644
--- a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/DropRelationshipToModel.java
+++ b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/DropRelationshipToModel.java
@@ -22,9 +22,6 @@ import org.apache.cayenne.dbsync.merge.factory.MergerTokenFactory;
 import org.apache.cayenne.map.DbEntity;
 import org.apache.cayenne.map.DbRelationship;
 
-import java.util.Collection;
-import java.util.Collections;
-
 public class DropRelationshipToModel extends AbstractToModelToken.Entity {
 
     private final DbRelationship relationship;
@@ -35,8 +32,8 @@ public class DropRelationshipToModel extends AbstractToModelToken.Entity {
     }
 
     @Override
-    public Collection<MergerToken> createReverse(MergerTokenFactory factory) {
-        return Collections.singleton(factory.createAddRelationshipToDb(getEntity(), relationship));
+    public MergerToken createReverse(MergerTokenFactory factory) {
+        return factory.createAddRelationshipToDb(getEntity(), relationship);
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/cayenne/blob/29d64392/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/DropTableToDb.java
----------------------------------------------------------------------
diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/DropTableToDb.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/DropTableToDb.java
index 0fe9f9f..3f416c7 100644
--- a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/DropTableToDb.java
+++ b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/DropTableToDb.java
@@ -23,8 +23,6 @@ import org.apache.cayenne.dbsync.merge.factory.MergerTokenFactory;
 import org.apache.cayenne.map.DbEntity;
 
 import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
 import java.util.List;
 
 public class DropTableToDb extends AbstractToDbToken.Entity {
@@ -46,8 +44,8 @@ public class DropTableToDb extends AbstractToDbToken.Entity {
     }
 
     @Override
-    public Collection<MergerToken> createReverse(MergerTokenFactory factory) {
-        return Collections.singleton(factory.createCreateTableToModel(getEntity()));
+    public MergerToken createReverse(MergerTokenFactory factory) {
+        return factory.createCreateTableToModel(getEntity());
     }
 
 }

http://git-wip-us.apache.org/repos/asf/cayenne/blob/29d64392/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/DropTableToModel.java
----------------------------------------------------------------------
diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/DropTableToModel.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/DropTableToModel.java
index 020edb0..b6eabee 100644
--- a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/DropTableToModel.java
+++ b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/DropTableToModel.java
@@ -23,9 +23,6 @@ import org.apache.cayenne.map.DataMap;
 import org.apache.cayenne.map.DbEntity;
 import org.apache.cayenne.map.ObjEntity;
 
-import java.util.Collection;
-import java.util.Collections;
-
 /**
  * A {@link MergerToken} to remove a {@link DbEntity} from a {@link DataMap}. Any
  * {@link ObjEntity} mapped to the {@link DbEntity} will also be removed.
@@ -38,8 +35,8 @@ public class DropTableToModel extends AbstractToModelToken.Entity {
     }
 
     @Override
-    public Collection<MergerToken> createReverse(MergerTokenFactory factory) {
-        return Collections.singleton(factory.createCreateTableToDb(getEntity()));
+    public MergerToken createReverse(MergerTokenFactory factory) {
+        return factory.createCreateTableToDb(getEntity());
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/cayenne/blob/29d64392/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/DummyReverseToken.java
----------------------------------------------------------------------
diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/DummyReverseToken.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/DummyReverseToken.java
index 0e6d23e..6428b36 100644
--- a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/DummyReverseToken.java
+++ b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/DummyReverseToken.java
@@ -20,9 +20,6 @@ package org.apache.cayenne.dbsync.merge;
 
 import org.apache.cayenne.dbsync.merge.factory.MergerTokenFactory;
 
-import java.util.Collection;
-import java.util.Collections;
-
 /**
  * The reverse of a {@link MergerToken} that can not be reversed.. This will not execute
  * any thing, but {@link #createReverse(MergerTokenFactory)} will get back the reverse that
@@ -37,8 +34,8 @@ class DummyReverseToken implements MergerToken {
     }
 
     @Override
-    public Collection<MergerToken> createReverse(MergerTokenFactory factory) {
-        return Collections.singleton(reverse);
+    public MergerToken createReverse(MergerTokenFactory factory) {
+        return reverse;
     }
 
     @Override
@@ -47,6 +44,11 @@ class DummyReverseToken implements MergerToken {
     }
 
     @Override
+    public boolean isEmpty() {
+        return true;
+    }
+
+    @Override
     public MergeDirection getDirection() {
         return reverse.getDirection().reverseDirection();
     }

http://git-wip-us.apache.org/repos/asf/cayenne/blob/29d64392/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/MergerToken.java
----------------------------------------------------------------------
diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/MergerToken.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/MergerToken.java
index 943a393..b2e7354 100644
--- a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/MergerToken.java
+++ b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/MergerToken.java
@@ -43,7 +43,7 @@ public interface MergerToken {
      * <p>
      * Not all tokens are reversible.
      */
-    Collection<MergerToken> createReverse(MergerTokenFactory factory);
+    MergerToken createReverse(MergerTokenFactory factory);
 
     /**
      * Executes synchronization operation.
@@ -52,4 +52,6 @@ public interface MergerToken {
      */
     void execute(MergerContext context);
 
+    boolean isEmpty();
+
 }

http://git-wip-us.apache.org/repos/asf/cayenne/blob/29d64392/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/SetAllowNullToDb.java
----------------------------------------------------------------------
diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/SetAllowNullToDb.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/SetAllowNullToDb.java
index 1b417ba..68078c2 100644
--- a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/SetAllowNullToDb.java
+++ b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/SetAllowNullToDb.java
@@ -24,7 +24,6 @@ import org.apache.cayenne.dbsync.merge.factory.MergerTokenFactory;
 import org.apache.cayenne.map.DbAttribute;
 import org.apache.cayenne.map.DbEntity;
 
-import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
 
@@ -52,8 +51,8 @@ public class SetAllowNullToDb extends AbstractToDbToken.EntityAndColumn {
     }
 
     @Override
-    public Collection<MergerToken> createReverse(MergerTokenFactory factory) {
-        return Collections.singleton(factory.createSetNotNullToModel(getEntity(), getColumn()));
+    public MergerToken createReverse(MergerTokenFactory factory) {
+        return factory.createSetNotNullToModel(getEntity(), getColumn());
     }
 
 }

http://git-wip-us.apache.org/repos/asf/cayenne/blob/29d64392/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/SetAllowNullToModel.java
----------------------------------------------------------------------
diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/SetAllowNullToModel.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/SetAllowNullToModel.java
index 30841bd..463f912 100644
--- a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/SetAllowNullToModel.java
+++ b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/SetAllowNullToModel.java
@@ -22,9 +22,6 @@ import org.apache.cayenne.dbsync.merge.factory.MergerTokenFactory;
 import org.apache.cayenne.map.DbAttribute;
 import org.apache.cayenne.map.DbEntity;
 
-import java.util.Collection;
-import java.util.Collections;
-
 /**
  * A {@link MergerToken} to set the mandatory field of a {@link DbAttribute} to false
  * 
@@ -36,8 +33,8 @@ public class SetAllowNullToModel extends AbstractToModelToken.EntityAndColumn {
     }
 
     @Override
-    public Collection<MergerToken> createReverse(MergerTokenFactory factory) {
-        return Collections.singleton(factory.createSetNotNullToDb(getEntity(), getColumn()));
+    public MergerToken createReverse(MergerTokenFactory factory) {
+        return factory.createSetNotNullToDb(getEntity(), getColumn());
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/cayenne/blob/29d64392/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/SetColumnTypeToDb.java
----------------------------------------------------------------------
diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/SetColumnTypeToDb.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/SetColumnTypeToDb.java
index a52de9d..2434dd1 100644
--- a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/SetColumnTypeToDb.java
+++ b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/SetColumnTypeToDb.java
@@ -26,7 +26,6 @@ import org.apache.cayenne.dbsync.merge.factory.MergerTokenFactory;
 import org.apache.cayenne.map.DbAttribute;
 import org.apache.cayenne.map.DbEntity;
 
-import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
 
@@ -106,7 +105,7 @@ public class SetColumnTypeToDb extends AbstractToDbToken.Entity {
     }
 
     @Override
-    public Collection<MergerToken> createReverse(MergerTokenFactory factory) {
-        return Collections.singleton(factory.createSetColumnTypeToModel(getEntity(), columnNew, columnOriginal));
+    public MergerToken createReverse(MergerTokenFactory factory) {
+        return factory.createSetColumnTypeToModel(getEntity(), columnNew, columnOriginal);
     }
 }

http://git-wip-us.apache.org/repos/asf/cayenne/blob/29d64392/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/SetColumnTypeToModel.java
----------------------------------------------------------------------
diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/SetColumnTypeToModel.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/SetColumnTypeToModel.java
index 1e51468..a960467 100644
--- a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/SetColumnTypeToModel.java
+++ b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/SetColumnTypeToModel.java
@@ -23,9 +23,6 @@ import org.apache.cayenne.dbsync.merge.factory.MergerTokenFactory;
 import org.apache.cayenne.map.DbAttribute;
 import org.apache.cayenne.map.DbEntity;
 
-import java.util.Collection;
-import java.util.Collections;
-
 /**
  * A {@link MergerToken} that modifies one original {@link DbAttribute} to match another
  * new {@link DbAttribute}s type, maxLength and precision. The name and mandatory fields
@@ -44,8 +41,8 @@ public class SetColumnTypeToModel extends AbstractToModelToken.Entity {
     }
 
     @Override
-    public Collection<MergerToken> createReverse(MergerTokenFactory factory) {
-        return Collections.singleton(factory.createSetColumnTypeToDb(getEntity(), columnNew, columnOriginal));
+    public MergerToken createReverse(MergerTokenFactory factory) {
+        return factory.createSetColumnTypeToDb(getEntity(), columnNew, columnOriginal);
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/cayenne/blob/29d64392/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/SetNotNullToDb.java
----------------------------------------------------------------------
diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/SetNotNullToDb.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/SetNotNullToDb.java
index 1b4c5f6..0591c2a 100644
--- a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/SetNotNullToDb.java
+++ b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/SetNotNullToDb.java
@@ -24,7 +24,6 @@ import org.apache.cayenne.dbsync.merge.factory.MergerTokenFactory;
 import org.apache.cayenne.map.DbAttribute;
 import org.apache.cayenne.map.DbEntity;
 
-import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
 
@@ -46,8 +45,8 @@ public class SetNotNullToDb extends AbstractToDbToken.EntityAndColumn {
     }
 
     @Override
-    public Collection<MergerToken> createReverse(MergerTokenFactory factory) {
-        return Collections.singleton(factory.createSetAllowNullToModel(getEntity(), getColumn()));
+    public MergerToken createReverse(MergerTokenFactory factory) {
+        return factory.createSetAllowNullToModel(getEntity(), getColumn());
     }
 
 }

http://git-wip-us.apache.org/repos/asf/cayenne/blob/29d64392/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/SetNotNullToModel.java
----------------------------------------------------------------------
diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/SetNotNullToModel.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/SetNotNullToModel.java
index 6aa7c63..dfc9b15 100644
--- a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/SetNotNullToModel.java
+++ b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/SetNotNullToModel.java
@@ -22,9 +22,6 @@ import org.apache.cayenne.dbsync.merge.factory.MergerTokenFactory;
 import org.apache.cayenne.map.DbAttribute;
 import org.apache.cayenne.map.DbEntity;
 
-import java.util.Collection;
-import java.util.Collections;
-
 /**
  * A {@link MergerToken} to set the mandatory field of a {@link DbAttribute} to true
  * 
@@ -36,8 +33,8 @@ public class SetNotNullToModel extends AbstractToModelToken.EntityAndColumn {
     }
 
     @Override
-    public Collection<MergerToken> createReverse(MergerTokenFactory factory) {
-        return Collections.singleton(factory.createSetAllowNullToDb(getEntity(), getColumn()));
+    public MergerToken createReverse(MergerTokenFactory factory) {
+        return factory.createSetAllowNullToDb(getEntity(), getColumn());
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/cayenne/blob/29d64392/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/SetPrimaryKeyToDb.java
----------------------------------------------------------------------
diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/SetPrimaryKeyToDb.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/SetPrimaryKeyToDb.java
index e052e1c..077b6ef 100644
--- a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/SetPrimaryKeyToDb.java
+++ b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/SetPrimaryKeyToDb.java
@@ -24,7 +24,10 @@ import org.apache.cayenne.dbsync.merge.factory.MergerTokenFactory;
 import org.apache.cayenne.map.DbAttribute;
 import org.apache.cayenne.map.DbEntity;
 
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
 
 public class SetPrimaryKeyToDb extends AbstractToDbToken.Entity {
 
@@ -77,8 +80,8 @@ public class SetPrimaryKeyToDb extends AbstractToDbToken.Entity {
     }
 
     @Override
-    public Collection<MergerToken> createReverse(MergerTokenFactory factory) {
-        return Collections.singleton(factory.createSetPrimaryKeyToModel(getEntity(), primaryKeyNew, primaryKeyOriginal,
-                detectedPrimaryKeyName));
+    public MergerToken createReverse(MergerTokenFactory factory) {
+        return factory.createSetPrimaryKeyToModel(getEntity(), primaryKeyNew, primaryKeyOriginal,
+                detectedPrimaryKeyName);
     }
 }

http://git-wip-us.apache.org/repos/asf/cayenne/blob/29d64392/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/SetPrimaryKeyToModel.java
----------------------------------------------------------------------
diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/SetPrimaryKeyToModel.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/SetPrimaryKeyToModel.java
index 1048b7f..a2198ba 100644
--- a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/SetPrimaryKeyToModel.java
+++ b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/SetPrimaryKeyToModel.java
@@ -24,7 +24,6 @@ import org.apache.cayenne.map.DbEntity;
 import org.apache.cayenne.map.event.AttributeEvent;
 
 import java.util.Collection;
-import java.util.Collections;
 import java.util.HashSet;
 import java.util.Set;
 
@@ -50,12 +49,12 @@ public class SetPrimaryKeyToModel extends AbstractToModelToken.Entity {
     }
 
     @Override
-    public Collection<MergerToken> createReverse(MergerTokenFactory factory) {
-        return Collections.singleton(factory.createSetPrimaryKeyToDb(
+    public MergerToken createReverse(MergerTokenFactory factory) {
+        return factory.createSetPrimaryKeyToDb(
                 getEntity(),
                 primaryKeyNew,
                 primaryKeyOriginal,
-                detectedPrimaryKeyName));
+                detectedPrimaryKeyName);
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/cayenne/blob/29d64392/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/SetValueForNullToDb.java
----------------------------------------------------------------------
diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/SetValueForNullToDb.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/SetValueForNullToDb.java
index ae231d1..340f2bf 100644
--- a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/SetValueForNullToDb.java
+++ b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/SetValueForNullToDb.java
@@ -23,8 +23,6 @@ import org.apache.cayenne.dbsync.merge.factory.MergerTokenFactory;
 import org.apache.cayenne.map.DbAttribute;
 import org.apache.cayenne.map.DbEntity;
 
-import java.util.Collection;
-import java.util.Collections;
 import java.util.List;
 
 
@@ -43,8 +41,8 @@ public class SetValueForNullToDb extends AbstractToDbToken.EntityAndColumn {
     }
 
     @Override
-    public Collection<MergerToken> createReverse(MergerTokenFactory factory) {
-        return Collections.<MergerToken>singleton(new DummyReverseToken(this));
+    public MergerToken createReverse(MergerTokenFactory factory) {
+        return new DummyReverseToken(this);
     }
     
 }

http://git-wip-us.apache.org/repos/asf/cayenne/blob/29d64392/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/AddColumnToModelIT.java
----------------------------------------------------------------------
diff --git a/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/AddColumnToModelIT.java b/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/AddColumnToModelIT.java
index 0e9ef89..30fa47b 100644
--- a/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/AddColumnToModelIT.java
+++ b/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/AddColumnToModelIT.java
@@ -75,7 +75,7 @@ public class AddColumnToModelIT extends MergeCase {
         assertEquals(1, tokens.size());
         MergerToken token = tokens.get(0);
         if (token.getDirection().isToDb()) {
-            token = token.createReverse(mergerFactory()).iterator().next();
+            token = token.createReverse(mergerFactory());
         }
         assertTrue(token instanceof AddColumnToModel);
         execute(token);

http://git-wip-us.apache.org/repos/asf/cayenne/blob/29d64392/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/CreateTableToModelIT.java
----------------------------------------------------------------------
diff --git a/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/CreateTableToModelIT.java b/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/CreateTableToModelIT.java
index 48a02d5..bac6402 100644
--- a/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/CreateTableToModelIT.java
+++ b/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/CreateTableToModelIT.java
@@ -58,7 +58,7 @@ public class CreateTableToModelIT extends MergeCase {
 		assertEquals(1, tokens.size());
 		MergerToken token = tokens.get(0);
 		if (token.getDirection().isToDb()) {
-			token = token.createReverse(mergerFactory()).iterator().next();
+			token = token.createReverse(mergerFactory());
 		}
 		assertTrue(token.getClass().getName(), token instanceof CreateTableToModel);
 

http://git-wip-us.apache.org/repos/asf/cayenne/blob/29d64392/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/DropColumnToModelIT.java
----------------------------------------------------------------------
diff --git a/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/DropColumnToModelIT.java b/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/DropColumnToModelIT.java
index 4e1b262..f6e0675 100644
--- a/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/DropColumnToModelIT.java
+++ b/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/DropColumnToModelIT.java
@@ -78,7 +78,7 @@ public class DropColumnToModelIT extends MergeCase {
 		assertEquals(1, tokens.size());
 		token = tokens.get(0);
 		if (token.getDirection().isToDb()) {
-			token = token.createReverse(mergerFactory()).iterator().next();
+			token = token.createReverse(mergerFactory());
 		}
 		assertTrue(token instanceof DropColumnToModel);
 		execute(token);
@@ -193,8 +193,8 @@ public class DropColumnToModelIT extends MergeCase {
 		assertTokens(tokens, 2, 0);
 		// TODO: reversing the following two tokens should also reverse the
 		// order
-		MergerToken token0 = tokens.get(0).createReverse(mergerFactory()).iterator().next();
-		MergerToken token1 = tokens.get(1).createReverse(mergerFactory()).iterator().next();
+		MergerToken token0 = tokens.get(0).createReverse(mergerFactory());
+		MergerToken token1 = tokens.get(1).createReverse(mergerFactory());
 		if (!(token0 instanceof DropRelationshipToModel && token1 instanceof DropColumnToModel || token1 instanceof DropRelationshipToModel
 				&& token0 instanceof DropColumnToModel)) {
 			fail();

http://git-wip-us.apache.org/repos/asf/cayenne/blob/29d64392/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/DropRelationshipToModelIT.java
----------------------------------------------------------------------
diff --git a/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/DropRelationshipToModelIT.java b/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/DropRelationshipToModelIT.java
index 41d6dbf..4981581 100644
--- a/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/DropRelationshipToModelIT.java
+++ b/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/DropRelationshipToModelIT.java
@@ -88,7 +88,7 @@ public class DropRelationshipToModelIT extends MergeCase {
 		assertSame(rel1To2, rel2To1.getReverseRelationship());
 		assertSame(rel2To1, rel1To2.getReverseRelationship());
 
-		assertTokensAndExecute(4, 0);
+		assertTokensAndExecute(3, 0);
 		assertTokensAndExecute(0, 0);
 
 		// create ObjEntities
@@ -153,8 +153,8 @@ public class DropRelationshipToModelIT extends MergeCase {
 		assertTokens(tokens, 2, 0);
 		// TODO: reversing the following two tokens should also reverse the
 		// order
-		MergerToken token0 = tokens.get(0).createReverse(mergerFactory()).iterator().next();
-		MergerToken token1 = tokens.get(1).createReverse(mergerFactory()).iterator().next();
+		MergerToken token0 = tokens.get(0).createReverse(mergerFactory());
+		MergerToken token1 = tokens.get(1).createReverse(mergerFactory());
 		if (!(token0 instanceof DropRelationshipToModel && token1 instanceof DropColumnToModel || token1 instanceof DropRelationshipToModel
 				&& token0 instanceof DropColumnToModel)) {
 			fail();

http://git-wip-us.apache.org/repos/asf/cayenne/blob/29d64392/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/DropTableToModelIT.java
----------------------------------------------------------------------
diff --git a/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/DropTableToModelIT.java b/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/DropTableToModelIT.java
index 1d05205..5b1744a 100644
--- a/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/DropTableToModelIT.java
+++ b/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/DropTableToModelIT.java
@@ -72,7 +72,7 @@ public class DropTableToModelIT extends MergeCase {
 		assertEquals(1, tokens.size());
 		token = tokens.get(0);
 		if (token.getDirection().isToDb()) {
-			token = token.createReverse(mergerFactory()).iterator().next();
+			token = token.createReverse(mergerFactory());
 		}
 		assertTrue(token instanceof DropTableToModel);
 		execute(token);

http://git-wip-us.apache.org/repos/asf/cayenne/blob/29d64392/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 0f4f1b1..e764778 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
@@ -119,7 +119,18 @@ public abstract class MergeCase extends DbSyncCase {
             throw new CayenneRuntimeException("Can't doLoad dataMap from db.", e);
         }
 
-        return merger().filters(filters).build().createMergeTokens(map, dbImport);
+        List<MergerToken> tokens = merger().filters(filters).build().createMergeTokens(map, dbImport);
+        return filterEmpty(tokens);
+    }
+
+    private List<MergerToken> filterEmpty(List<MergerToken> tokens) {
+        List<MergerToken> tokensOut = new ArrayList<>();
+        for(MergerToken token : tokens) {
+            if(!token.isEmpty()) {
+                tokensOut.add(token);
+            }
+        }
+        return tokensOut;
     }
 
     /**

http://git-wip-us.apache.org/repos/asf/cayenne/blob/29d64392/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/TokensReverseTest.java
----------------------------------------------------------------------
diff --git a/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/TokensReverseTest.java b/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/TokensReverseTest.java
index 34cd7cb..bde6c88 100644
--- a/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/TokensReverseTest.java
+++ b/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/TokensReverseTest.java
@@ -52,8 +52,8 @@ public class TokensReverseTest {
         testOneToOneReverse(factory().createDropColumnToModel(entity, attr));
 
         testOneToOneReverse(factory().createAddRelationshipToDb(entity, rel));
-        testCreateAddRelationshipToModel(factory().createAddRelationshipToModel(entity, rel));
-        testCreateDropRelationshipToDb(factory().createDropRelationshipToDb(entity, rel));
+        testOneToOneReverse(factory().createAddRelationshipToModel(entity, rel));
+        testOneToOneReverse(factory().createDropRelationshipToDb(entity, rel));
         testOneToOneReverse(factory().createDropRelationshipToModel(entity, rel));
 
         testOneToOneReverse(factory().createCreateTableToDb(entity));
@@ -76,32 +76,14 @@ public class TokensReverseTest {
         testOneToOneReverse(factory().createSetValueForNullToDb(entity, attr, new DefaultValueForNullProvider()));
     }
 
-    private void testReversTokenWithCount(MergerToken token, int countFirstReverse, int countSecondReverse) {
-        Collection<MergerToken> collectionReverse1 = token.createReverse(factory());
-        Assert.assertEquals(countFirstReverse, collectionReverse1.size());
-
-        Collection<MergerToken> collectionReverse2 = collectionReverse1.iterator().next().createReverse(factory());
-        Assert.assertEquals(countSecondReverse, collectionReverse2.size());
-
-        MergerToken token2 = collectionReverse2.iterator().next();
+    private void testOneToOneReverse(MergerToken token) {
+        MergerToken token2 = token.createReverse(factory()).createReverse(factory());
 
         Assert.assertEquals(token.getTokenName(), token2.getTokenName());
         Assert.assertEquals(token.getTokenValue(), token2.getTokenValue());
         Assert.assertEquals(token.getDirection(), token2.getDirection());
     }
 
-    private void testCreateAddRelationshipToModel(MergerToken token1) {
-        testReversTokenWithCount(token1, 1, 2);
-    }
-
-    private void testCreateDropRelationshipToDb(MergerToken token1) {
-        testReversTokenWithCount(token1, 2, 1);
-    }
-
-    private void testOneToOneReverse(MergerToken token1) {
-        testReversTokenWithCount(token1, 1, 1);
-    }
-
     private MergerTokenFactory factory() {
         return new HSQLMergerTokenFactory();
     }

http://git-wip-us.apache.org/repos/asf/cayenne/blob/29d64392/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 7f05d8c..e5b393f 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
@@ -117,7 +117,7 @@ public class DefaultDbImportAction implements DbImportAction {
     /**
      * Flattens many-to-many relationships in the generated model.
      */
-    protected static void flattenManyToManyRelationships(DataMap map, Collection<ObjEntity> loadedObjEntities,
+    public static void flattenManyToManyRelationships(DataMap map, Collection<ObjEntity> loadedObjEntities,
                                                          ObjectNameGenerator objectNameGenerator) {
         if (loadedObjEntities.isEmpty()) {
             return;
@@ -297,7 +297,7 @@ public class DefaultDbImportAction implements DbImportAction {
             if (token instanceof AbstractToModelToken) {
                 continue;
             }
-            tokens.addAll(token.createReverse(mergerTokenFactory));
+            tokens.add(token.createReverse(mergerTokenFactory));
         }
         return tokens;
     }

http://git-wip-us.apache.org/repos/asf/cayenne/blob/29d64392/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/db/MergerOptions.java
----------------------------------------------------------------------
diff --git a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/db/MergerOptions.java b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/db/MergerOptions.java
index 4ee133c..06260b6 100644
--- a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/db/MergerOptions.java
+++ b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/db/MergerOptions.java
@@ -29,6 +29,7 @@ import org.apache.cayenne.dbsync.merge.MergeDirection;
 import org.apache.cayenne.dbsync.merge.MergerContext;
 import org.apache.cayenne.dbsync.merge.MergerToken;
 import org.apache.cayenne.dbsync.merge.ModelMergeDelegate;
+import org.apache.cayenne.dbsync.merge.ProxyModelMergeDelegate;
 import org.apache.cayenne.dbsync.merge.factory.MergerTokenFactory;
 import org.apache.cayenne.dbsync.merge.factory.MergerTokenFactoryProvider;
 import org.apache.cayenne.dbsync.naming.DefaultObjectNameGenerator;
@@ -58,6 +59,7 @@ import org.apache.cayenne.project.Project;
 import org.apache.cayenne.resource.Resource;
 import org.apache.cayenne.swing.BindingBuilder;
 import org.apache.cayenne.swing.ObjectBinding;
+import org.apache.cayenne.tools.dbimport.DefaultDbImportAction;
 import org.apache.cayenne.validation.ValidationResult;
 import org.apache.commons.logging.LogFactory;
 
@@ -72,7 +74,9 @@ import java.io.IOException;
 import java.io.PrintWriter;
 import java.sql.Connection;
 import java.sql.SQLException;
+import java.util.Collection;
 import java.util.Iterator;
+import java.util.LinkedList;
 import java.util.List;
 
 public class MergerOptions extends CayenneController {
@@ -276,209 +280,95 @@ public class MergerOptions extends CayenneController {
             return;
         }
 
-        final ProjectController c = getProjectController();
-
-        final Object src = this;
-        final DataChannelDescriptor domain = (DataChannelDescriptor) getProjectController()
-                .getProject()
-                .getRootNode();
-        final DataNodeDescriptor node = getProjectController().getCurrentDataNode();
-
-        ModelMergeDelegate delegate = new ModelMergeDelegate() {
-
-            public void dbAttributeAdded(DbAttribute att) {
-                if (c.getCurrentDbEntity() == att.getEntity()) {
-                    c.fireDbAttributeDisplayEvent(new AttributeDisplayEvent(src, att, att
-                            .getEntity(), dataMap, domain));
-                }
-            }
-
-            public void dbAttributeModified(DbAttribute att) {
-                if (c.getCurrentDbEntity() == att.getEntity()) {
-                    c.fireDbAttributeDisplayEvent(new AttributeDisplayEvent(src, att, att
-                            .getEntity(), dataMap, domain));
-                }
-            }
-
-            public void dbAttributeRemoved(DbAttribute att) {
-                if (c.getCurrentDbEntity() == att.getEntity()) {
-                    c.fireDbAttributeDisplayEvent(new AttributeDisplayEvent(src, att, att
-                            .getEntity(), dataMap, domain));
-                }
-            }
-
-            public void dbEntityAdded(DbEntity ent) {
-                c.fireDbEntityEvent(new EntityEvent(src, ent, MapEvent.ADD));
-                c.fireDbEntityDisplayEvent(new EntityDisplayEvent(
-                        src,
-                        ent,
-                        dataMap,
-                        node,
-                        domain));
-            }
+        DataSource dataSource;
+        try {
+            dataSource = connectionInfo.makeDataSource(getApplication()
+                    .getClassLoadingService());
+        } catch (SQLException ex) {
+            reportError("Migration Error", ex);
+            return;
+        }
 
-            public void dbEntityRemoved(DbEntity ent) {
-                c.fireDbEntityEvent(new EntityEvent(src, ent, MapEvent.REMOVE));
-                c.fireDbEntityDisplayEvent(new EntityDisplayEvent(
-                        src,
-                        ent,
-                        dataMap,
-                        node,
-                        domain));
-            }
+        final Collection<ObjEntity> loadedObjEntities = new LinkedList<>();
 
-            public void dbRelationshipAdded(DbRelationship rel) {
-                if (c.getCurrentDbEntity() == rel.getSourceEntity()) {
-                    c.fireDbRelationshipDisplayEvent(new RelationshipDisplayEvent(
-                            src,
-                            rel,
-                            rel.getSourceEntity(),
-                            dataMap,
-                            domain));
-                }
-            }
+        MergerContext mergerContext = MergerContext.builder(dataMap)
+                .syntheticDataNode(dataSource, adapter)
+                .delegate(createDelegate(loadedObjEntities))
+                .build();
 
-            public void dbRelationshipRemoved(DbRelationship rel) {
-                if (c.getCurrentDbEntity() == rel.getSourceEntity()) {
-                    c.fireDbRelationshipDisplayEvent(new RelationshipDisplayEvent(
-                            src,
-                            rel,
-                            rel.getSourceEntity(),
-                            dataMap,
-                            domain));
-                }
-            }
+        boolean modelChanged = applyTokens(tokensToMigrate, mergerContext);
 
-            public void objAttributeAdded(ObjAttribute att) {
-                if (c.getCurrentObjEntity() == att.getEntity()) {
-                    c.fireObjAttributeDisplayEvent(new AttributeDisplayEvent(
-                            src,
-                            att,
-                            att.getEntity(),
-                            dataMap,
-                            domain));
-                }
-            }
+        DefaultDbImportAction.flattenManyToManyRelationships(dataMap, loadedObjEntities, mergerContext.getNameGenerator());
 
-            public void objAttributeModified(ObjAttribute att) {
-                if (c.getCurrentObjEntity() == att.getEntity()) {
-                    c.fireObjAttributeDisplayEvent(new AttributeDisplayEvent(
-                            src,
-                            att,
-                            att.getEntity(),
-                            dataMap,
-                            domain));
-                }
-            }
+        notifyProjectModified(modelChanged);
 
-            public void objAttributeRemoved(ObjAttribute att) {
-                if (c.getCurrentObjEntity() == att.getEntity()) {
-                    c.fireObjAttributeDisplayEvent(new AttributeDisplayEvent(
-                            src,
-                            att,
-                            att.getEntity(),
-                            dataMap,
-                            domain));
-                }
-            }
+        reportFailures(mergerContext);
+    }
 
+    private ModelMergeDelegate createDelegate(final Collection<ObjEntity> loadedObjEntities) {
+        return new ProxyModelMergeDelegate(
+                new MigrateModelMergeDelegate(getProjectController(), this)) {
+            @Override
             public void objEntityAdded(ObjEntity ent) {
-                c.fireObjEntityEvent(new EntityEvent(src, ent, MapEvent.ADD));
-                c.fireObjEntityDisplayEvent(new EntityDisplayEvent(
-                        src,
-                        ent,
-                        dataMap,
-                        node,
-                        domain));
+                loadedObjEntities.add(ent);
+                super.objEntityAdded(ent);
             }
-
-            public void objEntityRemoved(ObjEntity ent) {
-                c.fireObjEntityEvent(new EntityEvent(src, ent, MapEvent.REMOVE));
-                c.fireObjEntityDisplayEvent(new EntityDisplayEvent(
-                        src,
-                        ent,
-                        dataMap,
-                        node,
-                        domain));
-            }
-
-            public void objRelationshipAdded(ObjRelationship rel) {
-                if (c.getCurrentObjEntity() == rel.getSourceEntity()) {
-                    c.fireObjRelationshipDisplayEvent(new RelationshipDisplayEvent(
-                            src,
-                            rel,
-                            rel.getSourceEntity(),
-                            dataMap,
-                            domain));
-                }
-            }
-
-            public void objRelationshipRemoved(ObjRelationship rel) {
-                if (c.getCurrentObjEntity() == rel.getSourceEntity()) {
-                    c.fireObjRelationshipDisplayEvent(new RelationshipDisplayEvent(
-                            src,
-                            rel,
-                            rel.getSourceEntity(),
-                            dataMap,
-                            domain));
-                }
-            }
-
         };
+    }
 
-        try {
-            DataSource dataSource = connectionInfo.makeDataSource(getApplication()
-                    .getClassLoadingService());
-
-            MergerContext mergerContext = MergerContext.builder(dataMap)
-                    .syntheticDataNode(dataSource, adapter)
-                    .delegate(delegate)
-                    .build();
+    private boolean applyTokens(List<MergerToken> tokensToMigrate, MergerContext mergerContext) {
+        boolean modelChanged = false;
 
-            boolean modelChanged = false;
+        try {
             for (MergerToken tok : tokensToMigrate) {
-                int numOfFailuresBefore = mergerContext
-                        .getValidationResult()
-                        .getFailures()
-                        .size();
+                int numOfFailuresBefore = getFailuresCount(mergerContext);
+
                 tok.execute(mergerContext);
+
                 if (!modelChanged && tok.getDirection().equals(MergeDirection.TO_MODEL)) {
                     modelChanged = true;
                 }
-
-                if (numOfFailuresBefore == mergerContext
-                        .getValidationResult()
-                        .getFailures()
-                        .size()) {
+                if (numOfFailuresBefore == getFailuresCount(mergerContext)) {
                     // looks like the token executed without failures
                     tokens.removeToken(tok);
                 }
             }
+        } catch (Throwable th) {
+            reportError("Migration Error", th);
+        }
 
-            if (modelChanged) {
-                // mark the model as unsaved
-                Project project = getApplication().getProject();
-                project.setModified(true);
+        return modelChanged;
+    }
 
-                ProjectController projectController = getApplication()
-                        .getFrameController()
-                        .getProjectController();
-                projectController.setDirty(true);
-            }
+    private int getFailuresCount(MergerContext mergerContext) {
+        return mergerContext.getValidationResult().getFailures().size();
+    }
 
-            ValidationResult failures = mergerContext.getValidationResult();
+    private void reportFailures(MergerContext mergerContext) {
+        ValidationResult failures = mergerContext.getValidationResult();
+        if (failures == null || !failures.hasFailures()) {
+            JOptionPane.showMessageDialog(getView(), "Migration Complete.");
+        } else {
+            new ValidationResultBrowser(this).startupAction(
+                    "Migration Complete",
+                    "Migration finished. The following problem(s) were ignored.",
+                    failures);
+        }
+    }
 
-            if (failures == null || !failures.hasFailures()) {
-                JOptionPane.showMessageDialog(getView(), "Migration Complete.");
-            } else {
-                new ValidationResultBrowser(this).startupAction(
-                        "Migration Complete",
-                        "Migration finished. The following problem(s) were ignored.",
-                        failures);
-            }
-        } catch (Throwable th) {
-            reportError("Migration Error", th);
+    private void notifyProjectModified(boolean modelChanged) {
+        if(!modelChanged) {
+            return;
         }
+
+        // mark the model as unsaved
+        Project project = getApplication().getProject();
+        project.setModified(true);
+
+        ProjectController projectController = getApplication()
+                .getFrameController()
+                .getProjectController();
+        projectController.setDirty(true);
     }
 
     /**
@@ -518,4 +408,160 @@ public class MergerOptions extends CayenneController {
     public void closeAction() {
         view.dispose();
     }
+
+    private class MigrateModelMergeDelegate implements ModelMergeDelegate {
+
+        private final ProjectController controller;
+        private final Object src;
+        private final DataChannelDescriptor domain;
+        private final DataNodeDescriptor node;
+
+        public MigrateModelMergeDelegate(ProjectController controller, Object src) {
+            this.controller = controller;
+            this.src = src;
+            domain = (DataChannelDescriptor) getProjectController()
+                    .getProject()
+                    .getRootNode();
+            node = getProjectController().getCurrentDataNode();
+        }
+
+        public void dbAttributeAdded(DbAttribute att) {
+            if (controller.getCurrentDbEntity() == att.getEntity()) {
+                controller.fireDbAttributeDisplayEvent(new AttributeDisplayEvent(src, att, att
+                        .getEntity(), dataMap, domain));
+            }
+        }
+
+        public void dbAttributeModified(DbAttribute att) {
+            if (controller.getCurrentDbEntity() == att.getEntity()) {
+                controller.fireDbAttributeDisplayEvent(new AttributeDisplayEvent(src, att, att
+                        .getEntity(), dataMap, domain));
+            }
+        }
+
+        public void dbAttributeRemoved(DbAttribute att) {
+            if (controller.getCurrentDbEntity() == att.getEntity()) {
+                controller.fireDbAttributeDisplayEvent(new AttributeDisplayEvent(src, att, att
+                        .getEntity(), dataMap, domain));
+            }
+        }
+
+        public void dbEntityAdded(DbEntity ent) {
+            controller.fireDbEntityEvent(new EntityEvent(src, ent, MapEvent.ADD));
+            controller.fireDbEntityDisplayEvent(new EntityDisplayEvent(
+                    src,
+                    ent,
+                    dataMap,
+                    node,
+                    domain));
+        }
+
+        public void dbEntityRemoved(DbEntity ent) {
+            controller.fireDbEntityEvent(new EntityEvent(src, ent, MapEvent.REMOVE));
+            controller.fireDbEntityDisplayEvent(new EntityDisplayEvent(
+                    src,
+                    ent,
+                    dataMap,
+                    node,
+                    domain));
+        }
+
+        public void dbRelationshipAdded(DbRelationship rel) {
+            if (controller.getCurrentDbEntity() == rel.getSourceEntity()) {
+                controller.fireDbRelationshipDisplayEvent(new RelationshipDisplayEvent(
+                        src,
+                        rel,
+                        rel.getSourceEntity(),
+                        dataMap,
+                        domain));
+            }
+        }
+
+        public void dbRelationshipRemoved(DbRelationship rel) {
+            if (controller.getCurrentDbEntity() == rel.getSourceEntity()) {
+                controller.fireDbRelationshipDisplayEvent(new RelationshipDisplayEvent(
+                        src,
+                        rel,
+                        rel.getSourceEntity(),
+                        dataMap,
+                        domain));
+            }
+        }
+
+        public void objAttributeAdded(ObjAttribute att) {
+            if (controller.getCurrentObjEntity() == att.getEntity()) {
+                controller.fireObjAttributeDisplayEvent(new AttributeDisplayEvent(
+                        src,
+                        att,
+                        att.getEntity(),
+                        dataMap,
+                        domain));
+            }
+        }
+
+        public void objAttributeModified(ObjAttribute att) {
+            if (controller.getCurrentObjEntity() == att.getEntity()) {
+                controller.fireObjAttributeDisplayEvent(new AttributeDisplayEvent(
+                        src,
+                        att,
+                        att.getEntity(),
+                        dataMap,
+                        domain));
+            }
+        }
+
+        public void objAttributeRemoved(ObjAttribute att) {
+            if (controller.getCurrentObjEntity() == att.getEntity()) {
+                controller.fireObjAttributeDisplayEvent(new AttributeDisplayEvent(
+                        src,
+                        att,
+                        att.getEntity(),
+                        dataMap,
+                        domain));
+            }
+        }
+
+        public void objEntityAdded(ObjEntity ent) {
+            controller.fireObjEntityEvent(new EntityEvent(src, ent, MapEvent.ADD));
+            controller.fireObjEntityDisplayEvent(new EntityDisplayEvent(
+                    src,
+                    ent,
+                    dataMap,
+                    node,
+                    domain));
+        }
+
+        public void objEntityRemoved(ObjEntity ent) {
+            controller.fireObjEntityEvent(new EntityEvent(src, ent, MapEvent.REMOVE));
+            controller.fireObjEntityDisplayEvent(new EntityDisplayEvent(
+                    src,
+                    ent,
+                    dataMap,
+                    node,
+                    domain));
+        }
+
+        public void objRelationshipAdded(ObjRelationship rel) {
+            if (controller.getCurrentObjEntity() == rel.getSourceEntity()) {
+                controller.fireObjRelationshipDisplayEvent(new RelationshipDisplayEvent(
+                        src,
+                        rel,
+                        rel.getSourceEntity(),
+                        dataMap,
+                        domain));
+            }
+        }
+
+        public void objRelationshipRemoved(ObjRelationship rel) {
+            if (controller.getCurrentObjEntity() == rel.getSourceEntity()) {
+                controller.fireObjRelationshipDisplayEvent(new RelationshipDisplayEvent(
+                        src,
+                        rel,
+                        rel.getSourceEntity(),
+                        dataMap,
+                        domain));
+            }
+        }
+
+    }
 }

http://git-wip-us.apache.org/repos/asf/cayenne/blob/29d64392/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/db/MergerTokenSelectorController.java
----------------------------------------------------------------------
diff --git a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/db/MergerTokenSelectorController.java b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/db/MergerTokenSelectorController.java
index 8c36d25..e23f780 100644
--- a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/db/MergerTokenSelectorController.java
+++ b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/db/MergerTokenSelectorController.java
@@ -196,7 +196,7 @@ public class MergerTokenSelectorController extends CayenneController {
         }
         int i = selectableTokensList.indexOf(token);
         // TODO change to collection iteration
-        MergerToken reverse = token.createReverse(mergerTokenFactory).iterator().next();
+        MergerToken reverse = token.createReverse(mergerTokenFactory);
         selectableTokensList.set(i, reverse);
         if (excludedTokens.remove(token)) {
             excludedTokens.add(reverse);
@@ -228,7 +228,7 @@ public class MergerTokenSelectorController extends CayenneController {
         for (int i = 0; i < selectableTokensList.size(); i++) {
             MergerToken token = selectableTokensList.get(i);
             // TODO change to collection iteration
-            MergerToken reverse = token.createReverse(mergerTokenFactory).iterator().next();
+            MergerToken reverse = token.createReverse(mergerTokenFactory);
             selectableTokensList.set(i, reverse);
             if (excludedTokens.remove(token)) {
                 excludedTokens.add(reverse);