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

[08/16] cayenne git commit: New DbMerger for dbsync utils - merger process split in independent steps - new attributes compared (full type comparision of DbAttribute)

New DbMerger for dbsync utils
 - merger process split in independent steps
 - new attributes compared (full type comparision of DbAttribute)

Clean up and tests


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

Branch: refs/heads/master
Commit: d5707c75203cae33c7c0580b59effd7dcb1b6f66
Parents: c86094f
Author: Nikita Timofeev <st...@gmail.com>
Authored: Mon Dec 12 12:59:58 2016 +0300
Committer: Nikita Timofeev <st...@gmail.com>
Committed: Mon Dec 12 12:59:58 2016 +0300

----------------------------------------------------------------------
 .../apache/cayenne/dbsync/merge/DbMerger.java   | 402 -------------------
 .../cayenne/dbsync/merge/DataMapMergerTest.java | 387 ++++++++++++++++++
 .../cayenne/dbsync/merge/DbMergerTest.java      | 387 ------------------
 3 files changed, 387 insertions(+), 789 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cayenne/blob/d5707c75/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
deleted file mode 100644
index 9da71b5..0000000
--- a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/DbMerger.java
+++ /dev/null
@@ -1,402 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- *    or more contributor license agreements.  See the NOTICE file
- *    distributed with this work for additional information
- *    regarding copyright ownership.  The ASF licenses this file
- *    to you under the Apache License, Version 2.0 (the
- *    "License"); you may not use this file except in compliance
- *    with the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- *    Unless required by applicable law or agreed to in writing,
- *    software distributed under the License is distributed on an
- *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- *    KIND, either express or implied.  See the License for the
- *    specific language governing permissions and limitations
- *    under the License.
- */
-package org.apache.cayenne.dbsync.merge;
-
-import org.apache.cayenne.dbsync.merge.factory.MergerTokenFactory;
-import org.apache.cayenne.dbsync.merge.token.AddRelationshipToDb;
-import org.apache.cayenne.dbsync.merge.token.EmptyValueForNullProvider;
-import org.apache.cayenne.dbsync.merge.token.MergerToken;
-import org.apache.cayenne.dbsync.merge.token.TokenComparator;
-import org.apache.cayenne.dbsync.merge.token.ValueForNullProvider;
-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.map.Attribute;
-import org.apache.cayenne.map.DataMap;
-import org.apache.cayenne.map.DbAttribute;
-import org.apache.cayenne.map.DbEntity;
-import org.apache.cayenne.map.DbJoin;
-import org.apache.cayenne.map.DbRelationship;
-import org.apache.cayenne.map.DetectedDbEntity;
-
-import java.sql.Types;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Objects;
-import java.util.Set;
-
-/**
- * Wraps an algorithm to traverse a {@link DataMap} and create a group of {@link MergerToken}s that can be used to
- * synchronize data store and Cayenne model.
- * @deprecated
- */
-public class DbMerger {
-
-    private MergerTokenFactory tokenFactory;
-    private ValueForNullProvider valueForNull;
-    private boolean skipRelationshipsTokens;
-    private boolean skipPKTokens;
-    private FiltersConfig filters;
-
-    private DbMerger() {
-    }
-
-    public static Builder builder(MergerTokenFactory tokenFactory) {
-        return new Builder(tokenFactory);
-    }
-
-    public static DbMerger build(MergerTokenFactory tokenFactory) {
-        return builder(tokenFactory).build();
-    }
-
-    /**
-     * 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 MergerTokens that represent the difference between two {@link DataMap} objects.
-     */
-    public List<MergerToken> createMergeTokens(DataMap dataMap, DataMap dbImport) {
-
-        dbImport.setQuotingSQLIdentifiers(dataMap.isQuotingSQLIdentifiers());
-
-        List<MergerToken> tokens = createMergeTokens(filter(dataMap, filters), dbImport.getDbEntities());
-
-        // sort
-        Collections.sort(tokens, new TokenComparator());
-
-        return tokens;
-    }
-
-    private Collection<DbEntity> filter(DataMap existing, FiltersConfig filtersConfig) {
-        Collection<DbEntity> existingFiltered = new LinkedList<>();
-        for (DbEntity entity : existing.getDbEntities()) {
-            TableFilter tableFilter = filtersConfig.tableFilter(entity.getCatalog(), entity.getSchema());
-            if (tableFilter != null && tableFilter.isIncludeTable(entity.getName()) != null) {
-                existingFiltered.add(entity);
-            }
-        }
-        return existingFiltered;
-    }
-
-    protected List<MergerToken> createMergeTokens(Collection<DbEntity> entities, Collection<DbEntity> dbImportedEntities) {
-        Collection<DbEntity> dbEntitiesToDrop = new LinkedList<>(dbImportedEntities);
-
-        List<MergerToken> tokens = new LinkedList<>();
-        for (DbEntity dbEntity : entities) {
-            String tableName = dbEntity.getName();
-
-            // look for table
-            DbEntity detectedEntity = findDbEntity(dbImportedEntities, tableName);
-            if (detectedEntity == null) {
-                tokens.add(tokenFactory.createCreateTableToDb(dbEntity));
-                // TODO: does this work properly with createReverse?
-                for (DbRelationship rel : dbEntity.getRelationships()) {
-                    tokens.add(tokenFactory.createAddRelationshipToDb(dbEntity, rel));
-                }
-                continue;
-            }
-
-            dbEntitiesToDrop.remove(detectedEntity);
-
-            tokens.addAll(checkRelationshipsToDrop(dbEntity, detectedEntity));
-            if (!skipRelationshipsTokens) {
-                tokens.addAll(checkRelationshipsToAdd(dbEntity, detectedEntity));
-            }
-            tokens.addAll(checkRows(dbEntity, detectedEntity));
-
-            if (!skipPKTokens) {
-                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(tokenFactory.createDropTableToDb(e));
-            for (DbRelationship relationship : e.getRelationships()) {
-                DbEntity detectedEntity = findDbEntity(entities, relationship.getTargetEntityName());
-                if (detectedEntity != null) {
-                    tokens.add(tokenFactory.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(tokenFactory.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(tokenFactory.createAddColumnToDb(existing, attr));
-                if (attr.isMandatory()) {
-                    if (valueForNull.hasValueFor(existing, attr)) {
-                        tokens.add(tokenFactory.createSetValueForNullToDb(existing, attr, valueForNull));
-                    }
-                    tokens.add(tokenFactory.createSetNotNullToDb(existing, attr));
-                }
-                continue;
-            }
-
-            // check for not null
-            if (attr.isMandatory() != detected.isMandatory()) {
-                if (attr.isMandatory()) {
-                    if (valueForNull.hasValueFor(existing, attr)) {
-                        tokens.add(tokenFactory.createSetValueForNullToDb(existing, attr, valueForNull));
-                    }
-                    tokens.add(tokenFactory.createSetNotNullToDb(existing, attr));
-                } else {
-                    tokens.add(tokenFactory.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(tokenFactory.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());
-                    }
-                }
-
-                // Add all relationships. Tokens will decide whether or not to execute
-                MergerToken token = tokenFactory.createDropRelationshipToDb(dbEntity, detected);
-                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) {
-                // Add all relationships. Tokens will decide whether or not to execute
-                AddRelationshipToDb token = (AddRelationshipToDb) tokenFactory.createAddRelationshipToDb(dbEntity, rel);
-                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 tokenFactory.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;
-    }
-
-    @Deprecated
-    public static class Builder {
-        private DbMerger merger;
-
-        private Builder(MergerTokenFactory tokenFactory) {
-            this.merger = new DbMerger();
-            this.merger.tokenFactory = Objects.requireNonNull(tokenFactory);
-        }
-
-        public DbMerger build() {
-
-            if (merger.valueForNull == null) {
-                merger.valueForNull = new EmptyValueForNullProvider();
-            }
-
-            if (merger.filters == null) {
-                // default: match all tables, no stored procedures
-                merger.filters = FiltersConfig.create(null, null, TableFilter.everything(), PatternFilter.INCLUDE_NOTHING);
-            }
-
-            return merger;
-        }
-
-        public Builder valueForNullProvider(ValueForNullProvider provider) {
-            merger.valueForNull = provider;
-            return this;
-        }
-
-        public Builder skipRelationshipsTokens(boolean flag) {
-            merger.skipRelationshipsTokens = flag;
-            return this;
-        }
-
-        public Builder skipPKTokens(boolean flag) {
-            merger.skipPKTokens = flag;
-            return this;
-        }
-
-        public Builder filters(FiltersConfig filters) {
-            merger.filters = Objects.requireNonNull(filters);
-            return this;
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/d5707c75/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/DataMapMergerTest.java
----------------------------------------------------------------------
diff --git a/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/DataMapMergerTest.java b/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/DataMapMergerTest.java
new file mode 100644
index 0000000..c70752a
--- /dev/null
+++ b/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/DataMapMergerTest.java
@@ -0,0 +1,387 @@
+/*****************************************************************
+ *   Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ ****************************************************************/
+package org.apache.cayenne.dbsync.merge;
+
+import org.apache.cayenne.dbsync.merge.builders.DbEntityBuilder;
+import org.apache.cayenne.dbsync.merge.factory.HSQLMergerTokenFactory;
+import org.apache.cayenne.dbsync.merge.token.MergerToken;
+import org.apache.cayenne.dbsync.merge.token.SetColumnTypeToDb;
+import org.apache.cayenne.map.DataMap;
+import org.apache.cayenne.map.DbEntity;
+import org.junit.Test;
+
+import java.util.List;
+
+import static org.apache.cayenne.dbsync.merge.builders.ObjectMother.dataMap;
+import static org.apache.cayenne.dbsync.merge.builders.ObjectMother.dbAttr;
+import static org.apache.cayenne.dbsync.merge.builders.ObjectMother.dbEntity;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+public class DataMapMergerTest {
+
+    @Test
+    public void testEmptyDataMap() throws Exception {
+        DataMap existing = dataMap().build();
+        DataMap db = dataMap().build();
+        assertEquals(0, dbMerger().createMergeTokens(existing, db).size());
+    }
+
+    @Test
+    public void testAddTable() throws Exception {
+        DbEntityBuilder dbEntity =
+            dbEntity("table1").attributes(
+                dbAttr("attr01").typeInt()
+        );
+        DataMap existing = dataMap().with(dbEntity).build();
+        DataMap db = dataMap().build();
+
+        List<MergerToken> tokens = dbMerger().createMergeTokens(existing, db);
+
+        assertEquals(1, tokens.size());
+        assertEquals(factory().createCreateTableToDb(dbEntity.build()).getTokenValue(),
+                     tokens.get(0).getTokenValue());
+    }
+
+    @Test
+    public void testRemoveTable() throws Exception {
+        DataMap existing = dataMap().build();
+        DataMap db = dataMap().with(
+            dbEntity("table1").attributes(
+                dbAttr("attr01").typeInt()
+        )).build();
+
+        List<MergerToken> tokens = dbMerger().createMergeTokens(existing, db);
+
+        assertEquals(1, tokens.size());
+        assertEquals(factory().createDropTableToDb(db.getDbEntity("table1")).getTokenValue(),
+                     tokens.get(0).getTokenValue());
+    }
+
+    @Test
+    public void testAddColumn() throws Exception {
+        DataMap existing = dataMap().with(
+            dbEntity("table1").attributes(
+                dbAttr("attr01").typeInt(),
+                dbAttr("attr02").typeInt()
+        )).build();
+
+        DataMap db = dataMap().with(
+            dbEntity("table1").attributes(
+                dbAttr("attr01").typeInt()
+        )).build();
+
+        List<MergerToken> tokens = dbMerger().createMergeTokens(existing, db);
+
+        assertEquals(1, tokens.size());
+
+        DbEntity entity = existing.getDbEntity("table1");
+        assertEquals(factory().createAddColumnToDb(entity, entity.getAttribute("attr02")).getTokenValue(),
+                     tokens.get(0).getTokenValue());
+    }
+
+    @Test
+    public void testChangeColumnTypeSimple() throws Exception {
+        DataMap existing = dataMap().with(
+                dbEntity("table1").attributes(
+                        dbAttr("attr01").typeInt()
+                )).build();
+
+        DataMap db = dataMap().with(
+                dbEntity("table1").attributes(
+                        dbAttr("attr01").typeVarchar(30)
+                )).build();
+
+        List<MergerToken> tokens = dbMerger().createMergeTokens(existing, db);
+        assertEquals(1, tokens.size());
+
+        DbEntity entity = existing.getDbEntity("table1");
+        DbEntity entityDb = db.getDbEntity("table1");
+        assertTrue(tokens.get(0) instanceof SetColumnTypeToDb);
+
+        assertEquals(
+                factory()
+                        .createSetColumnTypeToDb(entity, entityDb.getAttribute("attr01"), entity.getAttribute("attr01"))
+                        .getTokenValue(),
+                tokens.get(0)
+                        .getTokenValue()
+        );
+    }
+
+    @Test
+    public void testChangeColumnLength() throws Exception {
+        DataMap existing = dataMap().with(
+                dbEntity("table1").attributes(
+                        dbAttr("attr01").typeVarchar(60)
+                )).build();
+
+        DataMap db = dataMap().with(
+                dbEntity("table1").attributes(
+                        dbAttr("attr01").typeVarchar(30)
+                )).build();
+
+        List<MergerToken> tokens = dbMerger().createMergeTokens(existing, db);
+        assertEquals(1, tokens.size());
+
+        DbEntity entity = existing.getDbEntity("table1");
+        DbEntity entityDb = db.getDbEntity("table1");
+        assertTrue(tokens.get(0) instanceof SetColumnTypeToDb);
+
+        assertEquals(
+                factory()
+                        .createSetColumnTypeToDb(entity, entityDb.getAttribute("attr01"), entity.getAttribute("attr01"))
+                        .getTokenValue(),
+                tokens.get(0)
+                        .getTokenValue()
+        );
+    }
+
+    /**
+     * Test unsupported type changes
+     */
+    @Test
+    public void testChangeColumnType() throws Exception {
+        DbEntity fromModel = dbEntity("table1").attributes(
+                dbAttr("attr01").typeInt(),
+                dbAttr("attr02").type("DATE"),
+                dbAttr("attr03").type("BOOLEAN"),
+                dbAttr("attr04").type("FLOAT")
+        ).build();
+        DataMap existing = dataMap().with(fromModel).build();
+
+        DbEntity fromDb = dbEntity("table1").attributes(
+                dbAttr("attr01").typeBigInt(),
+                dbAttr("attr02").type("NUMERIC"),
+                dbAttr("attr03").type("BLOB"),
+                dbAttr("attr04").type("TIMESTAMP")
+        ).build();
+        DataMap db = dataMap().with(fromDb).build();
+
+        List<MergerToken> tokens = dbMerger().createMergeTokens(existing, db);
+        assertEquals(4, tokens.size());
+
+        assertEquals(
+                factory()
+                        .createSetColumnTypeToDb(fromModel, fromDb.getAttribute("attr02"), fromModel.getAttribute("attr02"))
+                        .getTokenValue(),
+                tokens.get(1)
+                        .getTokenValue()
+        );
+
+        for(MergerToken token : tokens) {
+            assertTrue(token instanceof SetColumnTypeToDb);
+        }
+    }
+
+    @Test
+    public void testDropPrimaryKey() throws Exception {
+        DataMap existing = dataMap().with(
+                dbEntity("table1").attributes(
+                        dbAttr("attr01").typeInt()
+                )
+        ).build();
+
+        DataMap db = dataMap().with(
+                dbEntity("table1").attributes(
+                        dbAttr("attr01").typeInt().primaryKey()
+                )
+        ).build();
+
+        List<MergerToken> tokens = dbMerger().createMergeTokens(existing, db);
+        assertEquals(1, tokens.size());
+    }
+
+    @Test
+    public void testAddPrimaryKey() throws Exception {
+        DataMap existing = dataMap().with(
+                dbEntity("table1").attributes(
+                        dbAttr("attr01").typeInt().primaryKey()
+                )
+        ).build();
+
+        DataMap db = dataMap().with(
+                dbEntity("table1").attributes(
+                        dbAttr("attr01").typeInt()
+                )
+        ).build();
+
+        List<MergerToken> tokens = dbMerger().createMergeTokens(existing, db);
+        assertEquals(1, tokens.size());
+    }
+
+    @Test
+    public void testAddRelationship() throws Exception {
+        DataMap existing = dataMap().with(
+            dbEntity("table1").attributes(
+                dbAttr("attr01").typeInt(),
+                dbAttr("attr02").typeInt()),
+
+            dbEntity("table2").attributes(
+                dbAttr("attr01").typeInt().primaryKey(),
+                dbAttr("attr02").typeInt())
+        ).join("rel", "table1.attr01", "table2.attr01")
+         .build();
+
+        DataMap db = dataMap().with(
+            dbEntity("table1").attributes(
+                dbAttr("attr01").typeInt(),
+                dbAttr("attr02").typeInt()),
+
+            dbEntity("table2").attributes(
+                dbAttr("attr01").typeInt().primaryKey(),
+                dbAttr("attr02").typeInt())
+        )//.join("table1.attr01", "table2.attr01")
+         .build();
+
+
+        List<MergerToken> tokens = dbMerger().createMergeTokens(existing, db);
+
+        assertEquals(1, tokens.size());
+
+        DbEntity entity = existing.getDbEntity("table1");
+        assertEquals(factory().createAddRelationshipToDb(entity, entity.getRelationship("rel")).getTokenValue(),
+                     tokens.get(0).getTokenValue());
+    }
+
+    @Test
+    public void testAddRelationship1() throws Exception {
+        DataMap existing = dataMap().with(
+            dbEntity("table1").attributes(
+                dbAttr("attr01").typeInt(),
+                dbAttr("attr02").typeInt()),
+
+            dbEntity("table2").attributes(
+                dbAttr("attr01").typeInt().primaryKey(),
+                dbAttr("attr02").typeInt().primaryKey(),
+                dbAttr("attr03").typeInt().primaryKey())
+        ).join("rel", "table1.attr01", "table2.attr01")
+         .join("rel1", "table1.attr01", "table2.attr03")
+         .build();
+
+        DataMap db = dataMap().with(
+            dbEntity("table1").attributes(
+                dbAttr("attr01").typeInt(),
+                dbAttr("attr02").typeInt()),
+
+            dbEntity("table2").attributes(
+                dbAttr("attr01").typeInt().primaryKey(),
+                dbAttr("attr02").typeInt().primaryKey(),
+                dbAttr("attr03").typeInt().primaryKey())
+        ).join("rel", "table1.attr01", "table2.attr02")
+         .join("rel1", "table1.attr01", "table2.attr03")
+         .build();
+
+
+        List<MergerToken> tokens = dbMerger().createMergeTokens(existing, db);
+
+        assertEquals(2, tokens.size());
+
+        DbEntity entity = existing.getDbEntity("table1");
+        assertEquals(factory().createDropRelationshipToDb(entity, entity.getRelationship("rel")).getTokenValue(),
+                     tokens.get(0).getTokenValue());
+
+        entity = db.getDbEntity("table1");
+        assertEquals(factory().createAddRelationshipToDb(entity, entity.getRelationship("rel")).getTokenValue(),
+                     tokens.get(0).getTokenValue());
+    }
+
+    @Test
+    public void testRemoveRelationship() throws Exception {
+        DataMap existing = dataMap().with(
+            dbEntity("table1").attributes(
+                dbAttr("attr01").typeInt(),
+                dbAttr("attr02").typeInt()),
+
+            dbEntity("table2").attributes(
+                dbAttr("attr01").typeInt().primaryKey(),
+                dbAttr("attr02").typeInt())
+        )
+         .build();
+
+        DataMap db = dataMap().with(
+            dbEntity("table1").attributes(
+                dbAttr("attr01").typeInt(),
+                dbAttr("attr02").typeInt()),
+
+            dbEntity("table2").attributes(
+                dbAttr("attr01").typeInt().primaryKey(),
+                dbAttr("attr02").typeInt())
+        ).join("rel", "table1.attr01", "table2.attr01")
+         .build();
+
+
+        List<MergerToken> tokens = dbMerger().createMergeTokens(existing, db);
+
+        assertEquals(1, tokens.size());
+
+        DbEntity entity = db.getDbEntity("table1");
+        assertEquals(factory().createDropRelationshipToDb(entity, entity.getRelationship("rel")).getTokenValue(),
+                     tokens.get(0).getTokenValue());
+    }
+
+    @Test
+    public void testRemoveColumn() throws Exception {
+        DataMap existing = dataMap().with(
+            dbEntity("table1").attributes(
+                dbAttr("attr01").typeInt()
+        )).build();
+
+        DataMap db = dataMap().with(
+            dbEntity("table1").attributes(
+                dbAttr("attr01").typeInt(),
+                dbAttr("attr02").typeInt()
+        )).build();
+
+        List<MergerToken> tokens = dbMerger().createMergeTokens(existing, db);
+
+        assertEquals(1, tokens.size());
+
+        DbEntity entity = db.getDbEntity("table1");
+        assertEquals(factory().createDropColumnToModel(entity, entity.getAttribute("attr02")).getTokenValue(),
+                     tokens.get(0).getTokenValue());
+    }
+
+    @Test
+    public void testNoChanges() throws Exception {
+        DataMap dataMap1 = dataMap().with(
+                dbEntity("table1").attributes(
+                        dbAttr("attr01").typeInt(),
+                        dbAttr("attr02").typeInt(),
+                        dbAttr("attr03").typeInt()
+                )).build();
+
+        DataMap dataMap2 = dataMap().with(
+            dbEntity("table1").attributes(
+                dbAttr("attr01").typeInt(),
+                dbAttr("attr02").typeInt(),
+                dbAttr("attr03").typeInt()
+        )).build();
+
+
+        assertEquals(0, dbMerger().createMergeTokens(dataMap1, dataMap2).size());
+    }
+
+    private DataMapMerger dbMerger() {
+        return DataMapMerger.build(factory());
+    }
+
+    private HSQLMergerTokenFactory factory() {
+        return new HSQLMergerTokenFactory();
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cayenne/blob/d5707c75/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/DbMergerTest.java
----------------------------------------------------------------------
diff --git a/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/DbMergerTest.java b/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/DbMergerTest.java
deleted file mode 100644
index 55097e2..0000000
--- a/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/DbMergerTest.java
+++ /dev/null
@@ -1,387 +0,0 @@
-/*****************************************************************
- *   Licensed to the Apache Software Foundation (ASF) under one
- *  or more contributor license agreements.  See the NOTICE file
- *  distributed with this work for additional information
- *  regarding copyright ownership.  The ASF licenses this file
- *  to you under the Apache License, Version 2.0 (the
- *  "License"); you may not use this file except in compliance
- *  with the License.  You may obtain a copy of the License at
- *
- *    http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing,
- *  software distributed under the License is distributed on an
- *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- *  KIND, either express or implied.  See the License for the
- *  specific language governing permissions and limitations
- *  under the License.
- ****************************************************************/
-package org.apache.cayenne.dbsync.merge;
-
-import org.apache.cayenne.dbsync.merge.builders.DbEntityBuilder;
-import org.apache.cayenne.dbsync.merge.factory.HSQLMergerTokenFactory;
-import org.apache.cayenne.dbsync.merge.token.MergerToken;
-import org.apache.cayenne.dbsync.merge.token.SetColumnTypeToDb;
-import org.apache.cayenne.map.DataMap;
-import org.apache.cayenne.map.DbEntity;
-import org.junit.Test;
-
-import java.util.List;
-
-import static org.apache.cayenne.dbsync.merge.builders.ObjectMother.dataMap;
-import static org.apache.cayenne.dbsync.merge.builders.ObjectMother.dbAttr;
-import static org.apache.cayenne.dbsync.merge.builders.ObjectMother.dbEntity;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-
-public class DbMergerTest {
-
-    @Test
-    public void testEmptyDataMap() throws Exception {
-        DataMap existing = dataMap().build();
-        DataMap db = dataMap().build();
-        assertEquals(0, dbMerger().createMergeTokens(existing, db).size());
-    }
-
-    @Test
-    public void testAddTable() throws Exception {
-        DbEntityBuilder dbEntity =
-            dbEntity("table1").attributes(
-                dbAttr("attr01").typeInt()
-        );
-        DataMap existing = dataMap().with(dbEntity).build();
-        DataMap db = dataMap().build();
-
-        List<MergerToken> tokens = dbMerger().createMergeTokens(existing, db);
-
-        assertEquals(1, tokens.size());
-        assertEquals(factory().createCreateTableToDb(dbEntity.build()).getTokenValue(),
-                     tokens.get(0).getTokenValue());
-    }
-
-    @Test
-    public void testRemoveTable() throws Exception {
-        DataMap existing = dataMap().build();
-        DataMap db = dataMap().with(
-            dbEntity("table1").attributes(
-                dbAttr("attr01").typeInt()
-        )).build();
-
-        List<MergerToken> tokens = dbMerger().createMergeTokens(existing, db);
-
-        assertEquals(1, tokens.size());
-        assertEquals(factory().createDropTableToDb(db.getDbEntity("table1")).getTokenValue(),
-                     tokens.get(0).getTokenValue());
-    }
-
-    @Test
-    public void testAddColumn() throws Exception {
-        DataMap existing = dataMap().with(
-            dbEntity("table1").attributes(
-                dbAttr("attr01").typeInt(),
-                dbAttr("attr02").typeInt()
-        )).build();
-
-        DataMap db = dataMap().with(
-            dbEntity("table1").attributes(
-                dbAttr("attr01").typeInt()
-        )).build();
-
-        List<MergerToken> tokens = dbMerger().createMergeTokens(existing, db);
-
-        assertEquals(1, tokens.size());
-
-        DbEntity entity = existing.getDbEntity("table1");
-        assertEquals(factory().createAddColumnToDb(entity, entity.getAttribute("attr02")).getTokenValue(),
-                     tokens.get(0).getTokenValue());
-    }
-
-    @Test
-    public void testChangeColumnTypeSimple() throws Exception {
-        DataMap existing = dataMap().with(
-                dbEntity("table1").attributes(
-                        dbAttr("attr01").typeInt()
-                )).build();
-
-        DataMap db = dataMap().with(
-                dbEntity("table1").attributes(
-                        dbAttr("attr01").typeVarchar(30)
-                )).build();
-
-        List<MergerToken> tokens = dbMerger().createMergeTokens(existing, db);
-        assertEquals(1, tokens.size());
-
-        DbEntity entity = existing.getDbEntity("table1");
-        DbEntity entityDb = db.getDbEntity("table1");
-        assertTrue(tokens.get(0) instanceof SetColumnTypeToDb);
-
-        assertEquals(
-                factory()
-                        .createSetColumnTypeToDb(entity, entityDb.getAttribute("attr01"), entity.getAttribute("attr01"))
-                        .getTokenValue(),
-                tokens.get(0)
-                        .getTokenValue()
-        );
-    }
-
-    @Test
-    public void testChangeColumnLength() throws Exception {
-        DataMap existing = dataMap().with(
-                dbEntity("table1").attributes(
-                        dbAttr("attr01").typeVarchar(60)
-                )).build();
-
-        DataMap db = dataMap().with(
-                dbEntity("table1").attributes(
-                        dbAttr("attr01").typeVarchar(30)
-                )).build();
-
-        List<MergerToken> tokens = dbMerger().createMergeTokens(existing, db);
-        assertEquals(1, tokens.size());
-
-        DbEntity entity = existing.getDbEntity("table1");
-        DbEntity entityDb = db.getDbEntity("table1");
-        assertTrue(tokens.get(0) instanceof SetColumnTypeToDb);
-
-        assertEquals(
-                factory()
-                        .createSetColumnTypeToDb(entity, entityDb.getAttribute("attr01"), entity.getAttribute("attr01"))
-                        .getTokenValue(),
-                tokens.get(0)
-                        .getTokenValue()
-        );
-    }
-
-    /**
-     * Test unsupported type changes
-     */
-    @Test
-    public void testChangeColumnType() throws Exception {
-        DbEntity fromModel = dbEntity("table1").attributes(
-                dbAttr("attr01").typeInt(),
-                dbAttr("attr02").type("DATE"),
-                dbAttr("attr03").type("BOOLEAN"),
-                dbAttr("attr04").type("FLOAT")
-        ).build();
-        DataMap existing = dataMap().with(fromModel).build();
-
-        DbEntity fromDb = dbEntity("table1").attributes(
-                dbAttr("attr01").typeBigInt(),
-                dbAttr("attr02").type("NUMERIC"),
-                dbAttr("attr03").type("BLOB"),
-                dbAttr("attr04").type("TIMESTAMP")
-        ).build();
-        DataMap db = dataMap().with(fromDb).build();
-
-        List<MergerToken> tokens = dbMerger().createMergeTokens(existing, db);
-        assertEquals(4, tokens.size());
-
-        assertEquals(
-                factory()
-                        .createSetColumnTypeToDb(fromModel, fromDb.getAttribute("attr02"), fromModel.getAttribute("attr02"))
-                        .getTokenValue(),
-                tokens.get(1)
-                        .getTokenValue()
-        );
-
-        for(MergerToken token : tokens) {
-            assertTrue(token instanceof SetColumnTypeToDb);
-        }
-    }
-
-    @Test
-    public void testDropPrimaryKey() throws Exception {
-        DataMap existing = dataMap().with(
-                dbEntity("table1").attributes(
-                        dbAttr("attr01").typeInt()
-                )
-        ).build();
-
-        DataMap db = dataMap().with(
-                dbEntity("table1").attributes(
-                        dbAttr("attr01").typeInt().primaryKey()
-                )
-        ).build();
-
-        List<MergerToken> tokens = dbMerger().createMergeTokens(existing, db);
-        assertEquals(1, tokens.size());
-    }
-
-    @Test
-    public void testAddPrimaryKey() throws Exception {
-        DataMap existing = dataMap().with(
-                dbEntity("table1").attributes(
-                        dbAttr("attr01").typeInt().primaryKey()
-                )
-        ).build();
-
-        DataMap db = dataMap().with(
-                dbEntity("table1").attributes(
-                        dbAttr("attr01").typeInt()
-                )
-        ).build();
-
-        List<MergerToken> tokens = dbMerger().createMergeTokens(existing, db);
-        assertEquals(1, tokens.size());
-    }
-
-    @Test
-    public void testAddRelationship() throws Exception {
-        DataMap existing = dataMap().with(
-            dbEntity("table1").attributes(
-                dbAttr("attr01").typeInt(),
-                dbAttr("attr02").typeInt()),
-
-            dbEntity("table2").attributes(
-                dbAttr("attr01").typeInt().primaryKey(),
-                dbAttr("attr02").typeInt())
-        ).join("rel", "table1.attr01", "table2.attr01")
-         .build();
-
-        DataMap db = dataMap().with(
-            dbEntity("table1").attributes(
-                dbAttr("attr01").typeInt(),
-                dbAttr("attr02").typeInt()),
-
-            dbEntity("table2").attributes(
-                dbAttr("attr01").typeInt().primaryKey(),
-                dbAttr("attr02").typeInt())
-        )//.join("table1.attr01", "table2.attr01")
-         .build();
-
-
-        List<MergerToken> tokens = dbMerger().createMergeTokens(existing, db);
-
-        assertEquals(1, tokens.size());
-
-        DbEntity entity = existing.getDbEntity("table1");
-        assertEquals(factory().createAddRelationshipToDb(entity, entity.getRelationship("rel")).getTokenValue(),
-                     tokens.get(0).getTokenValue());
-    }
-
-    @Test
-    public void testAddRelationship1() throws Exception {
-        DataMap existing = dataMap().with(
-            dbEntity("table1").attributes(
-                dbAttr("attr01").typeInt(),
-                dbAttr("attr02").typeInt()),
-
-            dbEntity("table2").attributes(
-                dbAttr("attr01").typeInt().primaryKey(),
-                dbAttr("attr02").typeInt().primaryKey(),
-                dbAttr("attr03").typeInt().primaryKey())
-        ).join("rel", "table1.attr01", "table2.attr01")
-         .join("rel1", "table1.attr01", "table2.attr03")
-         .build();
-
-        DataMap db = dataMap().with(
-            dbEntity("table1").attributes(
-                dbAttr("attr01").typeInt(),
-                dbAttr("attr02").typeInt()),
-
-            dbEntity("table2").attributes(
-                dbAttr("attr01").typeInt().primaryKey(),
-                dbAttr("attr02").typeInt().primaryKey(),
-                dbAttr("attr03").typeInt().primaryKey())
-        ).join("rel", "table1.attr01", "table2.attr02")
-         .join("rel1", "table1.attr01", "table2.attr03")
-         .build();
-
-
-        List<MergerToken> tokens = dbMerger().createMergeTokens(existing, db);
-
-        assertEquals(2, tokens.size());
-
-        DbEntity entity = existing.getDbEntity("table1");
-        assertEquals(factory().createDropRelationshipToDb(entity, entity.getRelationship("rel")).getTokenValue(),
-                     tokens.get(0).getTokenValue());
-
-        entity = db.getDbEntity("table1");
-        assertEquals(factory().createAddRelationshipToDb(entity, entity.getRelationship("rel")).getTokenValue(),
-                     tokens.get(0).getTokenValue());
-    }
-
-    @Test
-    public void testRemoveRelationship() throws Exception {
-        DataMap existing = dataMap().with(
-            dbEntity("table1").attributes(
-                dbAttr("attr01").typeInt(),
-                dbAttr("attr02").typeInt()),
-
-            dbEntity("table2").attributes(
-                dbAttr("attr01").typeInt().primaryKey(),
-                dbAttr("attr02").typeInt())
-        )
-         .build();
-
-        DataMap db = dataMap().with(
-            dbEntity("table1").attributes(
-                dbAttr("attr01").typeInt(),
-                dbAttr("attr02").typeInt()),
-
-            dbEntity("table2").attributes(
-                dbAttr("attr01").typeInt().primaryKey(),
-                dbAttr("attr02").typeInt())
-        ).join("rel", "table1.attr01", "table2.attr01")
-         .build();
-
-
-        List<MergerToken> tokens = dbMerger().createMergeTokens(existing, db);
-
-        assertEquals(1, tokens.size());
-
-        DbEntity entity = db.getDbEntity("table1");
-        assertEquals(factory().createDropRelationshipToDb(entity, entity.getRelationship("rel")).getTokenValue(),
-                     tokens.get(0).getTokenValue());
-    }
-
-    @Test
-    public void testRemoveColumn() throws Exception {
-        DataMap existing = dataMap().with(
-            dbEntity("table1").attributes(
-                dbAttr("attr01").typeInt()
-        )).build();
-
-        DataMap db = dataMap().with(
-            dbEntity("table1").attributes(
-                dbAttr("attr01").typeInt(),
-                dbAttr("attr02").typeInt()
-        )).build();
-
-        List<MergerToken> tokens = dbMerger().createMergeTokens(existing, db);
-
-        assertEquals(1, tokens.size());
-
-        DbEntity entity = db.getDbEntity("table1");
-        assertEquals(factory().createDropColumnToModel(entity, entity.getAttribute("attr02")).getTokenValue(),
-                     tokens.get(0).getTokenValue());
-    }
-
-    @Test
-    public void testNoChanges() throws Exception {
-        DataMap dataMap1 = dataMap().with(
-                dbEntity("table1").attributes(
-                        dbAttr("attr01").typeInt(),
-                        dbAttr("attr02").typeInt(),
-                        dbAttr("attr03").typeInt()
-                )).build();
-
-        DataMap dataMap2 = dataMap().with(
-            dbEntity("table1").attributes(
-                dbAttr("attr01").typeInt(),
-                dbAttr("attr02").typeInt(),
-                dbAttr("attr03").typeInt()
-        )).build();
-
-
-        assertEquals(0, dbMerger().createMergeTokens(dataMap1, dataMap2).size());
-    }
-
-    private DataMapMerger dbMerger() {
-        return DataMapMerger.build(factory());
-    }
-
-    private HSQLMergerTokenFactory factory() {
-        return new HSQLMergerTokenFactory();
-    }
-}
\ No newline at end of file