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:47 UTC

[11/16] cayenne git commit: CAY-2169 - split Tokens to model and db packages - all tests are passing - fix SQL syntax for HSQLDB allow column NULL value - fix some problems with tokens sort, but still it's far from perfect (see CAY-2170)

http://git-wip-us.apache.org/repos/asf/cayenne/blob/725e6ae9/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/token/SetAllowNullToModel.java
----------------------------------------------------------------------
diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/token/SetAllowNullToModel.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/token/SetAllowNullToModel.java
deleted file mode 100644
index b9e7f19..0000000
--- a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/token/SetAllowNullToModel.java
+++ /dev/null
@@ -1,47 +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.token;
-
-import org.apache.cayenne.dbsync.merge.context.MergerContext;
-import org.apache.cayenne.dbsync.merge.factory.MergerTokenFactory;
-import org.apache.cayenne.map.DbAttribute;
-import org.apache.cayenne.map.DbEntity;
-
-/**
- * A {@link MergerToken} to set the mandatory field of a {@link DbAttribute} to false
- * 
- */
-public class SetAllowNullToModel extends AbstractToModelToken.EntityAndColumn {
-
-    public SetAllowNullToModel(DbEntity entity, DbAttribute column) {
-        super("Set Allow Null", entity, column);
-    }
-
-    @Override
-    public MergerToken createReverse(MergerTokenFactory factory) {
-        return factory.createSetNotNullToDb(getEntity(), getColumn());
-    }
-
-    @Override
-    public void execute(MergerContext mergerContext) {
-        getColumn().setMandatory(false);
-        mergerContext.getDelegate().dbAttributeModified(getColumn());
-    }
-}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/725e6ae9/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/token/SetColumnTypeToDb.java
----------------------------------------------------------------------
diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/token/SetColumnTypeToDb.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/token/SetColumnTypeToDb.java
deleted file mode 100644
index d8eeca9..0000000
--- a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/token/SetColumnTypeToDb.java
+++ /dev/null
@@ -1,112 +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.token;
-
-import org.apache.cayenne.dba.DbAdapter;
-import org.apache.cayenne.dba.JdbcAdapter;
-import org.apache.cayenne.dba.QuotingStrategy;
-import org.apache.cayenne.dba.TypesMapping;
-import org.apache.cayenne.dbsync.merge.factory.MergerTokenFactory;
-import org.apache.cayenne.map.DbAttribute;
-import org.apache.cayenne.map.DbEntity;
-
-import java.util.Collections;
-import java.util.List;
-
-/**
- * An {@link MergerToken} to use to set type, length and precision.
- */
-public class SetColumnTypeToDb extends AbstractToDbToken.Entity {
-
-    private DbAttribute columnOriginal;
-    private DbAttribute columnNew;
-
-    public SetColumnTypeToDb(DbEntity entity, DbAttribute columnOriginal, DbAttribute columnNew) {
-        super("Set Column Type", entity);
-        this.columnOriginal = columnOriginal;
-        this.columnNew = columnNew;
-    }
-    
-    /**
-     * append the part of the token before the actual column data type
-     * @param context 
-     */
-    protected void appendPrefix(StringBuffer sqlBuffer, QuotingStrategy context) {
-        sqlBuffer.append("ALTER TABLE ");
-        sqlBuffer.append(context.quotedFullyQualifiedName(getEntity()));
-        sqlBuffer.append(" ALTER ");
-        sqlBuffer.append(context.quotedName(columnNew));
-        sqlBuffer.append(" TYPE ");
-    }
-
-    @Override
-    public List<String> createSql(DbAdapter adapter) {
-        StringBuffer sqlBuffer = new StringBuffer();
-        appendPrefix(sqlBuffer, adapter.getQuotingStrategy());
-  
-        sqlBuffer.append(JdbcAdapter.getType(adapter, columnNew));
-        sqlBuffer.append(JdbcAdapter.sizeAndPrecision(adapter, columnNew));
-
-        return Collections.singletonList(sqlBuffer.toString());
-    }
-
-    @Override
-    public String getTokenValue() {
-        StringBuilder sb = new StringBuilder();
-        sb.append(getEntity().getName());
-        sb.append(".");
-        sb.append(columnNew.getName());
-
-        if (columnOriginal.getType() != columnNew.getType()) {
-            sb.append(" type: ");
-            sb.append(TypesMapping.getSqlNameByType(columnOriginal.getType()));
-            sb.append(" -> ");
-            sb.append(TypesMapping.getSqlNameByType(columnNew.getType()));
-        }
-
-        if (columnOriginal.getMaxLength() != columnNew.getMaxLength()) {
-            sb.append(" maxLength: ");
-            sb.append(columnOriginal.getMaxLength());
-            sb.append(" -> ");
-            sb.append(columnNew.getMaxLength());
-        }
-
-        if (columnOriginal.getAttributePrecision() != columnNew.getAttributePrecision()) {
-            sb.append(" precision: ");
-            sb.append(columnOriginal.getAttributePrecision());
-            sb.append(" -> ");
-            sb.append(columnNew.getAttributePrecision());
-        }
-
-        if (columnOriginal.getScale() != columnNew.getScale()) {
-            sb.append(" scale: ");
-            sb.append(columnOriginal.getScale());
-            sb.append(" -> ");
-            sb.append(columnNew.getScale());
-        }
-
-        return sb.toString();
-    }
-
-    @Override
-    public MergerToken createReverse(MergerTokenFactory factory) {
-        return factory.createSetColumnTypeToModel(getEntity(), columnNew, columnOriginal);
-    }
-}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/725e6ae9/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/token/SetColumnTypeToModel.java
----------------------------------------------------------------------
diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/token/SetColumnTypeToModel.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/token/SetColumnTypeToModel.java
deleted file mode 100644
index 26730a9..0000000
--- a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/token/SetColumnTypeToModel.java
+++ /dev/null
@@ -1,97 +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.token;
-
-import org.apache.cayenne.dba.TypesMapping;
-import org.apache.cayenne.dbsync.merge.context.MergerContext;
-import org.apache.cayenne.dbsync.merge.factory.MergerTokenFactory;
-import org.apache.cayenne.map.DbAttribute;
-import org.apache.cayenne.map.DbEntity;
-
-/**
- * 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
- * are not modified by this token.
- * 
- */
-public class SetColumnTypeToModel extends AbstractToModelToken.Entity {
-
-    private DbAttribute columnOriginal;
-    private DbAttribute columnNew;
-
-    public SetColumnTypeToModel(DbEntity entity, DbAttribute columnOriginal, DbAttribute columnNew) {
-        super("Set Column Type", entity);
-        this.columnOriginal = columnOriginal;
-        this.columnNew = columnNew;
-    }
-
-    @Override
-    public MergerToken createReverse(MergerTokenFactory factory) {
-        return factory.createSetColumnTypeToDb(getEntity(), columnNew, columnOriginal);
-    }
-
-    @Override
-    public void execute(MergerContext mergerContext) {
-        columnOriginal.setType(columnNew.getType());
-        columnOriginal.setMaxLength(columnNew.getMaxLength());
-        columnOriginal.setAttributePrecision(columnNew.getAttributePrecision());
-        columnOriginal.setScale(columnNew.getScale());
-        mergerContext.getDelegate().dbAttributeModified(columnOriginal);
-    }
-
-    @Override
-    public String getTokenValue() {
-        StringBuilder sb = new StringBuilder();
-        sb.append(getEntity().getName());
-        sb.append(".");
-        sb.append(columnNew.getName());
-
-        if (columnOriginal.getType() != columnNew.getType()) {
-            sb.append(" type: ");
-            sb.append(TypesMapping.getSqlNameByType(columnOriginal.getType()));
-            sb.append(" -> ");
-            sb.append(TypesMapping.getSqlNameByType(columnNew.getType()));
-        }
-
-        if (columnOriginal.getMaxLength() != columnNew.getMaxLength()) {
-            sb.append(" maxLength: ");
-            sb.append(columnOriginal.getMaxLength());
-            sb.append(" -> ");
-            sb.append(columnNew.getMaxLength());
-        }
-
-        if (columnOriginal.getAttributePrecision() != columnNew.getAttributePrecision()) {
-            sb.append(" precision: ");
-            sb.append(columnOriginal.getAttributePrecision());
-            sb.append(" -> ");
-            sb.append(columnNew.getAttributePrecision());
-        }
-
-        if (columnOriginal.getScale() != columnNew.getScale()) {
-            sb.append(" scale: ");
-            sb.append(columnOriginal.getScale());
-            sb.append(" -> ");
-            sb.append(columnNew.getScale());
-        }
-
-        return sb.toString();
-    }
-    
-}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/725e6ae9/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/token/SetNotNullToDb.java
----------------------------------------------------------------------
diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/token/SetNotNullToDb.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/token/SetNotNullToDb.java
deleted file mode 100644
index a0fe583..0000000
--- a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/token/SetNotNullToDb.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*****************************************************************
- *   Licensed to the Apache Software Foundation (ASF) under one
- *  or more contributor license agreements.  See the NOTICE file
- *  distributed with this work for additional information
- *  regarding copyright ownership.  The ASF licenses this file
- *  to you under the Apache License, Version 2.0 (the
- *  "License"); you may not use this file except in compliance
- *  with the License.  You may obtain a copy of the License at
- *
- *    http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing,
- *  software distributed under the License is distributed on an
- *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- *  KIND, either express or implied.  See the License for the
- *  specific language governing permissions and limitations
- *  under the License.
- ****************************************************************/
-
-package org.apache.cayenne.dbsync.merge.token;
-
-import org.apache.cayenne.dba.DbAdapter;
-import org.apache.cayenne.dba.QuotingStrategy;
-import org.apache.cayenne.dbsync.merge.factory.MergerTokenFactory;
-import org.apache.cayenne.map.DbAttribute;
-import org.apache.cayenne.map.DbEntity;
-
-import java.util.Collections;
-import java.util.List;
-
-/**
- * A {@link MergerToken} to add a "not null" clause to a column.
- */
-public class SetNotNullToDb extends AbstractToDbToken.EntityAndColumn {
-
-    public SetNotNullToDb(DbEntity entity, DbAttribute column) {
-        super("Set Not Null", entity, column);
-    }
-
-    @Override
-    public List<String> createSql(DbAdapter adapter) {
-        QuotingStrategy context = adapter.getQuotingStrategy();
-
-        return Collections.singletonList("ALTER TABLE " + context.quotedFullyQualifiedName(getEntity())
-                + " ALTER COLUMN " + context.quotedName(getColumn()) + " SET NOT NULL");
-    }
-
-    @Override
-    public MergerToken createReverse(MergerTokenFactory factory) {
-        return factory.createSetAllowNullToModel(getEntity(), getColumn());
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/725e6ae9/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/token/SetNotNullToModel.java
----------------------------------------------------------------------
diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/token/SetNotNullToModel.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/token/SetNotNullToModel.java
deleted file mode 100644
index 50a4b43..0000000
--- a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/token/SetNotNullToModel.java
+++ /dev/null
@@ -1,47 +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.token;
-
-import org.apache.cayenne.dbsync.merge.context.MergerContext;
-import org.apache.cayenne.dbsync.merge.factory.MergerTokenFactory;
-import org.apache.cayenne.map.DbAttribute;
-import org.apache.cayenne.map.DbEntity;
-
-/**
- * A {@link MergerToken} to set the mandatory field of a {@link DbAttribute} to true
- * 
- */
-public class SetNotNullToModel extends AbstractToModelToken.EntityAndColumn {
-
-    public SetNotNullToModel(DbEntity entity, DbAttribute column) {
-        super("Set Not Null", entity, column);
-    }
-
-    @Override
-    public MergerToken createReverse(MergerTokenFactory factory) {
-        return factory.createSetAllowNullToDb(getEntity(), getColumn());
-    }
-
-    @Override
-    public void execute(MergerContext mergerContext) {
-        getColumn().setMandatory(true);
-        mergerContext.getDelegate().dbAttributeModified(getColumn());
-    }
-}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/725e6ae9/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/token/SetPrimaryKeyToDb.java
----------------------------------------------------------------------
diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/token/SetPrimaryKeyToDb.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/token/SetPrimaryKeyToDb.java
deleted file mode 100644
index 6380555..0000000
--- a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/token/SetPrimaryKeyToDb.java
+++ /dev/null
@@ -1,88 +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.token;
-
-import org.apache.cayenne.dba.DbAdapter;
-import org.apache.cayenne.dba.QuotingStrategy;
-import org.apache.cayenne.dbsync.merge.factory.MergerTokenFactory;
-import org.apache.cayenne.map.DbAttribute;
-import org.apache.cayenne.map.DbEntity;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Iterator;
-import java.util.List;
-
-public class SetPrimaryKeyToDb extends AbstractToDbToken.Entity {
-
-    private Collection<DbAttribute> primaryKeyOriginal;
-    private Collection<DbAttribute> primaryKeyNew;
-    private String detectedPrimaryKeyName;
-
-    public SetPrimaryKeyToDb(DbEntity entity, Collection<DbAttribute> primaryKeyOriginal,
-            Collection<DbAttribute> primaryKeyNew, String detectedPrimaryKeyName) {
-        super("Set Primary Key", entity);
-
-        this.primaryKeyOriginal = primaryKeyOriginal;
-        this.primaryKeyNew = primaryKeyNew;
-        this.detectedPrimaryKeyName = detectedPrimaryKeyName;
-    }
-
-    @Override
-    public List<String> createSql(DbAdapter adapter) {
-        List<String> sqls = new ArrayList<String>();
-        if (!primaryKeyOriginal.isEmpty()) {
-            appendDropOriginalPrimaryKeySQL(adapter, sqls);
-        }
-        appendAddNewPrimaryKeySQL(adapter, sqls);
-        return sqls;
-    }
-
-    protected void appendDropOriginalPrimaryKeySQL(DbAdapter adapter, List<String> sqls) {
-        if (detectedPrimaryKeyName == null) {
-            return;
-        }
-        sqls.add("ALTER TABLE " + adapter.getQuotingStrategy().quotedFullyQualifiedName(getEntity())
-                + " DROP CONSTRAINT " + detectedPrimaryKeyName);
-    }
-
-    protected void appendAddNewPrimaryKeySQL(DbAdapter adapter, List<String> sqls) {
-        QuotingStrategy quotingStrategy = adapter.getQuotingStrategy();
-
-        StringBuilder sql = new StringBuilder();
-        sql.append("ALTER TABLE ");
-        sql.append(quotingStrategy.quotedFullyQualifiedName(getEntity()));
-        sql.append(" ADD PRIMARY KEY (");
-        for (Iterator<DbAttribute> it = primaryKeyNew.iterator(); it.hasNext();) {
-            sql.append(quotingStrategy.quotedName(it.next()));
-            if (it.hasNext()) {
-                sql.append(", ");
-            }
-        }
-        sql.append(")");
-        sqls.add(sql.toString());
-    }
-
-    @Override
-    public MergerToken createReverse(MergerTokenFactory factory) {
-        return factory.createSetPrimaryKeyToModel(getEntity(), primaryKeyNew, primaryKeyOriginal,
-                detectedPrimaryKeyName);
-    }
-}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/725e6ae9/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/token/SetPrimaryKeyToModel.java
----------------------------------------------------------------------
diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/token/SetPrimaryKeyToModel.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/token/SetPrimaryKeyToModel.java
deleted file mode 100644
index 86e0ac3..0000000
--- a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/token/SetPrimaryKeyToModel.java
+++ /dev/null
@@ -1,82 +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.token;
-
-import org.apache.cayenne.dbsync.merge.context.MergerContext;
-import org.apache.cayenne.dbsync.merge.factory.MergerTokenFactory;
-import org.apache.cayenne.map.DbAttribute;
-import org.apache.cayenne.map.DbEntity;
-import org.apache.cayenne.map.event.AttributeEvent;
-
-import java.util.Collection;
-import java.util.HashSet;
-import java.util.Set;
-
-public class SetPrimaryKeyToModel extends AbstractToModelToken.Entity {
-
-    private Collection<DbAttribute> primaryKeyOriginal;
-    private Collection<DbAttribute> primaryKeyNew;
-    private String detectedPrimaryKeyName;
-    private Set<String> primaryKeyNewAttributeNames = new HashSet<String>();
-
-    public SetPrimaryKeyToModel(DbEntity entity,
-            Collection<DbAttribute> primaryKeyOriginal,
-            Collection<DbAttribute> primaryKeyNew, String detectedPrimaryKeyName) {
-        super("Set Primary Key", entity);
-        
-        this.primaryKeyOriginal = primaryKeyOriginal;
-        this.primaryKeyNew = primaryKeyNew;
-        this.detectedPrimaryKeyName = detectedPrimaryKeyName;
-        
-        for (DbAttribute attr : primaryKeyNew) {
-            primaryKeyNewAttributeNames.add(attr.getName().toUpperCase());
-        }
-    }
-
-    @Override
-    public MergerToken createReverse(MergerTokenFactory factory) {
-        return factory.createSetPrimaryKeyToDb(
-                getEntity(),
-                primaryKeyNew,
-                primaryKeyOriginal,
-                detectedPrimaryKeyName);
-    }
-
-    @Override
-    public void execute(MergerContext mergerContext) {
-        DbEntity e = getEntity();
-
-        for (DbAttribute attr : e.getAttributes()) {
-
-            boolean wasPrimaryKey = attr.isPrimaryKey();
-            boolean willBePrimaryKey = primaryKeyNewAttributeNames.contains(attr
-                    .getName()
-                    .toUpperCase());
-
-            if (wasPrimaryKey != willBePrimaryKey) {
-                attr.setPrimaryKey(willBePrimaryKey);
-                e.dbAttributeChanged(new AttributeEvent(this, attr, e));
-                mergerContext.getDelegate().dbAttributeModified(attr);
-            }
-
-        }
-
-    }
-}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/725e6ae9/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/token/SetValueForNullToDb.java
----------------------------------------------------------------------
diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/token/SetValueForNullToDb.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/token/SetValueForNullToDb.java
deleted file mode 100644
index b83d619..0000000
--- a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/token/SetValueForNullToDb.java
+++ /dev/null
@@ -1,49 +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.token;
-
-import org.apache.cayenne.dba.DbAdapter;
-import org.apache.cayenne.dbsync.merge.factory.MergerTokenFactory;
-import org.apache.cayenne.map.DbAttribute;
-import org.apache.cayenne.map.DbEntity;
-
-import java.util.List;
-
-
-public class SetValueForNullToDb extends AbstractToDbToken.EntityAndColumn {
-    
-    private ValueForNullProvider valueForNullProvider;
-
-    public SetValueForNullToDb(DbEntity entity, DbAttribute column, ValueForNullProvider valueForNullProvider) {
-        super("Set value for null", entity, column);
-        this.valueForNullProvider = valueForNullProvider;
-    }
-    
-    @Override
-    public List<String> createSql(DbAdapter adapter) {
-        return valueForNullProvider.createSql(getEntity(), getColumn());
-    }
-
-    @Override
-    public MergerToken createReverse(MergerTokenFactory factory) {
-        return new DummyReverseToken(this);
-    }
-    
-}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/725e6ae9/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/token/TokenComparator.java
----------------------------------------------------------------------
diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/token/TokenComparator.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/token/TokenComparator.java
index 4d9754a..249b99f 100644
--- a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/token/TokenComparator.java
+++ b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/token/TokenComparator.java
@@ -21,6 +21,10 @@ package org.apache.cayenne.dbsync.merge.token;
 
 import java.util.Comparator;
 
+import org.apache.cayenne.dbsync.merge.token.db.AbstractToDbToken;
+import org.apache.cayenne.dbsync.merge.token.db.AddRelationshipToDb;
+import org.apache.cayenne.dbsync.merge.token.model.AddRelationshipToModel;
+
 /**
  * Simple sort of merge tokens.
  * Just move all relationships creation tokens to the end of the list.
@@ -30,7 +34,15 @@ public class TokenComparator implements Comparator<MergerToken> {
     @Override
     public int compare(MergerToken o1, MergerToken o2) {
         if (o1 instanceof AbstractToDbToken && o2 instanceof AbstractToDbToken) {
-            return ((AbstractToDbToken) o1).compareTo(o2);
+            if (o1 instanceof AddRelationshipToDb && o2 instanceof AddRelationshipToDb) {
+                return 0;
+            }
+
+            if (!(o1 instanceof AddRelationshipToDb || o2 instanceof AddRelationshipToDb)) {
+                return 0;
+            }
+
+            return o1 instanceof AddRelationshipToDb ? 1 : -1;
         }
 
         if (o1 instanceof AddRelationshipToModel && o2 instanceof AddRelationshipToModel) {

http://git-wip-us.apache.org/repos/asf/cayenne/blob/725e6ae9/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/token/db/AbstractToDbToken.java
----------------------------------------------------------------------
diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/token/db/AbstractToDbToken.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/token/db/AbstractToDbToken.java
new file mode 100644
index 0000000..11812b8
--- /dev/null
+++ b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/token/db/AbstractToDbToken.java
@@ -0,0 +1,134 @@
+/*****************************************************************
+ *   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.token.db;
+
+import org.apache.cayenne.dba.DbAdapter;
+import org.apache.cayenne.dbsync.merge.context.MergeDirection;
+import org.apache.cayenne.dbsync.merge.context.MergerContext;
+import org.apache.cayenne.dbsync.merge.token.MergerToken;
+import org.apache.cayenne.log.JdbcEventLogger;
+import org.apache.cayenne.map.DbAttribute;
+import org.apache.cayenne.map.DbEntity;
+import org.apache.cayenne.validation.SimpleValidationFailure;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.List;
+
+/**
+ * Common abstract superclass for all {@link MergerToken}s going from the model
+ * to the database.
+ */
+public abstract class AbstractToDbToken implements MergerToken, Comparable<MergerToken> {
+
+	private final String tokenName;
+
+	protected AbstractToDbToken(String tokenName) {
+		this.tokenName = tokenName;
+	}
+
+	@Override
+	public final String getTokenName() {
+		return tokenName;
+	}
+
+	@Override
+	public final MergeDirection getDirection() {
+		return MergeDirection.TO_DB;
+	}
+
+	@Override
+	public void execute(MergerContext mergerContext) {
+		for (String sql : createSql(mergerContext.getDataNode().getAdapter())) {
+			executeSql(mergerContext, sql);
+		}
+	}
+
+	protected void executeSql(MergerContext mergerContext, String sql) {
+		JdbcEventLogger logger = mergerContext.getDataNode().getJdbcEventLogger();
+		logger.log(sql);
+
+		try (Connection conn = mergerContext.getDataNode().getDataSource().getConnection();) {
+
+			try (Statement st = conn.createStatement();) {
+				st.execute(sql);
+			}
+		} catch (SQLException e) {
+			mergerContext.getValidationResult().addFailure(new SimpleValidationFailure(sql, e.getMessage()));
+			logger.logQueryError(e);
+		}
+	}
+
+	@Override
+	public String toString() {
+		return getTokenName() + ' ' + getTokenValue() + ' ' + getDirection();
+	}
+
+	public boolean isEmpty() {
+		return false;
+	}
+
+	public abstract List<String> createSql(DbAdapter adapter);
+
+	abstract static class Entity extends AbstractToDbToken {
+
+		private DbEntity entity;
+
+		public Entity(String tokenName, DbEntity entity) {
+			super(tokenName);
+			this.entity = entity;
+		}
+
+		public DbEntity getEntity() {
+			return entity;
+		}
+
+		public String getTokenValue() {
+			return getEntity().getName();
+		}
+
+		public int compareTo(MergerToken o) {
+			// default order as tokens are created
+			return 0;
+		}
+
+	}
+
+	abstract static class EntityAndColumn extends Entity {
+
+		private DbAttribute column;
+
+		public EntityAndColumn(String tokenName, DbEntity entity, DbAttribute column) {
+			super(tokenName, entity);
+			this.column = column;
+		}
+
+		public DbAttribute getColumn() {
+			return column;
+		}
+
+		@Override
+		public String getTokenValue() {
+			return getEntity().getName() + "." + getColumn().getName();
+		}
+
+	}
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/725e6ae9/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/token/db/AddColumnToDb.java
----------------------------------------------------------------------
diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/token/db/AddColumnToDb.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/token/db/AddColumnToDb.java
new file mode 100644
index 0000000..65ce341
--- /dev/null
+++ b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/token/db/AddColumnToDb.java
@@ -0,0 +1,76 @@
+/*****************************************************************
+ *   Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ ****************************************************************/
+
+package org.apache.cayenne.dbsync.merge.token.db;
+
+import org.apache.cayenne.dba.DbAdapter;
+import org.apache.cayenne.dba.JdbcAdapter;
+import org.apache.cayenne.dba.QuotingStrategy;
+import org.apache.cayenne.dbsync.merge.factory.MergerTokenFactory;
+import org.apache.cayenne.dbsync.merge.token.MergerToken;
+import org.apache.cayenne.map.DbAttribute;
+import org.apache.cayenne.map.DbEntity;
+
+import java.util.Collections;
+import java.util.List;
+
+public class AddColumnToDb extends AbstractToDbToken.EntityAndColumn {
+
+    public AddColumnToDb(DbEntity entity, DbAttribute column) {
+        super("Add Column", entity, column);
+    }
+
+    /**
+     * append the part of the token before the actual column data type
+     */
+    protected void appendPrefix(StringBuffer sqlBuffer, QuotingStrategy context) {
+
+        sqlBuffer.append("ALTER TABLE ");
+        sqlBuffer.append(context.quotedFullyQualifiedName(getEntity()));
+        sqlBuffer.append(" ADD COLUMN ");
+        sqlBuffer.append(context.quotedName(getColumn()));
+        sqlBuffer.append(" ");
+    }
+
+    @Override
+    public List<String> createSql(DbAdapter adapter) {
+        StringBuffer sqlBuffer = new StringBuffer();
+        QuotingStrategy context = adapter.getQuotingStrategy();
+        appendPrefix(sqlBuffer, context);
+
+        sqlBuffer.append(JdbcAdapter.getType(adapter, getColumn()));
+        sqlBuffer.append(JdbcAdapter.sizeAndPrecision(adapter, getColumn()));
+
+        return Collections.singletonList(sqlBuffer.toString());
+    }
+
+    @Override
+    public MergerToken createReverse(MergerTokenFactory factory) {
+        return factory.createDropColumnToModel(getEntity(), getColumn());
+    }
+
+    @Override
+    public int compareTo(MergerToken o) {
+        // add all AddRelationshipToDb to the end.
+        if (o instanceof AddRelationshipToDb) {
+            return -1;
+        }
+        return super.compareTo(o);
+    }
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/725e6ae9/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/token/db/AddRelationshipToDb.java
----------------------------------------------------------------------
diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/token/db/AddRelationshipToDb.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/token/db/AddRelationshipToDb.java
new file mode 100644
index 0000000..9502d06
--- /dev/null
+++ b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/token/db/AddRelationshipToDb.java
@@ -0,0 +1,84 @@
+/*****************************************************************
+ *   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.token.db;
+
+import org.apache.cayenne.access.DbGenerator;
+import org.apache.cayenne.dba.DbAdapter;
+import org.apache.cayenne.dbsync.merge.factory.MergerTokenFactory;
+import org.apache.cayenne.dbsync.merge.token.MergerToken;
+import org.apache.cayenne.map.DbEntity;
+import org.apache.cayenne.map.DbRelationship;
+
+import java.util.Collections;
+import java.util.List;
+
+public class AddRelationshipToDb extends AbstractToDbToken.Entity {
+
+    private DbRelationship relationship;
+
+    public AddRelationshipToDb(DbEntity entity, DbRelationship relationship) {
+        super("Add foreign key", entity);
+        this.relationship = relationship;
+    }
+
+    /**
+     * @see DbGenerator#createConstraintsQueries(org.apache.cayenne.map.DbEntity)
+     */
+    @Override
+    public List<String> createSql(DbAdapter adapter) {
+        // TODO: skip FK to a different DB
+        if (!this.isEmpty()) {
+            String fksql = adapter.createFkConstraint(relationship);
+            if (fksql != null) {
+                return Collections.singletonList(fksql);
+            }
+        }
+        return Collections.emptyList();
+    }
+
+    @Override
+    public MergerToken createReverse(MergerTokenFactory factory) {
+        return factory.createDropRelationshipToModel(getEntity(), relationship);
+    }
+
+    @Override
+    public String getTokenValue() {
+        if (!this.isEmpty()) {
+            return relationship.getSourceEntity().getName() + "->" + relationship.getTargetEntityName();
+        } else {
+            return "Skip. No sql representation.";
+        }
+    }
+
+    @Override
+    public boolean isEmpty() {
+        return relationship.isSourceIndependentFromTargetChange();
+    }
+
+    @Override
+    public int compareTo(MergerToken o) {
+        // add all AddRelationshipToDb to the end.
+        if (o instanceof AddRelationshipToDb) {
+            return super.compareTo(o);
+        }
+        return 1;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/725e6ae9/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/token/db/CreateTableToDb.java
----------------------------------------------------------------------
diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/token/db/CreateTableToDb.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/token/db/CreateTableToDb.java
new file mode 100644
index 0000000..ac2305b
--- /dev/null
+++ b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/token/db/CreateTableToDb.java
@@ -0,0 +1,89 @@
+/*****************************************************************
+ *   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.token.db;
+
+import org.apache.cayenne.access.DataNode;
+import org.apache.cayenne.dba.DbAdapter;
+import org.apache.cayenne.dbsync.merge.context.MergerContext;
+import org.apache.cayenne.dbsync.merge.factory.MergerTokenFactory;
+import org.apache.cayenne.dbsync.merge.token.MergerToken;
+import org.apache.cayenne.map.DbAttribute;
+import org.apache.cayenne.map.DbEntity;
+import org.apache.cayenne.validation.SimpleValidationFailure;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+public class CreateTableToDb extends AbstractToDbToken.Entity {
+
+    public CreateTableToDb(DbEntity entity) {
+        super("Create Table", entity);
+    }
+
+    @Override
+    public List<String> createSql(DbAdapter adapter) {
+        List<String> sqls = new ArrayList<String>();
+        if(needAutoPkSupport()) {
+            sqls.addAll(adapter.getPkGenerator().createAutoPkStatements(
+                    Collections.singletonList(getEntity())));
+        }
+        sqls.add(adapter.createTable(getEntity()));
+        return sqls;
+    }
+
+    @Override
+    public void execute(MergerContext mergerContext) {
+        try {
+            DataNode node = mergerContext.getDataNode();
+            DbAdapter adapter = node.getAdapter();
+            if(needAutoPkSupport()) {
+                adapter.getPkGenerator().createAutoPk(
+                        node,
+                        Collections.singletonList(getEntity()));
+            }
+            executeSql(mergerContext, adapter.createTable(getEntity()));
+        }
+        catch (Exception e) {
+            mergerContext.getValidationResult().addFailure(
+                    new SimpleValidationFailure(this, e.getMessage()));
+        }
+    }
+
+    private boolean needAutoPkSupport() {
+        DbEntity entity = getEntity();
+        if(entity.getPrimaryKeyGenerator() != null) {
+            return false;
+        }
+
+        for(DbAttribute attribute : entity.getPrimaryKeys()) {
+            if(attribute.isGenerated()) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    public MergerToken createReverse(MergerTokenFactory factory) {
+        return factory.createDropTableToModel(getEntity());
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/725e6ae9/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/token/db/DropColumnToDb.java
----------------------------------------------------------------------
diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/token/db/DropColumnToDb.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/token/db/DropColumnToDb.java
new file mode 100644
index 0000000..fc95745
--- /dev/null
+++ b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/token/db/DropColumnToDb.java
@@ -0,0 +1,63 @@
+/*****************************************************************
+ *   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.token.db;
+
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.cayenne.dba.DbAdapter;
+import org.apache.cayenne.dba.QuotingStrategy;
+import org.apache.cayenne.dbsync.merge.factory.MergerTokenFactory;
+import org.apache.cayenne.dbsync.merge.token.MergerToken;
+import org.apache.cayenne.map.DbAttribute;
+import org.apache.cayenne.map.DbEntity;
+
+public class DropColumnToDb extends AbstractToDbToken.EntityAndColumn {
+
+    public DropColumnToDb(DbEntity entity, DbAttribute column) {
+        super("Drop Column", entity, column);
+    }
+
+    @Override
+    public List<String> createSql(DbAdapter adapter) {
+        StringBuilder sqlBuffer = new StringBuilder();
+        QuotingStrategy context = adapter.getQuotingStrategy();
+        sqlBuffer.append("ALTER TABLE ");
+        sqlBuffer.append(context.quotedFullyQualifiedName(getEntity()));
+        sqlBuffer.append(" DROP COLUMN ");
+        sqlBuffer.append(context.quotedName(getColumn()));
+
+        return Collections.singletonList(sqlBuffer.toString());
+    }
+
+    public MergerToken createReverse(MergerTokenFactory factory) {
+        return factory.createAddColumnToModel(getEntity(), getColumn());
+    }
+
+    @Override
+    public int compareTo(MergerToken o) {
+        // add all AddRelationshipToDb to the end.
+        if (o instanceof DropRelationshipToDb) {
+            return 1;
+        }
+        return super.compareTo(o);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/725e6ae9/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/token/db/DropRelationshipToDb.java
----------------------------------------------------------------------
diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/token/db/DropRelationshipToDb.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/token/db/DropRelationshipToDb.java
new file mode 100644
index 0000000..f58ae59
--- /dev/null
+++ b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/token/db/DropRelationshipToDb.java
@@ -0,0 +1,85 @@
+/*****************************************************************
+ *   Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ ****************************************************************/
+
+package org.apache.cayenne.dbsync.merge.token.db;
+
+import org.apache.cayenne.dba.DbAdapter;
+import org.apache.cayenne.dba.QuotingStrategy;
+import org.apache.cayenne.dbsync.merge.factory.MergerTokenFactory;
+import org.apache.cayenne.dbsync.merge.token.MergerToken;
+import org.apache.cayenne.map.DbEntity;
+import org.apache.cayenne.map.DbRelationship;
+import org.apache.cayenne.dbsync.reverse.db.DbRelationshipDetected;
+
+import java.util.Collections;
+import java.util.List;
+
+public class DropRelationshipToDb extends AbstractToDbToken.Entity {
+
+    private DbRelationship relationship;
+
+    public DropRelationshipToDb(DbEntity entity, DbRelationship relationship) {
+        super("Drop foreign key", entity);
+        this.relationship = relationship;
+    }
+    
+    public String getFkName() {
+        if (relationship instanceof DbRelationshipDetected) {
+            return ((DbRelationshipDetected) relationship).getFkName();
+        }
+        return null;
+    }
+
+    @Override
+    public List<String> createSql(DbAdapter adapter) {
+        if (isEmpty()) {
+            return Collections.emptyList();
+        }
+
+        QuotingStrategy context = adapter.getQuotingStrategy();
+        return Collections.singletonList(
+                "ALTER TABLE " + context.quotedFullyQualifiedName(getEntity()) + " DROP CONSTRAINT " + getFkName());
+    }
+
+    public MergerToken createReverse(MergerTokenFactory factory) {
+        return factory.createAddRelationshipToModel(getEntity(), relationship);
+    }
+
+    @Override
+    public boolean isEmpty() {
+        return getFkName() == null || relationship.isToMany();
+    }
+
+    @Override
+    public String getTokenValue() {
+        if(relationship.isToMany()) {
+            return "Skip. No sql representation.";
+        }
+        return relationship.getSourceEntity().getName() + "->" + relationship.getTargetEntityName();
+    }
+
+    @Override
+    public int compareTo(MergerToken o) {
+        // add all AddRelationshipToDb to the end.
+        if (o instanceof DropRelationshipToDb) {
+            return super.compareTo(o);
+        }
+        return -1;
+    }
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/725e6ae9/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/token/db/DropTableToDb.java
----------------------------------------------------------------------
diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/token/db/DropTableToDb.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/token/db/DropTableToDb.java
new file mode 100644
index 0000000..0c202d5
--- /dev/null
+++ b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/token/db/DropTableToDb.java
@@ -0,0 +1,53 @@
+/*****************************************************************
+ *   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.token.db;
+
+import org.apache.cayenne.dba.DbAdapter;
+import org.apache.cayenne.dbsync.merge.factory.MergerTokenFactory;
+import org.apache.cayenne.dbsync.merge.token.MergerToken;
+import org.apache.cayenne.map.DbEntity;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class DropTableToDb extends AbstractToDbToken.Entity {
+
+    public DropTableToDb(DbEntity entity) {
+        super("Drop Table", entity);
+    }
+
+    @Override
+    public List<String> createSql(DbAdapter adapter) {
+        List<String> sqls = new ArrayList<>();
+        // TODO: fix. some adapters drop the complete AUTO_PK_SUPPORT here
+        /*
+        sqls.addAll(adapter.getPkGenerator().dropAutoPkStatements(
+                Collections.singletonList(entity)));
+         */
+        sqls.addAll(adapter.dropTableStatements(getEntity()));
+        return sqls;
+    }
+
+    @Override
+    public MergerToken createReverse(MergerTokenFactory factory) {
+        return factory.createCreateTableToModel(getEntity());
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/725e6ae9/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/token/db/SetAllowNullToDb.java
----------------------------------------------------------------------
diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/token/db/SetAllowNullToDb.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/token/db/SetAllowNullToDb.java
new file mode 100644
index 0000000..0c25db9
--- /dev/null
+++ b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/token/db/SetAllowNullToDb.java
@@ -0,0 +1,60 @@
+/*****************************************************************
+ *   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.token.db;
+
+import org.apache.cayenne.dba.DbAdapter;
+import org.apache.cayenne.dba.QuotingStrategy;
+import org.apache.cayenne.dbsync.merge.factory.MergerTokenFactory;
+import org.apache.cayenne.dbsync.merge.token.MergerToken;
+import org.apache.cayenne.map.DbAttribute;
+import org.apache.cayenne.map.DbEntity;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * A {@link MergerToken} to add a "allow null" clause to a column.
+ * 
+ */
+public class SetAllowNullToDb extends AbstractToDbToken.EntityAndColumn {
+
+    public SetAllowNullToDb(DbEntity entity, DbAttribute column) {
+        super("Set Allow Null", entity, column);
+    }
+
+    @Override
+    public List<String> createSql(DbAdapter adapter) {
+        StringBuilder sqlBuffer = new StringBuilder();
+        QuotingStrategy context = adapter.getQuotingStrategy();
+        sqlBuffer.append("ALTER TABLE ");
+        sqlBuffer.append(context.quotedFullyQualifiedName(getEntity()));
+        sqlBuffer.append(" ALTER COLUMN ");
+        sqlBuffer.append(context.quotedName(getColumn()));
+        sqlBuffer.append(" DROP NOT NULL");
+
+        return Collections.singletonList(sqlBuffer.toString());
+    }
+
+    @Override
+    public MergerToken createReverse(MergerTokenFactory factory) {
+        return factory.createSetNotNullToModel(getEntity(), getColumn());
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/725e6ae9/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/token/db/SetColumnTypeToDb.java
----------------------------------------------------------------------
diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/token/db/SetColumnTypeToDb.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/token/db/SetColumnTypeToDb.java
new file mode 100644
index 0000000..d5e9988
--- /dev/null
+++ b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/token/db/SetColumnTypeToDb.java
@@ -0,0 +1,113 @@
+/*****************************************************************
+ *   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.token.db;
+
+import org.apache.cayenne.dba.DbAdapter;
+import org.apache.cayenne.dba.JdbcAdapter;
+import org.apache.cayenne.dba.QuotingStrategy;
+import org.apache.cayenne.dba.TypesMapping;
+import org.apache.cayenne.dbsync.merge.factory.MergerTokenFactory;
+import org.apache.cayenne.dbsync.merge.token.MergerToken;
+import org.apache.cayenne.map.DbAttribute;
+import org.apache.cayenne.map.DbEntity;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * An {@link MergerToken} to use to set type, length and precision.
+ */
+public class SetColumnTypeToDb extends AbstractToDbToken.Entity {
+
+    private DbAttribute columnOriginal;
+    private DbAttribute columnNew;
+
+    public SetColumnTypeToDb(DbEntity entity, DbAttribute columnOriginal, DbAttribute columnNew) {
+        super("Set Column Type", entity);
+        this.columnOriginal = columnOriginal;
+        this.columnNew = columnNew;
+    }
+    
+    /**
+     * append the part of the token before the actual column data type
+     * @param context 
+     */
+    protected void appendPrefix(StringBuffer sqlBuffer, QuotingStrategy context) {
+        sqlBuffer.append("ALTER TABLE ");
+        sqlBuffer.append(context.quotedFullyQualifiedName(getEntity()));
+        sqlBuffer.append(" ALTER ");
+        sqlBuffer.append(context.quotedName(columnNew));
+        sqlBuffer.append(" TYPE ");
+    }
+
+    @Override
+    public List<String> createSql(DbAdapter adapter) {
+        StringBuffer sqlBuffer = new StringBuffer();
+        appendPrefix(sqlBuffer, adapter.getQuotingStrategy());
+  
+        sqlBuffer.append(JdbcAdapter.getType(adapter, columnNew));
+        sqlBuffer.append(JdbcAdapter.sizeAndPrecision(adapter, columnNew));
+
+        return Collections.singletonList(sqlBuffer.toString());
+    }
+
+    @Override
+    public String getTokenValue() {
+        StringBuilder sb = new StringBuilder();
+        sb.append(getEntity().getName());
+        sb.append(".");
+        sb.append(columnNew.getName());
+
+        if (columnOriginal.getType() != columnNew.getType()) {
+            sb.append(" type: ");
+            sb.append(TypesMapping.getSqlNameByType(columnOriginal.getType()));
+            sb.append(" -> ");
+            sb.append(TypesMapping.getSqlNameByType(columnNew.getType()));
+        }
+
+        if (columnOriginal.getMaxLength() != columnNew.getMaxLength()) {
+            sb.append(" maxLength: ");
+            sb.append(columnOriginal.getMaxLength());
+            sb.append(" -> ");
+            sb.append(columnNew.getMaxLength());
+        }
+
+        if (columnOriginal.getAttributePrecision() != columnNew.getAttributePrecision()) {
+            sb.append(" precision: ");
+            sb.append(columnOriginal.getAttributePrecision());
+            sb.append(" -> ");
+            sb.append(columnNew.getAttributePrecision());
+        }
+
+        if (columnOriginal.getScale() != columnNew.getScale()) {
+            sb.append(" scale: ");
+            sb.append(columnOriginal.getScale());
+            sb.append(" -> ");
+            sb.append(columnNew.getScale());
+        }
+
+        return sb.toString();
+    }
+
+    @Override
+    public MergerToken createReverse(MergerTokenFactory factory) {
+        return factory.createSetColumnTypeToModel(getEntity(), columnNew, columnOriginal);
+    }
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/725e6ae9/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/token/db/SetNotNullToDb.java
----------------------------------------------------------------------
diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/token/db/SetNotNullToDb.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/token/db/SetNotNullToDb.java
new file mode 100644
index 0000000..34af9aa
--- /dev/null
+++ b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/token/db/SetNotNullToDb.java
@@ -0,0 +1,54 @@
+/*****************************************************************
+ *   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.token.db;
+
+import org.apache.cayenne.dba.DbAdapter;
+import org.apache.cayenne.dba.QuotingStrategy;
+import org.apache.cayenne.dbsync.merge.factory.MergerTokenFactory;
+import org.apache.cayenne.dbsync.merge.token.MergerToken;
+import org.apache.cayenne.map.DbAttribute;
+import org.apache.cayenne.map.DbEntity;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * A {@link MergerToken} to add a "not null" clause to a column.
+ */
+public class SetNotNullToDb extends AbstractToDbToken.EntityAndColumn {
+
+    public SetNotNullToDb(DbEntity entity, DbAttribute column) {
+        super("Set Not Null", entity, column);
+    }
+
+    @Override
+    public List<String> createSql(DbAdapter adapter) {
+        QuotingStrategy context = adapter.getQuotingStrategy();
+
+        return Collections.singletonList("ALTER TABLE " + context.quotedFullyQualifiedName(getEntity())
+                + " ALTER COLUMN " + context.quotedName(getColumn()) + " SET NOT NULL");
+    }
+
+    @Override
+    public MergerToken createReverse(MergerTokenFactory factory) {
+        return factory.createSetAllowNullToModel(getEntity(), getColumn());
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/725e6ae9/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/token/db/SetPrimaryKeyToDb.java
----------------------------------------------------------------------
diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/token/db/SetPrimaryKeyToDb.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/token/db/SetPrimaryKeyToDb.java
new file mode 100644
index 0000000..c48133a
--- /dev/null
+++ b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/token/db/SetPrimaryKeyToDb.java
@@ -0,0 +1,89 @@
+/*****************************************************************
+ *   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.token.db;
+
+import org.apache.cayenne.dba.DbAdapter;
+import org.apache.cayenne.dba.QuotingStrategy;
+import org.apache.cayenne.dbsync.merge.factory.MergerTokenFactory;
+import org.apache.cayenne.dbsync.merge.token.MergerToken;
+import org.apache.cayenne.map.DbAttribute;
+import org.apache.cayenne.map.DbEntity;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+
+public class SetPrimaryKeyToDb extends AbstractToDbToken.Entity {
+
+    private Collection<DbAttribute> primaryKeyOriginal;
+    private Collection<DbAttribute> primaryKeyNew;
+    private String detectedPrimaryKeyName;
+
+    public SetPrimaryKeyToDb(DbEntity entity, Collection<DbAttribute> primaryKeyOriginal,
+            Collection<DbAttribute> primaryKeyNew, String detectedPrimaryKeyName) {
+        super("Set Primary Key", entity);
+
+        this.primaryKeyOriginal = primaryKeyOriginal;
+        this.primaryKeyNew = primaryKeyNew;
+        this.detectedPrimaryKeyName = detectedPrimaryKeyName;
+    }
+
+    @Override
+    public List<String> createSql(DbAdapter adapter) {
+        List<String> sqls = new ArrayList<String>();
+        if (!primaryKeyOriginal.isEmpty()) {
+            appendDropOriginalPrimaryKeySQL(adapter, sqls);
+        }
+        appendAddNewPrimaryKeySQL(adapter, sqls);
+        return sqls;
+    }
+
+    protected void appendDropOriginalPrimaryKeySQL(DbAdapter adapter, List<String> sqls) {
+        if (detectedPrimaryKeyName == null) {
+            return;
+        }
+        sqls.add("ALTER TABLE " + adapter.getQuotingStrategy().quotedFullyQualifiedName(getEntity())
+                + " DROP CONSTRAINT " + detectedPrimaryKeyName);
+    }
+
+    protected void appendAddNewPrimaryKeySQL(DbAdapter adapter, List<String> sqls) {
+        QuotingStrategy quotingStrategy = adapter.getQuotingStrategy();
+
+        StringBuilder sql = new StringBuilder();
+        sql.append("ALTER TABLE ");
+        sql.append(quotingStrategy.quotedFullyQualifiedName(getEntity()));
+        sql.append(" ADD PRIMARY KEY (");
+        for (Iterator<DbAttribute> it = primaryKeyNew.iterator(); it.hasNext();) {
+            sql.append(quotingStrategy.quotedName(it.next()));
+            if (it.hasNext()) {
+                sql.append(", ");
+            }
+        }
+        sql.append(")");
+        sqls.add(sql.toString());
+    }
+
+    @Override
+    public MergerToken createReverse(MergerTokenFactory factory) {
+        return factory.createSetPrimaryKeyToModel(getEntity(), primaryKeyNew, primaryKeyOriginal,
+                detectedPrimaryKeyName);
+    }
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/725e6ae9/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/token/db/SetValueForNullToDb.java
----------------------------------------------------------------------
diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/token/db/SetValueForNullToDb.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/token/db/SetValueForNullToDb.java
new file mode 100644
index 0000000..2091847
--- /dev/null
+++ b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/token/db/SetValueForNullToDb.java
@@ -0,0 +1,52 @@
+/*****************************************************************
+ *   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.token.db;
+
+import org.apache.cayenne.dba.DbAdapter;
+import org.apache.cayenne.dbsync.merge.factory.MergerTokenFactory;
+import org.apache.cayenne.dbsync.merge.token.DummyReverseToken;
+import org.apache.cayenne.dbsync.merge.token.MergerToken;
+import org.apache.cayenne.dbsync.merge.token.ValueForNullProvider;
+import org.apache.cayenne.map.DbAttribute;
+import org.apache.cayenne.map.DbEntity;
+
+import java.util.List;
+
+
+public class SetValueForNullToDb extends AbstractToDbToken.EntityAndColumn {
+    
+    private ValueForNullProvider valueForNullProvider;
+
+    public SetValueForNullToDb(DbEntity entity, DbAttribute column, ValueForNullProvider valueForNullProvider) {
+        super("Set value for null", entity, column);
+        this.valueForNullProvider = valueForNullProvider;
+    }
+    
+    @Override
+    public List<String> createSql(DbAdapter adapter) {
+        return valueForNullProvider.createSql(getEntity(), getColumn());
+    }
+
+    @Override
+    public MergerToken createReverse(MergerTokenFactory factory) {
+        return new DummyReverseToken(this);
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/725e6ae9/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/token/model/AbstractToModelToken.java
----------------------------------------------------------------------
diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/token/model/AbstractToModelToken.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/token/model/AbstractToModelToken.java
new file mode 100644
index 0000000..cdf0a74
--- /dev/null
+++ b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/token/model/AbstractToModelToken.java
@@ -0,0 +1,129 @@
+/*****************************************************************
+ *   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.token.model;
+
+import org.apache.cayenne.dbsync.merge.context.MergeDirection;
+import org.apache.cayenne.dbsync.merge.token.MergerToken;
+import org.apache.cayenne.dbsync.reverse.dbload.ModelMergeDelegate;
+import org.apache.cayenne.map.DbAttribute;
+import org.apache.cayenne.map.DbEntity;
+import org.apache.cayenne.map.DbRelationship;
+import org.apache.cayenne.map.ObjEntity;
+import org.apache.cayenne.map.ObjRelationship;
+
+/**
+ * Common abstract superclass for all {@link MergerToken}s going from the database to the
+ * model.
+ */
+public abstract class AbstractToModelToken implements MergerToken {
+
+    private final String tokenName;
+
+    protected AbstractToModelToken(String tokenName) {
+        this.tokenName = tokenName;
+    }
+
+    protected static void remove(ModelMergeDelegate mergerContext, DbRelationship rel, boolean reverse) {
+        if (rel == null) {
+            return;
+        }
+        if (reverse) {
+            remove(mergerContext, rel.getReverseRelationship(), false);
+        }
+
+        DbEntity dbEntity = rel.getSourceEntity();
+        for (ObjEntity objEntity : dbEntity.mappedObjEntities()) {
+            remove(mergerContext, objEntity.getRelationshipForDbRelationship(rel), true);
+        }
+
+        rel.getSourceEntity().removeRelationship(rel.getName());
+        mergerContext.dbRelationshipRemoved(rel);
+    }
+
+    protected static void remove(ModelMergeDelegate mergerContext, ObjRelationship rel, boolean reverse) {
+        if (rel == null) {
+            return;
+        }
+        if (reverse) {
+            remove(mergerContext, rel.getReverseRelationship(), false);
+        }
+        rel.getSourceEntity().removeRelationship(rel.getName());
+        mergerContext.objRelationshipRemoved(rel);
+    }
+
+    @Override
+    public final String getTokenName() {
+        return tokenName;
+    }
+
+    @Override
+    public final MergeDirection getDirection() {
+        return MergeDirection.TO_MODEL;
+    }
+
+    @Override
+    public String toString() {
+        return getTokenName() + ' ' + getTokenValue() + ' ' + getDirection();
+    }
+
+    public boolean isEmpty() {
+        return false;
+    }
+
+    abstract static class Entity extends AbstractToModelToken {
+
+        private final DbEntity entity;
+
+        protected Entity(String tokenName, DbEntity entity) {
+            super(tokenName);
+            this.entity = entity;
+        }
+
+        public DbEntity getEntity() {
+            return entity;
+        }
+
+        public String getTokenValue() {
+            return getEntity().getName();
+        }
+
+    }
+
+    abstract static class EntityAndColumn extends Entity {
+
+        private final DbAttribute column;
+
+        protected EntityAndColumn(String tokenName, DbEntity entity, DbAttribute column) {
+            super(tokenName, entity);
+            this.column = column;
+        }
+
+        public DbAttribute getColumn() {
+            return column;
+        }
+
+        @Override
+        public String getTokenValue() {
+            return getEntity().getName() + "." + getColumn().getName();
+        }
+
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/725e6ae9/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/token/model/AddColumnToModel.java
----------------------------------------------------------------------
diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/token/model/AddColumnToModel.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/token/model/AddColumnToModel.java
new file mode 100644
index 0000000..5832774
--- /dev/null
+++ b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/token/model/AddColumnToModel.java
@@ -0,0 +1,56 @@
+/*****************************************************************
+ *   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.token.model;
+
+import org.apache.cayenne.dbsync.merge.context.EntityMergeSupport;
+import org.apache.cayenne.dbsync.merge.context.MergerContext;
+import org.apache.cayenne.dbsync.merge.factory.MergerTokenFactory;
+import org.apache.cayenne.dbsync.merge.token.MergerToken;
+import org.apache.cayenne.map.DbAttribute;
+import org.apache.cayenne.map.DbEntity;
+import org.apache.cayenne.map.ObjEntity;
+
+/**
+ * A {@link MergerToken} to add a {@link DbAttribute} to a {@link DbEntity}. The
+ * {@link EntityMergeSupport} will be used to update the mapped {@link ObjEntity}
+ */
+public class AddColumnToModel extends AbstractToModelToken.EntityAndColumn {
+
+    public AddColumnToModel(DbEntity entity, DbAttribute column) {
+        super("Add Column", entity, column);
+    }
+
+    @Override
+    public MergerToken createReverse(MergerTokenFactory factory) {
+        return factory.createDropColumnToDb(getEntity(), getColumn());
+    }
+
+    @Override
+    public void execute(MergerContext mergerContext) {
+        getEntity().addAttribute(getColumn());
+
+        for (ObjEntity e : getEntity().mappedObjEntities()) {
+            mergerContext.getEntityMergeSupport().synchronizeOnDbAttributeAdded(e, getColumn());
+        }
+
+        mergerContext.getDelegate().dbAttributeAdded(getColumn());
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/725e6ae9/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/token/model/AddRelationshipToModel.java
----------------------------------------------------------------------
diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/token/model/AddRelationshipToModel.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/token/model/AddRelationshipToModel.java
new file mode 100644
index 0000000..73f611a
--- /dev/null
+++ b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/token/model/AddRelationshipToModel.java
@@ -0,0 +1,92 @@
+/*****************************************************************
+ *   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.token.model;
+
+import org.apache.cayenne.dbsync.merge.context.MergerContext;
+import org.apache.cayenne.dbsync.merge.factory.MergerTokenFactory;
+import org.apache.cayenne.dbsync.merge.token.MergerToken;
+import org.apache.cayenne.map.DbEntity;
+import org.apache.cayenne.map.DbJoin;
+import org.apache.cayenne.map.DbRelationship;
+import org.apache.cayenne.map.ObjEntity;
+
+public class AddRelationshipToModel extends AbstractToModelToken.Entity {
+
+    public static final String COMMA_SEPARATOR = ", ";
+    public static final int COMMA_SEPARATOR_LENGTH = COMMA_SEPARATOR.length();
+
+    private DbRelationship relationship;
+
+    public AddRelationshipToModel(DbEntity entity, DbRelationship relationship) {
+        super("Add Relationship", entity);
+        this.relationship = relationship;
+    }
+
+    public static String getTokenValue(DbRelationship rel) {
+        String attributes = "";
+        if (rel.getJoins().size() == 1) {
+            attributes = rel.getJoins().get(0).getTargetName();
+        } else {
+            for (DbJoin dbJoin : rel.getJoins()) {
+                attributes += dbJoin.getTargetName() + COMMA_SEPARATOR;
+            }
+
+            attributes = "{" + attributes.substring(0, attributes.length() - COMMA_SEPARATOR_LENGTH) + "}";
+        }
+
+        return rel.getName() + " " + rel.getSourceEntity().getName() + "->" + rel.getTargetEntityName() + "." + attributes;
+    }
+
+    @Override
+    public MergerToken createReverse(MergerTokenFactory factory) {
+        return factory.createDropRelationshipToDb(getEntity(), relationship);
+    }
+
+    @Override
+    public void execute(MergerContext context) {
+        // Set name to relationship if it was created without it, e.g. in createReverse() action
+        if(relationship.getName() == null) {
+            relationship.setName(context.getNameGenerator().relationshipName(relationship));
+        }
+
+        getEntity().addRelationship(relationship);
+        for (ObjEntity e : getEntity().mappedObjEntities()) {
+            context.getEntityMergeSupport().synchronizeOnDbRelationshipAdded(e, relationship);
+        }
+
+        context.getDelegate().dbRelationshipAdded(relationship);
+    }
+
+    @Override
+    public String getTokenValue() {
+        String attributes = "";
+        if (relationship.getJoins().size() == 1) {
+            attributes = relationship.getJoins().get(0).getTargetName();
+        } else {
+            for (DbJoin dbJoin : relationship.getJoins()) {
+                attributes += dbJoin.getTargetName() + COMMA_SEPARATOR;
+            }
+
+            attributes = "{" + attributes.substring(0, attributes.length() - COMMA_SEPARATOR_LENGTH) + "}";
+        }
+
+        return relationship.getName() + " " + relationship.getSourceEntity().getName() + "->" + relationship.getTargetEntityName() + "." + attributes;
+    }
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/725e6ae9/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/token/model/CreateTableToModel.java
----------------------------------------------------------------------
diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/token/model/CreateTableToModel.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/token/model/CreateTableToModel.java
new file mode 100644
index 0000000..05ae2e1
--- /dev/null
+++ b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/token/model/CreateTableToModel.java
@@ -0,0 +1,101 @@
+/*****************************************************************
+ *   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.token.model;
+
+import org.apache.cayenne.dbsync.merge.context.MergerContext;
+import org.apache.cayenne.dbsync.merge.factory.MergerTokenFactory;
+import org.apache.cayenne.dbsync.merge.token.MergerToken;
+import org.apache.cayenne.dbsync.naming.NameBuilder;
+import org.apache.cayenne.map.DataMap;
+import org.apache.cayenne.map.DbEntity;
+import org.apache.cayenne.map.ObjEntity;
+
+/**
+ * A {@link MergerToken} to add a {@link DbEntity} to a {@link DataMap}
+ */
+public class CreateTableToModel extends AbstractToModelToken.Entity {
+
+    /**
+     * className if {@link ObjEntity} should be generated with a
+     * special class name.
+     * Setting this to <code>null</code>, because by default class name should be generated
+     */
+    private String objEntityClassName;
+
+    public CreateTableToModel(DbEntity entity) {
+        super("Create Table", entity);
+    }
+
+    /**
+     * Set the {@link ObjEntity} className if {@link ObjEntity} should be generated with a
+     * special class name. Set to null if the {@link ObjEntity} should be created with a
+     * name based on {@link DataMap#getDefaultPackage()} and {@link ObjEntity#getName()}
+     * <p>
+     * The default value is <code>null</code>
+     */
+    public void setObjEntityClassName(String n) {
+        objEntityClassName = n;
+    }
+
+    @Override
+    public void execute(MergerContext context) {
+        DbEntity dbEntity = getEntity();
+
+        DataMap map = context.getDataMap();
+        map.addDbEntity(dbEntity);
+
+        // create a ObjEntity
+        ObjEntity objEntity = new ObjEntity();
+
+        objEntity.setName(NameBuilder
+                .builder(objEntity, dbEntity.getDataMap())
+                .baseName(context.getNameGenerator().objEntityName(dbEntity))
+                .name());
+        objEntity.setDbEntity(getEntity());
+
+        // try to find a class name for the ObjEntity
+        String className = objEntityClassName;
+        if (className == null) {
+            // generate a className based on the objEntityName
+            className = map.getNameWithDefaultPackage(objEntity.getName());
+        }
+
+        objEntity.setClassName(className);
+        objEntity.setSuperClassName(map.getDefaultSuperclass());
+
+        if (map.isClientSupported()) {
+            objEntity.setClientClassName(map.getNameWithDefaultClientPackage(objEntity.getName()));
+            objEntity.setClientSuperClassName(map.getDefaultClientSuperclass());
+        }
+
+        map.addObjEntity(objEntity);
+
+        // presumably there are no other ObjEntities pointing to this DbEntity, so syncing just this one...
+        context.getEntityMergeSupport().synchronizeWithDbEntity(objEntity);
+
+        context.getDelegate().dbEntityAdded(getEntity());
+        context.getDelegate().objEntityAdded(objEntity);
+    }
+
+    public MergerToken createReverse(MergerTokenFactory factory) {
+        return factory.createDropTableToDb(getEntity());
+    }
+
+}