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:39 UTC
[03/16] cayenne git commit: New DbMerger for dbsync utils - merger
process split in independent steps - new attributes compared (full type
comparision of DbAttribute)
http://git-wip-us.apache.org/repos/asf/cayenne/blob/46c8ded5/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/SetPrimaryKeyToDb.java
----------------------------------------------------------------------
diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/SetPrimaryKeyToDb.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/SetPrimaryKeyToDb.java
deleted file mode 100644
index 077b6ef..0000000
--- a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/SetPrimaryKeyToDb.java
+++ /dev/null
@@ -1,87 +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.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/46c8ded5/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/SetPrimaryKeyToModel.java
----------------------------------------------------------------------
diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/SetPrimaryKeyToModel.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/SetPrimaryKeyToModel.java
deleted file mode 100644
index a2198ba..0000000
--- a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/SetPrimaryKeyToModel.java
+++ /dev/null
@@ -1,80 +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.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/46c8ded5/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/SetValueForNullToDb.java
----------------------------------------------------------------------
diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/SetValueForNullToDb.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/SetValueForNullToDb.java
deleted file mode 100644
index 340f2bf..0000000
--- a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/SetValueForNullToDb.java
+++ /dev/null
@@ -1,48 +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.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/46c8ded5/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/TokenComparator.java
----------------------------------------------------------------------
diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/TokenComparator.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/TokenComparator.java
deleted file mode 100644
index 4cc312f..0000000
--- a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/TokenComparator.java
+++ /dev/null
@@ -1,46 +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 java.util.Comparator;
-
-/**
- * Simple sort of merge tokens.
- * Just move all relationships creation tokens to the end of the list.
- */
-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 AddRelationshipToModel && o2 instanceof AddRelationshipToModel) {
- return 0;
- }
-
- if (!(o1 instanceof AddRelationshipToModel || o2 instanceof AddRelationshipToModel)) {
- return o1.getClass().getSimpleName().compareTo(o2.getClass().getSimpleName());
- }
-
- return o1 instanceof AddRelationshipToModel ? 1 : -1;
- }
-}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/46c8ded5/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/ValueForNullProvider.java
----------------------------------------------------------------------
diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/ValueForNullProvider.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/ValueForNullProvider.java
deleted file mode 100644
index 6bbd68a..0000000
--- a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/ValueForNullProvider.java
+++ /dev/null
@@ -1,41 +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.map.DbAttribute;
-import org.apache.cayenne.map.DbEntity;
-
-import java.util.List;
-
-/**
- * Class that will be used to set value for null on not null columns
- */
-public interface ValueForNullProvider {
-
- /**
- * @return true if there exist a value that should be inserted for null values
- */
- boolean hasValueFor(DbEntity entity, DbAttribute column);
-
- /**
- * @return a {@link List} of sql to set value for null
- */
- List<String> createSql(DbEntity entity, DbAttribute column);
-
-}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/46c8ded5/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/context/EntityMergeSupport.java
----------------------------------------------------------------------
diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/context/EntityMergeSupport.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/context/EntityMergeSupport.java
new file mode 100644
index 0000000..9bb2a84
--- /dev/null
+++ b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/context/EntityMergeSupport.java
@@ -0,0 +1,516 @@
+/*****************************************************************
+ * 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.context;
+
+import org.apache.cayenne.dba.TypesMapping;
+import org.apache.cayenne.dbsync.filter.NameFilter;
+import org.apache.cayenne.dbsync.naming.NameBuilder;
+import org.apache.cayenne.dbsync.naming.ObjectNameGenerator;
+import org.apache.cayenne.map.DataMap;
+import org.apache.cayenne.map.DbAttribute;
+import org.apache.cayenne.map.DbEntity;
+import org.apache.cayenne.map.DbJoin;
+import org.apache.cayenne.map.DbRelationship;
+import org.apache.cayenne.map.Entity;
+import org.apache.cayenne.map.ObjAttribute;
+import org.apache.cayenne.map.ObjEntity;
+import org.apache.cayenne.map.ObjRelationship;
+import org.apache.cayenne.map.Relationship;
+import org.apache.cayenne.util.DeleteRuleUpdater;
+import org.apache.cayenne.util.EntityMergeListener;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Implements methods for entity merging.
+ */
+public class EntityMergeSupport {
+
+ private static final Log LOGGER = LogFactory.getLog(EntityMergeSupport.class);
+
+ private static final Map<String, String> CLASS_TO_PRIMITIVE;
+
+ static {
+ CLASS_TO_PRIMITIVE = new HashMap<>();
+ CLASS_TO_PRIMITIVE.put(Byte.class.getName(), "byte");
+ CLASS_TO_PRIMITIVE.put(Long.class.getName(), "long");
+ CLASS_TO_PRIMITIVE.put(Double.class.getName(), "double");
+ CLASS_TO_PRIMITIVE.put(Boolean.class.getName(), "boolean");
+ CLASS_TO_PRIMITIVE.put(Float.class.getName(), "float");
+ CLASS_TO_PRIMITIVE.put(Short.class.getName(), "short");
+ CLASS_TO_PRIMITIVE.put(Integer.class.getName(), "int");
+ }
+
+ private final ObjectNameGenerator nameGenerator;
+ private final List<EntityMergeListener> listeners;
+ private final boolean removingMeaningfulFKs;
+ private final NameFilter meaningfulPKsFilter;
+ private final boolean usingPrimitives;
+
+ public EntityMergeSupport(ObjectNameGenerator nameGenerator,
+ NameFilter meaningfulPKsFilter,
+ boolean removingMeaningfulFKs,
+ boolean usingPrimitives) {
+
+ this.listeners = new ArrayList<>();
+ this.nameGenerator = nameGenerator;
+ this.removingMeaningfulFKs = removingMeaningfulFKs;
+ this.meaningfulPKsFilter = meaningfulPKsFilter;
+ this.usingPrimitives = usingPrimitives;
+
+ // will ensure that all created ObjRelationships would have
+ // default delete rule
+ addEntityMergeListener(DeleteRuleUpdater.getEntityMergeListener());
+ }
+
+ public boolean isRemovingMeaningfulFKs() {
+ return removingMeaningfulFKs;
+ }
+
+
+ /**
+ * Updates each one of the collection of ObjEntities, adding attributes and
+ * relationships based on the current state of its DbEntity.
+ *
+ * @return true if any ObjEntity has changed as a result of synchronization.
+ */
+ public boolean synchronizeWithDbEntities(Iterable<ObjEntity> objEntities) {
+ boolean changed = false;
+ for (ObjEntity nextEntity : objEntities) {
+ if (synchronizeWithDbEntity(nextEntity)) {
+ changed = true;
+ }
+ }
+
+ return changed;
+ }
+
+ /**
+ * Updates ObjEntity attributes and relationships based on the current state
+ * of its DbEntity.
+ *
+ * @return true if the ObjEntity has changed as a result of synchronization.
+ */
+ public boolean synchronizeWithDbEntity(ObjEntity entity) {
+
+ if (entity == null) {
+ return false;
+ }
+
+ DbEntity dbEntity = entity.getDbEntity();
+ if (dbEntity == null) {
+ return false;
+ }
+
+ boolean changed = false;
+
+ if (removingMeaningfulFKs) {
+ changed = getRidOfAttributesThatAreNowSrcAttributesForRelationships(entity);
+ }
+
+ changed |= addMissingAttributes(entity);
+ changed |= addMissingRelationships(entity);
+
+ return changed;
+ }
+
+ /**
+ * @since 4.0
+ */
+ public boolean synchronizeOnDbAttributeAdded(ObjEntity entity, DbAttribute dbAttribute) {
+
+ Collection<DbRelationship> incomingRels = getIncomingRelationships(dbAttribute.getEntity());
+ if (shouldAddToObjEntity(entity, dbAttribute, incomingRels)) {
+ addMissingAttribute(entity, dbAttribute);
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * @since 4.0
+ */
+ public boolean synchronizeOnDbRelationshipAdded(ObjEntity entity, DbRelationship dbRelationship) {
+
+ if (shouldAddToObjEntity(entity, dbRelationship)) {
+ addMissingRelationship(entity, dbRelationship);
+ }
+
+ return true;
+ }
+
+ private boolean addMissingRelationships(ObjEntity entity) {
+ List<DbRelationship> relationshipsToAdd = getRelationshipsToAdd(entity);
+ if (relationshipsToAdd.isEmpty()) {
+ return false;
+ }
+
+ for (DbRelationship dr : relationshipsToAdd) {
+ addMissingRelationship(entity, dr);
+ }
+
+ return true;
+ }
+
+ private boolean createObjRelationship(ObjEntity entity, DbRelationship dr, String targetEntityName) {
+ ObjRelationship or = new ObjRelationship();
+ or.setName(NameBuilder.builder(or, entity)
+ .baseName(nameGenerator.relationshipName(dr))
+ .name());
+
+ or.addDbRelationship(dr);
+ Map<String, ObjEntity> objEntities = entity.getDataMap().getSubclassesForObjEntity(entity);
+
+ boolean hasFlattingAttributes = false;
+ boolean needGeneratedEntity = true;
+
+ if (objEntities.containsKey(targetEntityName)) {
+ needGeneratedEntity = false;
+ }
+
+ for (ObjEntity subObjEntity : objEntities.values()) {
+ for (ObjAttribute objAttribute : subObjEntity.getAttributes()) {
+ String path = objAttribute.getDbAttributePath();
+ if (path != null) {
+ if (path.startsWith(or.getDbRelationshipPath())) {
+ hasFlattingAttributes = true;
+ break;
+ }
+ }
+ }
+ }
+
+ if (!hasFlattingAttributes) {
+ if (needGeneratedEntity) {
+ or.setTargetEntityName(targetEntityName);
+ or.setSourceEntity(entity);
+ }
+
+ entity.addRelationship(or);
+ fireRelationshipAdded(or);
+ }
+
+ return needGeneratedEntity;
+ }
+
+ private boolean addMissingAttributes(ObjEntity entity) {
+ boolean changed = false;
+
+ for (DbAttribute da : getAttributesToAdd(entity)) {
+ addMissingAttribute(entity, da);
+ changed = true;
+ }
+ return changed;
+ }
+
+ private void addMissingRelationship(ObjEntity entity, DbRelationship dbRelationship) {
+
+ // getting DataMap from DbRelationship's source entity. This is the only object in our arguments that
+ // is guaranteed to be a part of the map....
+ DataMap dataMap = dbRelationship.getSourceEntity().getDataMap();
+
+ DbEntity targetEntity = dbRelationship.getTargetEntity();
+ Collection<ObjEntity> mappedObjEntities = dataMap.getMappedEntities(targetEntity);
+ if (mappedObjEntities.isEmpty()) {
+ if (targetEntity == null) {
+ targetEntity = new DbEntity(dbRelationship.getTargetEntityName());
+ }
+
+ if (dbRelationship.getTargetEntityName() != null) {
+ boolean needGeneratedEntity = createObjRelationship(entity, dbRelationship,
+ nameGenerator.objEntityName(targetEntity));
+ if (needGeneratedEntity) {
+ LOGGER.warn("Can't find ObjEntity for " + dbRelationship.getTargetEntityName());
+ LOGGER.warn("Db Relationship (" + dbRelationship + ") will have GUESSED Obj Relationship reflection. ");
+ }
+ }
+ } else {
+ for (Entity mappedTarget : mappedObjEntities) {
+ createObjRelationship(entity, dbRelationship, mappedTarget.getName());
+ }
+ }
+ }
+
+ private void addMissingAttribute(ObjEntity entity, DbAttribute da) {
+ ObjAttribute oa = new ObjAttribute();
+ oa.setName(NameBuilder.builder(oa, entity)
+ .baseName(nameGenerator.objAttributeName(da))
+ .name());
+ oa.setEntity(entity);
+
+ String type = TypesMapping.getJavaBySqlType(da.getType());
+ if (usingPrimitives) {
+ String primitive = CLASS_TO_PRIMITIVE.get(type);
+ if (primitive != null) {
+ type = primitive;
+ }
+ }
+ oa.setType(type);
+ oa.setDbAttributePath(da.getName());
+ entity.addAttribute(oa);
+ fireAttributeAdded(oa);
+ }
+
+ private boolean getRidOfAttributesThatAreNowSrcAttributesForRelationships(ObjEntity entity) {
+ boolean changed = false;
+ for (DbAttribute da : getMeaningfulFKs(entity)) {
+ ObjAttribute oa = entity.getAttributeForDbAttribute(da);
+ while (oa != null) {
+ String attrName = oa.getName();
+ entity.removeAttribute(attrName);
+ changed = true;
+ oa = entity.getAttributeForDbAttribute(da);
+ }
+ }
+ return changed;
+ }
+
+ /**
+ * Returns a list of DbAttributes that are mapped to foreign keys.
+ *
+ * @since 1.2
+ */
+ public Collection<DbAttribute> getMeaningfulFKs(ObjEntity objEntity) {
+ List<DbAttribute> fks = new ArrayList<>(2);
+
+ for (ObjAttribute property : objEntity.getAttributes()) {
+ DbAttribute column = property.getDbAttribute();
+
+ // check if adding it makes sense at all
+ if (column != null && column.isForeignKey()) {
+ fks.add(column);
+ }
+ }
+
+ return fks;
+ }
+
+ /**
+ * Returns a list of attributes that exist in the DbEntity, but are missing
+ * from the ObjEntity.
+ */
+ protected List<DbAttribute> getAttributesToAdd(ObjEntity objEntity) {
+ DbEntity dbEntity = objEntity.getDbEntity();
+
+ List<DbAttribute> missing = new ArrayList<>();
+ Collection<DbRelationship> incomingRels = getIncomingRelationships(dbEntity);
+
+ for (DbAttribute dba : dbEntity.getAttributes()) {
+
+ if (shouldAddToObjEntity(objEntity, dba, incomingRels)) {
+ missing.add(dba);
+ }
+ }
+
+ return missing;
+ }
+
+ protected boolean shouldAddToObjEntity(ObjEntity entity, DbAttribute dbAttribute, Collection<DbRelationship> incomingRels) {
+
+ if (dbAttribute.getName() == null || entity.getAttributeForDbAttribute(dbAttribute) != null) {
+ return false;
+ }
+
+ boolean addMeaningfulPK = meaningfulPKsFilter.isIncluded(entity.getDbEntityName());
+
+ if (dbAttribute.isPrimaryKey() && !addMeaningfulPK) {
+ return false;
+ }
+
+ // check FK's
+ boolean isFK = false;
+ Iterator<DbRelationship> rit = dbAttribute.getEntity().getRelationships().iterator();
+ while (!isFK && rit.hasNext()) {
+ DbRelationship rel = rit.next();
+ for (DbJoin join : rel.getJoins()) {
+ if (join.getSource() == dbAttribute) {
+ isFK = true;
+ break;
+ }
+ }
+ }
+
+ if (addMeaningfulPK) {
+ if (!dbAttribute.isPrimaryKey() && isFK) {
+ return false;
+ }
+ } else {
+ if (isFK) {
+ return false;
+ }
+ }
+
+ // check incoming relationships
+ rit = incomingRels.iterator();
+ while (!isFK && rit.hasNext()) {
+ DbRelationship rel = rit.next();
+ for (DbJoin join : rel.getJoins()) {
+ if (join.getTarget() == dbAttribute) {
+ isFK = true;
+ break;
+ }
+ }
+ }
+
+ if (addMeaningfulPK) {
+ if (!dbAttribute.isPrimaryKey() && isFK) {
+ return false;
+ }
+ } else {
+ if (isFK) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ private boolean shouldAddToObjEntity(ObjEntity entity, DbRelationship dbRelationship) {
+ if(dbRelationship.getName() == null) {
+ return false;
+ }
+
+ for(Relationship relationship : entity.getRelationships()) {
+ ObjRelationship objRelationship = (ObjRelationship)relationship;
+ if(objRelationshipHasDbRelationship(objRelationship, dbRelationship)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * @return true if objRelationship includes given dbRelationship
+ */
+ private boolean objRelationshipHasDbRelationship(ObjRelationship objRelationship, DbRelationship dbRelationship) {
+ for(DbRelationship relationship : objRelationship.getDbRelationships()) {
+
+ if(relationship.getSourceEntityName().equals(dbRelationship.getSourceEntityName())
+ && relationship.getTargetEntityName().equals(dbRelationship.getTargetEntityName())
+ && isSameAttributes(relationship.getSourceAttributes(), dbRelationship.getSourceAttributes())
+ && isSameAttributes(relationship.getTargetAttributes(), dbRelationship.getTargetAttributes())) {
+ return true;
+ }
+
+ }
+ return false;
+ }
+
+
+ /**
+ * @param collection1 first collection to compare
+ * @param collection2 second collection to compare
+ * @return true if collections have same size and attributes in them have same names
+ */
+ private boolean isSameAttributes(Collection<DbAttribute> collection1, Collection<DbAttribute> collection2) {
+ if(collection1.size() != collection2.size()) {
+ return false;
+ }
+
+ if(collection1.isEmpty()) {
+ return true;
+ }
+
+ Iterator<DbAttribute> iterator1 = collection1.iterator();
+ Iterator<DbAttribute> iterator2 = collection2.iterator();
+ for(int i=0; i<collection1.size(); i++) {
+ DbAttribute attr1 = iterator1.next();
+ DbAttribute attr2 = iterator2.next();
+ if(!attr1.getName().equals(attr2.getName())) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ private Collection<DbRelationship> getIncomingRelationships(DbEntity entity) {
+ Collection<DbRelationship> incoming = new ArrayList<DbRelationship>();
+
+ for (DbEntity nextEntity : entity.getDataMap().getDbEntities()) {
+ for (DbRelationship relationship : nextEntity.getRelationships()) {
+
+ // TODO: PERFORMANCE 'getTargetEntity' is generally slow, called
+ // in this iterator it is showing (e.g. in YourKit profiles)..
+ // perhaps use cheaper 'getTargetEntityName()' or even better -
+ // pre-cache all relationships by target entity to avoid O(n)
+ // search ?
+ // (need to profile to prove the difference)
+ if (entity == relationship.getTargetEntity()) {
+ incoming.add(relationship);
+ }
+ }
+ }
+
+ return incoming;
+ }
+
+ protected List<DbRelationship> getRelationshipsToAdd(ObjEntity objEntity) {
+ List<DbRelationship> missing = new ArrayList<DbRelationship>();
+ for (DbRelationship dbRel : objEntity.getDbEntity().getRelationships()) {
+ if (shouldAddToObjEntity(objEntity, dbRel)) {
+ missing.add(dbRel);
+ }
+ }
+
+ return missing;
+ }
+
+ /**
+ * Registers new EntityMergeListener
+ */
+ public void addEntityMergeListener(EntityMergeListener listener) {
+ listeners.add(listener);
+ }
+
+ /**
+ * Unregisters an EntityMergeListener
+ */
+ public void removeEntityMergeListener(EntityMergeListener listener) {
+ listeners.remove(listener);
+ }
+
+ /**
+ * Notifies all listeners that an ObjAttribute was added
+ */
+ protected void fireAttributeAdded(ObjAttribute attr) {
+ for (EntityMergeListener listener : listeners) {
+ listener.objAttributeAdded(attr);
+ }
+ }
+
+ /**
+ * Notifies all listeners that an ObjRelationship was added
+ */
+ protected void fireRelationshipAdded(ObjRelationship rel) {
+ for (EntityMergeListener listener : listeners) {
+ listener.objRelationshipAdded(rel);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/46c8ded5/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/context/MergeDirection.java
----------------------------------------------------------------------
diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/context/MergeDirection.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/context/MergeDirection.java
new file mode 100644
index 0000000..fe2e9ac
--- /dev/null
+++ b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/context/MergeDirection.java
@@ -0,0 +1,71 @@
+/*****************************************************************
+ * 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.context;
+
+/**
+ * Represent a merge direction that can be either from the model to the db or from the db to the model.
+ */
+public enum MergeDirection {
+
+ /**
+ * TO_DB Token means that changes was made in object model and should be reflected at DB
+ */
+ TO_DB("To DB"),
+
+ /**
+ * TO_MODEL Token represent database changes that should be allayed to object model
+ */
+ TO_MODEL("To Model");
+
+ private String name;
+
+ MergeDirection(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public boolean isToDb() {
+ return (this == TO_DB);
+ }
+
+ public boolean isToModel() {
+ return (this == TO_MODEL);
+ }
+
+ @Override
+ public String toString() {
+ return getName();
+ }
+
+ public MergeDirection reverseDirection() {
+ switch (this) {
+ case TO_DB:
+ return TO_MODEL;
+ case TO_MODEL:
+ return TO_DB;
+ default:
+ throw new IllegalStateException("Invalid direction: " + this);
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/46c8ded5/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/context/MergerContext.java
----------------------------------------------------------------------
diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/context/MergerContext.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/context/MergerContext.java
new file mode 100644
index 0000000..b048952
--- /dev/null
+++ b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/context/MergerContext.java
@@ -0,0 +1,174 @@
+/*****************************************************************
+ * 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.context;
+
+import org.apache.cayenne.access.DataNode;
+import org.apache.cayenne.dba.DbAdapter;
+import org.apache.cayenne.dbsync.filter.NameFilter;
+import org.apache.cayenne.dbsync.filter.NamePatternMatcher;
+import org.apache.cayenne.dbsync.merge.token.MergerToken;
+import org.apache.cayenne.dbsync.naming.DefaultObjectNameGenerator;
+import org.apache.cayenne.dbsync.naming.NoStemStemmer;
+import org.apache.cayenne.dbsync.naming.ObjectNameGenerator;
+import org.apache.cayenne.dbsync.reverse.dbload.DefaultModelMergeDelegate;
+import org.apache.cayenne.dbsync.reverse.dbload.ModelMergeDelegate;
+import org.apache.cayenne.map.DataMap;
+import org.apache.cayenne.validation.ValidationResult;
+
+import javax.sql.DataSource;
+import java.util.Objects;
+
+/**
+ * An object passed as an argument to {@link MergerToken#execute(MergerContext)}s that a
+ * {@link MergerToken} can do its work.
+ */
+public class MergerContext {
+
+ private DataMap dataMap;
+ private DataNode dataNode;
+ private ValidationResult validationResult;
+ private ModelMergeDelegate delegate;
+ private EntityMergeSupport entityMergeSupport;
+ private ObjectNameGenerator nameGenerator;
+
+ protected MergerContext() {
+ }
+
+ /**
+ * @since 4.0
+ */
+ public static Builder builder(DataMap dataMap) {
+ return new Builder(dataMap);
+ }
+
+ /**
+ * @since 4.0
+ */
+ public EntityMergeSupport getEntityMergeSupport() {
+ return entityMergeSupport;
+ }
+
+ /**
+ * Returns the DataMap that is the target of a the merge operation.
+ *
+ * @return the DataMap that is the target of a the merge operation.
+ */
+ public DataMap getDataMap() {
+ return dataMap;
+ }
+
+ public DataNode getDataNode() {
+ return dataNode;
+ }
+
+ public ValidationResult getValidationResult() {
+ return validationResult;
+ }
+
+ /**
+ * Returns a callback object that is invoked as the merge proceeds through tokens, modifying the DataMap.
+ *
+ * @return a callback object that is invoked as the merge proceeds through tokens, modifying the DataMap.
+ * @since 4.0
+ */
+ public ModelMergeDelegate getDelegate() {
+ return delegate;
+ }
+
+ /**
+ * @since 4.0
+ */
+ public ObjectNameGenerator getNameGenerator() {
+ return nameGenerator;
+ }
+
+ public static class Builder {
+
+ private MergerContext context;
+ private boolean usingPrimitives;
+ private NameFilter meaningfulPKsFilter;
+
+ private Builder(DataMap dataMap) {
+ this.context = new MergerContext();
+ this.context.dataMap = Objects.requireNonNull(dataMap);
+ this.context.validationResult = new ValidationResult();
+ }
+
+ public MergerContext build() {
+
+ // init missing defaults ...
+
+ if (context.delegate == null) {
+ delegate(new DefaultModelMergeDelegate());
+ }
+
+ if (context.dataNode == null) {
+ dataNode(new DataNode());
+ }
+
+ if(context.nameGenerator == null) {
+ context.nameGenerator = new DefaultObjectNameGenerator(NoStemStemmer.getInstance());
+ }
+
+ if(meaningfulPKsFilter == null) {
+ meaningfulPKsFilter = NamePatternMatcher.EXCLUDE_ALL;
+ }
+
+ context.entityMergeSupport = new EntityMergeSupport(context.nameGenerator,
+ meaningfulPKsFilter,
+ true,
+ usingPrimitives);
+
+ return context;
+ }
+
+ public Builder delegate(ModelMergeDelegate delegate) {
+ context.delegate = Objects.requireNonNull(delegate);
+ return this;
+ }
+
+ public Builder nameGenerator(ObjectNameGenerator nameGenerator) {
+ this.context.nameGenerator = Objects.requireNonNull(nameGenerator);
+ return this;
+ }
+
+ public Builder usingPrimitives(boolean flag) {
+ this.usingPrimitives = flag;
+ return this;
+ }
+
+ public Builder dataNode(DataNode dataNode) {
+ this.context.dataNode = Objects.requireNonNull(dataNode);
+ return this;
+ }
+
+ public Builder meaningfulPKFilter(NameFilter filter) {
+ this.meaningfulPKsFilter = Objects.requireNonNull(filter);
+ return this;
+ }
+
+ public Builder syntheticDataNode(DataSource dataSource, DbAdapter adapter) {
+ DataNode dataNode = new DataNode();
+ dataNode.setDataSource(dataSource);
+ dataNode.setAdapter(adapter);
+ return dataNode(dataNode);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/46c8ded5/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/factory/DB2MergerTokenFactory.java
----------------------------------------------------------------------
diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/factory/DB2MergerTokenFactory.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/factory/DB2MergerTokenFactory.java
index 586a5cd..5100184 100644
--- a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/factory/DB2MergerTokenFactory.java
+++ b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/factory/DB2MergerTokenFactory.java
@@ -19,8 +19,8 @@
package org.apache.cayenne.dbsync.merge.factory;
import org.apache.cayenne.dba.QuotingStrategy;
-import org.apache.cayenne.dbsync.merge.MergerToken;
-import org.apache.cayenne.dbsync.merge.SetColumnTypeToDb;
+import org.apache.cayenne.dbsync.merge.token.MergerToken;
+import org.apache.cayenne.dbsync.merge.token.SetColumnTypeToDb;
import org.apache.cayenne.map.DbAttribute;
import org.apache.cayenne.map.DbEntity;
http://git-wip-us.apache.org/repos/asf/cayenne/blob/46c8ded5/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/factory/DefaultMergerTokenFactory.java
----------------------------------------------------------------------
diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/factory/DefaultMergerTokenFactory.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/factory/DefaultMergerTokenFactory.java
index c2f96ce..80121fe 100644
--- a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/factory/DefaultMergerTokenFactory.java
+++ b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/factory/DefaultMergerTokenFactory.java
@@ -18,29 +18,29 @@
****************************************************************/
package org.apache.cayenne.dbsync.merge.factory;
-import org.apache.cayenne.dbsync.merge.AddColumnToDb;
-import org.apache.cayenne.dbsync.merge.AddColumnToModel;
-import org.apache.cayenne.dbsync.merge.AddRelationshipToDb;
-import org.apache.cayenne.dbsync.merge.AddRelationshipToModel;
-import org.apache.cayenne.dbsync.merge.CreateTableToDb;
-import org.apache.cayenne.dbsync.merge.CreateTableToModel;
-import org.apache.cayenne.dbsync.merge.DropColumnToDb;
-import org.apache.cayenne.dbsync.merge.DropColumnToModel;
-import org.apache.cayenne.dbsync.merge.DropRelationshipToDb;
-import org.apache.cayenne.dbsync.merge.DropRelationshipToModel;
-import org.apache.cayenne.dbsync.merge.DropTableToDb;
-import org.apache.cayenne.dbsync.merge.DropTableToModel;
-import org.apache.cayenne.dbsync.merge.MergerToken;
-import org.apache.cayenne.dbsync.merge.SetAllowNullToDb;
-import org.apache.cayenne.dbsync.merge.SetAllowNullToModel;
-import org.apache.cayenne.dbsync.merge.SetColumnTypeToDb;
-import org.apache.cayenne.dbsync.merge.SetColumnTypeToModel;
-import org.apache.cayenne.dbsync.merge.SetNotNullToDb;
-import org.apache.cayenne.dbsync.merge.SetNotNullToModel;
-import org.apache.cayenne.dbsync.merge.SetPrimaryKeyToDb;
-import org.apache.cayenne.dbsync.merge.SetPrimaryKeyToModel;
-import org.apache.cayenne.dbsync.merge.SetValueForNullToDb;
-import org.apache.cayenne.dbsync.merge.ValueForNullProvider;
+import org.apache.cayenne.dbsync.merge.token.AddColumnToDb;
+import org.apache.cayenne.dbsync.merge.token.AddColumnToModel;
+import org.apache.cayenne.dbsync.merge.token.AddRelationshipToDb;
+import org.apache.cayenne.dbsync.merge.token.AddRelationshipToModel;
+import org.apache.cayenne.dbsync.merge.token.CreateTableToDb;
+import org.apache.cayenne.dbsync.merge.token.CreateTableToModel;
+import org.apache.cayenne.dbsync.merge.token.DropColumnToDb;
+import org.apache.cayenne.dbsync.merge.token.DropColumnToModel;
+import org.apache.cayenne.dbsync.merge.token.DropRelationshipToDb;
+import org.apache.cayenne.dbsync.merge.token.DropRelationshipToModel;
+import org.apache.cayenne.dbsync.merge.token.DropTableToDb;
+import org.apache.cayenne.dbsync.merge.token.DropTableToModel;
+import org.apache.cayenne.dbsync.merge.token.MergerToken;
+import org.apache.cayenne.dbsync.merge.token.SetAllowNullToDb;
+import org.apache.cayenne.dbsync.merge.token.SetAllowNullToModel;
+import org.apache.cayenne.dbsync.merge.token.SetColumnTypeToDb;
+import org.apache.cayenne.dbsync.merge.token.SetColumnTypeToModel;
+import org.apache.cayenne.dbsync.merge.token.SetNotNullToDb;
+import org.apache.cayenne.dbsync.merge.token.SetNotNullToModel;
+import org.apache.cayenne.dbsync.merge.token.SetPrimaryKeyToDb;
+import org.apache.cayenne.dbsync.merge.token.SetPrimaryKeyToModel;
+import org.apache.cayenne.dbsync.merge.token.SetValueForNullToDb;
+import org.apache.cayenne.dbsync.merge.token.ValueForNullProvider;
import org.apache.cayenne.map.DbAttribute;
import org.apache.cayenne.map.DbEntity;
import org.apache.cayenne.map.DbRelationship;
http://git-wip-us.apache.org/repos/asf/cayenne/blob/46c8ded5/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/factory/DerbyMergerTokenFactory.java
----------------------------------------------------------------------
diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/factory/DerbyMergerTokenFactory.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/factory/DerbyMergerTokenFactory.java
index 398d5cc..8979fa0 100644
--- a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/factory/DerbyMergerTokenFactory.java
+++ b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/factory/DerbyMergerTokenFactory.java
@@ -20,10 +20,10 @@ package org.apache.cayenne.dbsync.merge.factory;
import org.apache.cayenne.dba.DbAdapter;
import org.apache.cayenne.dba.QuotingStrategy;
-import org.apache.cayenne.dbsync.merge.MergerToken;
-import org.apache.cayenne.dbsync.merge.SetAllowNullToDb;
-import org.apache.cayenne.dbsync.merge.SetColumnTypeToDb;
-import org.apache.cayenne.dbsync.merge.SetNotNullToDb;
+import org.apache.cayenne.dbsync.merge.token.MergerToken;
+import org.apache.cayenne.dbsync.merge.token.SetAllowNullToDb;
+import org.apache.cayenne.dbsync.merge.token.SetColumnTypeToDb;
+import org.apache.cayenne.dbsync.merge.token.SetNotNullToDb;
import org.apache.cayenne.map.DbAttribute;
import org.apache.cayenne.map.DbEntity;
http://git-wip-us.apache.org/repos/asf/cayenne/blob/46c8ded5/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/factory/FirebirdMergerTokenFactory.java
----------------------------------------------------------------------
diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/factory/FirebirdMergerTokenFactory.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/factory/FirebirdMergerTokenFactory.java
index 4368977..ce94ba0 100644
--- a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/factory/FirebirdMergerTokenFactory.java
+++ b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/factory/FirebirdMergerTokenFactory.java
@@ -21,11 +21,11 @@ package org.apache.cayenne.dbsync.merge.factory;
import org.apache.cayenne.dba.DbAdapter;
import org.apache.cayenne.dba.QuotingStrategy;
-import org.apache.cayenne.dbsync.merge.AddColumnToDb;
-import org.apache.cayenne.dbsync.merge.DropColumnToDb;
-import org.apache.cayenne.dbsync.merge.MergerToken;
-import org.apache.cayenne.dbsync.merge.SetAllowNullToDb;
-import org.apache.cayenne.dbsync.merge.SetNotNullToDb;
+import org.apache.cayenne.dbsync.merge.token.AddColumnToDb;
+import org.apache.cayenne.dbsync.merge.token.DropColumnToDb;
+import org.apache.cayenne.dbsync.merge.token.MergerToken;
+import org.apache.cayenne.dbsync.merge.token.SetAllowNullToDb;
+import org.apache.cayenne.dbsync.merge.token.SetNotNullToDb;
import org.apache.cayenne.map.DbAttribute;
import org.apache.cayenne.map.DbEntity;
http://git-wip-us.apache.org/repos/asf/cayenne/blob/46c8ded5/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/factory/H2MergerTokenFactory.java
----------------------------------------------------------------------
diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/factory/H2MergerTokenFactory.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/factory/H2MergerTokenFactory.java
index 8acafc3..803921c 100644
--- a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/factory/H2MergerTokenFactory.java
+++ b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/factory/H2MergerTokenFactory.java
@@ -21,10 +21,10 @@ package org.apache.cayenne.dbsync.merge.factory;
import org.apache.cayenne.dba.DbAdapter;
import org.apache.cayenne.dba.QuotingStrategy;
-import org.apache.cayenne.dbsync.merge.MergerToken;
-import org.apache.cayenne.dbsync.merge.SetAllowNullToDb;
-import org.apache.cayenne.dbsync.merge.SetColumnTypeToDb;
-import org.apache.cayenne.dbsync.merge.SetPrimaryKeyToDb;
+import org.apache.cayenne.dbsync.merge.token.MergerToken;
+import org.apache.cayenne.dbsync.merge.token.SetAllowNullToDb;
+import org.apache.cayenne.dbsync.merge.token.SetColumnTypeToDb;
+import org.apache.cayenne.dbsync.merge.token.SetPrimaryKeyToDb;
import org.apache.cayenne.map.DbAttribute;
import org.apache.cayenne.map.DbEntity;
http://git-wip-us.apache.org/repos/asf/cayenne/blob/46c8ded5/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/factory/HSQLMergerTokenFactory.java
----------------------------------------------------------------------
diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/factory/HSQLMergerTokenFactory.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/factory/HSQLMergerTokenFactory.java
index 15cfa18..d168118 100644
--- a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/factory/HSQLMergerTokenFactory.java
+++ b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/factory/HSQLMergerTokenFactory.java
@@ -20,10 +20,10 @@ package org.apache.cayenne.dbsync.merge.factory;
import org.apache.cayenne.dba.DbAdapter;
import org.apache.cayenne.dba.QuotingStrategy;
-import org.apache.cayenne.dbsync.merge.MergerToken;
-import org.apache.cayenne.dbsync.merge.SetAllowNullToDb;
-import org.apache.cayenne.dbsync.merge.SetColumnTypeToDb;
-import org.apache.cayenne.dbsync.merge.SetPrimaryKeyToDb;
+import org.apache.cayenne.dbsync.merge.token.MergerToken;
+import org.apache.cayenne.dbsync.merge.token.SetAllowNullToDb;
+import org.apache.cayenne.dbsync.merge.token.SetColumnTypeToDb;
+import org.apache.cayenne.dbsync.merge.token.SetPrimaryKeyToDb;
import org.apache.cayenne.map.DbAttribute;
import org.apache.cayenne.map.DbEntity;
http://git-wip-us.apache.org/repos/asf/cayenne/blob/46c8ded5/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/factory/IngresMergerTokenFactory.java
----------------------------------------------------------------------
diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/factory/IngresMergerTokenFactory.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/factory/IngresMergerTokenFactory.java
index 19d2860..4dc715e 100644
--- a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/factory/IngresMergerTokenFactory.java
+++ b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/factory/IngresMergerTokenFactory.java
@@ -20,13 +20,13 @@ package org.apache.cayenne.dbsync.merge.factory;
import org.apache.cayenne.dba.DbAdapter;
import org.apache.cayenne.dba.QuotingStrategy;
-import org.apache.cayenne.dbsync.merge.AddRelationshipToDb;
-import org.apache.cayenne.dbsync.merge.DropColumnToDb;
-import org.apache.cayenne.dbsync.merge.DropRelationshipToDb;
-import org.apache.cayenne.dbsync.merge.MergerToken;
-import org.apache.cayenne.dbsync.merge.SetAllowNullToDb;
-import org.apache.cayenne.dbsync.merge.SetColumnTypeToDb;
-import org.apache.cayenne.dbsync.merge.SetNotNullToDb;
+import org.apache.cayenne.dbsync.merge.token.AddRelationshipToDb;
+import org.apache.cayenne.dbsync.merge.token.DropColumnToDb;
+import org.apache.cayenne.dbsync.merge.token.DropRelationshipToDb;
+import org.apache.cayenne.dbsync.merge.token.MergerToken;
+import org.apache.cayenne.dbsync.merge.token.SetAllowNullToDb;
+import org.apache.cayenne.dbsync.merge.token.SetColumnTypeToDb;
+import org.apache.cayenne.dbsync.merge.token.SetNotNullToDb;
import org.apache.cayenne.map.DbAttribute;
import org.apache.cayenne.map.DbEntity;
import org.apache.cayenne.map.DbJoin;
http://git-wip-us.apache.org/repos/asf/cayenne/blob/46c8ded5/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/factory/MergerTokenFactory.java
----------------------------------------------------------------------
diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/factory/MergerTokenFactory.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/factory/MergerTokenFactory.java
index 46b6ef3..c61f1ed 100644
--- a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/factory/MergerTokenFactory.java
+++ b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/factory/MergerTokenFactory.java
@@ -18,8 +18,8 @@
****************************************************************/
package org.apache.cayenne.dbsync.merge.factory;
-import org.apache.cayenne.dbsync.merge.MergerToken;
-import org.apache.cayenne.dbsync.merge.ValueForNullProvider;
+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 org.apache.cayenne.map.DbRelationship;
http://git-wip-us.apache.org/repos/asf/cayenne/blob/46c8ded5/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/factory/MySQLMergerTokenFactory.java
----------------------------------------------------------------------
diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/factory/MySQLMergerTokenFactory.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/factory/MySQLMergerTokenFactory.java
index 2193446..1d4ab9f 100644
--- a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/factory/MySQLMergerTokenFactory.java
+++ b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/factory/MySQLMergerTokenFactory.java
@@ -20,12 +20,12 @@ package org.apache.cayenne.dbsync.merge.factory;
import org.apache.cayenne.dba.DbAdapter;
import org.apache.cayenne.dba.QuotingStrategy;
-import org.apache.cayenne.dbsync.merge.DropRelationshipToDb;
-import org.apache.cayenne.dbsync.merge.MergerToken;
-import org.apache.cayenne.dbsync.merge.SetAllowNullToDb;
-import org.apache.cayenne.dbsync.merge.SetColumnTypeToDb;
-import org.apache.cayenne.dbsync.merge.SetNotNullToDb;
-import org.apache.cayenne.dbsync.merge.SetPrimaryKeyToDb;
+import org.apache.cayenne.dbsync.merge.token.DropRelationshipToDb;
+import org.apache.cayenne.dbsync.merge.token.MergerToken;
+import org.apache.cayenne.dbsync.merge.token.SetAllowNullToDb;
+import org.apache.cayenne.dbsync.merge.token.SetColumnTypeToDb;
+import org.apache.cayenne.dbsync.merge.token.SetNotNullToDb;
+import org.apache.cayenne.dbsync.merge.token.SetPrimaryKeyToDb;
import org.apache.cayenne.map.DbAttribute;
import org.apache.cayenne.map.DbEntity;
import org.apache.cayenne.map.DbRelationship;
http://git-wip-us.apache.org/repos/asf/cayenne/blob/46c8ded5/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/factory/OpenBaseMergerTokenFactory.java
----------------------------------------------------------------------
diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/factory/OpenBaseMergerTokenFactory.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/factory/OpenBaseMergerTokenFactory.java
index 7235f6b..19fa0b9 100644
--- a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/factory/OpenBaseMergerTokenFactory.java
+++ b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/factory/OpenBaseMergerTokenFactory.java
@@ -19,12 +19,12 @@
package org.apache.cayenne.dbsync.merge.factory;
import org.apache.cayenne.dba.DbAdapter;
-import org.apache.cayenne.dbsync.merge.CreateTableToDb;
-import org.apache.cayenne.dbsync.merge.DropRelationshipToDb;
-import org.apache.cayenne.dbsync.merge.MergerToken;
-import org.apache.cayenne.dbsync.merge.SetAllowNullToDb;
-import org.apache.cayenne.dbsync.merge.SetColumnTypeToDb;
-import org.apache.cayenne.dbsync.merge.SetNotNullToDb;
+import org.apache.cayenne.dbsync.merge.token.CreateTableToDb;
+import org.apache.cayenne.dbsync.merge.token.DropRelationshipToDb;
+import org.apache.cayenne.dbsync.merge.token.MergerToken;
+import org.apache.cayenne.dbsync.merge.token.SetAllowNullToDb;
+import org.apache.cayenne.dbsync.merge.token.SetColumnTypeToDb;
+import org.apache.cayenne.dbsync.merge.token.SetNotNullToDb;
import org.apache.cayenne.map.DbAttribute;
import org.apache.cayenne.map.DbEntity;
import org.apache.cayenne.map.DbJoin;
http://git-wip-us.apache.org/repos/asf/cayenne/blob/46c8ded5/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/factory/OracleMergerTokenFactory.java
----------------------------------------------------------------------
diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/factory/OracleMergerTokenFactory.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/factory/OracleMergerTokenFactory.java
index 2c4032b..865e5a7 100644
--- a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/factory/OracleMergerTokenFactory.java
+++ b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/factory/OracleMergerTokenFactory.java
@@ -20,11 +20,11 @@ package org.apache.cayenne.dbsync.merge.factory;
import org.apache.cayenne.dba.DbAdapter;
import org.apache.cayenne.dba.QuotingStrategy;
-import org.apache.cayenne.dbsync.merge.AddColumnToDb;
-import org.apache.cayenne.dbsync.merge.MergerToken;
-import org.apache.cayenne.dbsync.merge.SetAllowNullToDb;
-import org.apache.cayenne.dbsync.merge.SetColumnTypeToDb;
-import org.apache.cayenne.dbsync.merge.SetNotNullToDb;
+import org.apache.cayenne.dbsync.merge.token.AddColumnToDb;
+import org.apache.cayenne.dbsync.merge.token.MergerToken;
+import org.apache.cayenne.dbsync.merge.token.SetAllowNullToDb;
+import org.apache.cayenne.dbsync.merge.token.SetColumnTypeToDb;
+import org.apache.cayenne.dbsync.merge.token.SetNotNullToDb;
import org.apache.cayenne.map.DbAttribute;
import org.apache.cayenne.map.DbEntity;
http://git-wip-us.apache.org/repos/asf/cayenne/blob/46c8ded5/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/factory/PostgresMergerTokenFactory.java
----------------------------------------------------------------------
diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/factory/PostgresMergerTokenFactory.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/factory/PostgresMergerTokenFactory.java
index 935ecfb..a14b99d 100644
--- a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/factory/PostgresMergerTokenFactory.java
+++ b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/factory/PostgresMergerTokenFactory.java
@@ -21,8 +21,8 @@ package org.apache.cayenne.dbsync.merge.factory;
import org.apache.cayenne.dba.QuotingStrategy;
import org.apache.cayenne.map.DbAttribute;
import org.apache.cayenne.map.DbEntity;
-import org.apache.cayenne.dbsync.merge.MergerToken;
-import org.apache.cayenne.dbsync.merge.SetColumnTypeToDb;
+import org.apache.cayenne.dbsync.merge.token.MergerToken;
+import org.apache.cayenne.dbsync.merge.token.SetColumnTypeToDb;
public class PostgresMergerTokenFactory extends DefaultMergerTokenFactory {
http://git-wip-us.apache.org/repos/asf/cayenne/blob/46c8ded5/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/factory/SQLServerMergerTokenFactory.java
----------------------------------------------------------------------
diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/factory/SQLServerMergerTokenFactory.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/factory/SQLServerMergerTokenFactory.java
index 768b957..00673ad 100644
--- a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/factory/SQLServerMergerTokenFactory.java
+++ b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/factory/SQLServerMergerTokenFactory.java
@@ -20,11 +20,11 @@ package org.apache.cayenne.dbsync.merge.factory;
import org.apache.cayenne.dba.DbAdapter;
import org.apache.cayenne.dba.QuotingStrategy;
-import org.apache.cayenne.dbsync.merge.AddColumnToDb;
-import org.apache.cayenne.dbsync.merge.MergerToken;
-import org.apache.cayenne.dbsync.merge.SetAllowNullToDb;
-import org.apache.cayenne.dbsync.merge.SetColumnTypeToDb;
-import org.apache.cayenne.dbsync.merge.SetNotNullToDb;
+import org.apache.cayenne.dbsync.merge.token.AddColumnToDb;
+import org.apache.cayenne.dbsync.merge.token.MergerToken;
+import org.apache.cayenne.dbsync.merge.token.SetAllowNullToDb;
+import org.apache.cayenne.dbsync.merge.token.SetColumnTypeToDb;
+import org.apache.cayenne.dbsync.merge.token.SetNotNullToDb;
import org.apache.cayenne.map.DbAttribute;
import org.apache.cayenne.map.DbEntity;
http://git-wip-us.apache.org/repos/asf/cayenne/blob/46c8ded5/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/factory/SybaseMergerTokenFactory.java
----------------------------------------------------------------------
diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/factory/SybaseMergerTokenFactory.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/factory/SybaseMergerTokenFactory.java
index f295305..48b5a22 100644
--- a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/factory/SybaseMergerTokenFactory.java
+++ b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/factory/SybaseMergerTokenFactory.java
@@ -20,12 +20,12 @@ package org.apache.cayenne.dbsync.merge.factory;
import org.apache.cayenne.dba.DbAdapter;
import org.apache.cayenne.dba.QuotingStrategy;
-import org.apache.cayenne.dbsync.merge.AddColumnToDb;
-import org.apache.cayenne.dbsync.merge.DropColumnToDb;
-import org.apache.cayenne.dbsync.merge.MergerToken;
-import org.apache.cayenne.dbsync.merge.SetAllowNullToDb;
-import org.apache.cayenne.dbsync.merge.SetColumnTypeToDb;
-import org.apache.cayenne.dbsync.merge.SetNotNullToDb;
+import org.apache.cayenne.dbsync.merge.token.AddColumnToDb;
+import org.apache.cayenne.dbsync.merge.token.DropColumnToDb;
+import org.apache.cayenne.dbsync.merge.token.MergerToken;
+import org.apache.cayenne.dbsync.merge.token.SetAllowNullToDb;
+import org.apache.cayenne.dbsync.merge.token.SetColumnTypeToDb;
+import org.apache.cayenne.dbsync.merge.token.SetNotNullToDb;
import org.apache.cayenne.map.DbAttribute;
import org.apache.cayenne.map.DbEntity;
http://git-wip-us.apache.org/repos/asf/cayenne/blob/46c8ded5/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/token/AbstractToDbToken.java
----------------------------------------------------------------------
diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/token/AbstractToDbToken.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/token/AbstractToDbToken.java
new file mode 100644
index 0000000..b054ddb
--- /dev/null
+++ b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/token/AbstractToDbToken.java
@@ -0,0 +1,133 @@
+/*****************************************************************
+ * 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.context.MergeDirection;
+import org.apache.cayenne.dbsync.merge.context.MergerContext;
+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/46c8ded5/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/token/AbstractToModelToken.java
----------------------------------------------------------------------
diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/token/AbstractToModelToken.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/token/AbstractToModelToken.java
new file mode 100644
index 0000000..f61067e
--- /dev/null
+++ b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/token/AbstractToModelToken.java
@@ -0,0 +1,128 @@
+/*****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ ****************************************************************/
+
+package org.apache.cayenne.dbsync.merge.token;
+
+import org.apache.cayenne.dbsync.merge.context.MergeDirection;
+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/46c8ded5/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/token/AddColumnToDb.java
----------------------------------------------------------------------
diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/token/AddColumnToDb.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/token/AddColumnToDb.java
new file mode 100644
index 0000000..19ac1ce
--- /dev/null
+++ b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/token/AddColumnToDb.java
@@ -0,0 +1,74 @@
+/*****************************************************************
+ * 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.dbsync.merge.factory.MergerTokenFactory;
+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/46c8ded5/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/token/AddColumnToModel.java
----------------------------------------------------------------------
diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/token/AddColumnToModel.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/token/AddColumnToModel.java
new file mode 100644
index 0000000..e68d271
--- /dev/null
+++ b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/token/AddColumnToModel.java
@@ -0,0 +1,55 @@
+/*****************************************************************
+ * 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.EntityMergeSupport;
+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.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/46c8ded5/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/token/AddRelationshipToDb.java
----------------------------------------------------------------------
diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/token/AddRelationshipToDb.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/token/AddRelationshipToDb.java
new file mode 100644
index 0000000..2d19422
--- /dev/null
+++ b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/token/AddRelationshipToDb.java
@@ -0,0 +1,88 @@
+/*****************************************************************
+ * 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.access.DbGenerator;
+import org.apache.cayenne.dba.DbAdapter;
+import org.apache.cayenne.dbsync.merge.factory.MergerTokenFactory;
+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() {
+ // Method DbRelationship.isSourceIndependentFromTargetChange() looks same
+ return relationship.isSourceIndependentFromTargetChange();
+ /*return relationship.isToMany()
+ || relationship.isToDependentPK()
+ || !relationship.isToPK(); // TODO it is not necessary primary key it can be unique index
+ */
+ }
+
+ @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/46c8ded5/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/token/AddRelationshipToModel.java
----------------------------------------------------------------------
diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/token/AddRelationshipToModel.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/token/AddRelationshipToModel.java
new file mode 100644
index 0000000..c132bbc
--- /dev/null
+++ b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/token/AddRelationshipToModel.java
@@ -0,0 +1,91 @@
+/*****************************************************************
+ * 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.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;
+ }
+}