cayenne-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ntimof...@apache.org
Subject cayenne git commit: CAY-2282 Various Update Issues With Vertical Inheritance - test cases and patch for part 2 (Optimistic Lock) from Matt Watson - minor code cleanup
Date Thu, 04 May 2017 11:45:06 GMT
Repository: cayenne
Updated Branches:
  refs/heads/master c04206edf -> 2e0acfef6


CAY-2282 Various Update Issues With Vertical Inheritance
 - test cases and patch for part 2 (Optimistic Lock) from Matt Watson
 - minor code cleanup


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

Branch: refs/heads/master
Commit: 2e0acfef6c829ac722d6f7ea0d49f1288645eab5
Parents: c04206e
Author: Nikita Timofeev <stariy95@gmail.com>
Authored: Thu May 4 14:44:59 2017 +0300
Committer: Nikita Timofeev <stariy95@gmail.com>
Committed: Thu May 4 14:44:59 2017 +0300

----------------------------------------------------------------------
 .../cayenne/access/DataDomainDeleteBucket.java  |   2 +-
 .../access/DataDomainFlattenedBucket.java       |   4 +-
 .../cayenne/access/DataDomainFlushAction.java   |  23 ++--
 .../access/DataDomainIndirectDiffBuilder.java   |  22 ++--
 .../cayenne/access/DataDomainInsertBucket.java  |   7 +-
 .../cayenne/access/DataDomainSyncBucket.java    |  45 +++-----
 .../cayenne/access/DataDomainUpdateBucket.java  |  39 +++----
 .../access/DataNodeSyncQualifierDescriptor.java |  13 +--
 .../apache/cayenne/access/FlattenedArcKey.java  |   5 +-
 .../cayenne/access/VerticalInheritanceIT.java   | 106 +++++++++++++++++++
 .../inheritance_vertical/IvBaseWithLock.java    |  28 +++++
 .../inheritance_vertical/IvImplWithLock.java    |  28 +++++
 .../auto/_IvBaseWithLock.java                   |  54 ++++++++++
 .../auto/_IvImplWithLock.java                   |  57 ++++++++++
 .../inheritance_vertical/auto/_IvOther.java     |  29 +++++
 .../test/resources/inheritance-vertical.map.xml |  36 +++++++
 16 files changed, 393 insertions(+), 105 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cayenne/blob/2e0acfef/cayenne-server/src/main/java/org/apache/cayenne/access/DataDomainDeleteBucket.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/DataDomainDeleteBucket.java b/cayenne-server/src/main/java/org/apache/cayenne/access/DataDomainDeleteBucket.java
index b025ef3..7d1e772 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/access/DataDomainDeleteBucket.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/access/DataDomainDeleteBucket.java
@@ -69,7 +69,7 @@ class DataDomainDeleteBucket extends DataDomainSyncBucket {
         for (DbEntity dbEntity : dbEntities) {
             Collection<DbEntityClassDescriptor> descriptors = descriptorsByDbEntity
                     .get(dbEntity);
-            Map<Object, Query> batches = new LinkedHashMap<Object, Query>();
+            Map<Object, Query> batches = new LinkedHashMap<>();
 
             for (DbEntityClassDescriptor descriptor : descriptors) {
 

http://git-wip-us.apache.org/repos/asf/cayenne/blob/2e0acfef/cayenne-server/src/main/java/org/apache/cayenne/access/DataDomainFlattenedBucket.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/DataDomainFlattenedBucket.java b/cayenne-server/src/main/java/org/apache/cayenne/access/DataDomainFlattenedBucket.java
index 900174e..ba2a844 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/access/DataDomainFlattenedBucket.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/access/DataDomainFlattenedBucket.java
@@ -31,7 +31,6 @@ import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
-import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 
@@ -71,11 +70,10 @@ class DataDomainFlattenedBucket {
 
         DeleteBatchQuery relationDeleteQuery = flattenedDeleteQueries.get(flattenedEntity);
         if (relationDeleteQuery == null) {
-            boolean optimisticLocking = false;
             Collection<DbAttribute> pk = flattenedEntity.getPrimaryKeys();
             List<DbAttribute> pkList = pk instanceof List ? (List<DbAttribute>) pk : new ArrayList<>(pk);
             relationDeleteQuery = new DeleteBatchQuery(flattenedEntity, pkList, Collections.<String> emptySet(), 50);
-            relationDeleteQuery.setUsingOptimisticLocking(optimisticLocking);
+            relationDeleteQuery.setUsingOptimisticLocking(false);
             flattenedDeleteQueries.put(flattenedEntity, relationDeleteQuery);
         }
 

http://git-wip-us.apache.org/repos/asf/cayenne/blob/2e0acfef/cayenne-server/src/main/java/org/apache/cayenne/access/DataDomainFlushAction.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/DataDomainFlushAction.java b/cayenne-server/src/main/java/org/apache/cayenne/access/DataDomainFlushAction.java
index 8225390..2acb561 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/access/DataDomainFlushAction.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/access/DataDomainFlushAction.java
@@ -38,7 +38,6 @@ import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
-import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 
@@ -126,8 +125,7 @@ class DataDomainFlushAction {
 
         // TODO: Andrus, 3/13/2006 - support categorizing an arbitrary diff
         if (!(changes instanceof ObjectStoreGraphDiff)) {
-            throw new IllegalArgumentException("Expected 'ObjectStoreGraphDiff', got: "
-                    + changes.getClass().getName());
+            throw new IllegalArgumentException("Expected 'ObjectStoreGraphDiff', got: " + changes.getClass().getName());
         }
 
         this.context = context;
@@ -140,7 +138,7 @@ class DataDomainFlushAction {
         this.flattenedBucket = new DataDomainFlattenedBucket(this);
 
         this.queries = new ArrayList<>();
-        this.resultIndirectlyModifiedIds = new HashSet<ObjectId>();
+        this.resultIndirectlyModifiedIds = new HashSet<>();
 
         preprocess(context, changes);
 
@@ -164,12 +162,10 @@ class DataDomainFlushAction {
 
         ObjectStore objectStore = context.getObjectStore();
 
-        Iterator<?> it = changesByObjectId.keySet().iterator();
-        while (it.hasNext()) {
-            ObjectId id = (ObjectId) it.next();
+        for (Object o : changesByObjectId.keySet()) {
+            ObjectId id = (ObjectId) o;
             Persistent object = (Persistent) objectStore.getNode(id);
-            ClassDescriptor descriptor = context.getEntityResolver().getClassDescriptor(
-                    id.getEntityName());
+            ClassDescriptor descriptor = context.getEntityResolver().getClassDescriptor(id.getEntityName());
 
             switch (object.getPersistenceState()) {
                 case PersistenceState.NEW:
@@ -224,9 +220,7 @@ class DataDomainFlushAction {
                     if (node != lastNode) {
 
                         if (i - rangeStart > 0) {
-                            lastNode.performQueries(
-                                    queries.subList(rangeStart, i),
-                                    observer);
+                            lastNode.performQueries(queries.subList(rangeStart, i), observer);
                         }
 
                         rangeStart = i;
@@ -237,8 +231,7 @@ class DataDomainFlushAction {
 
             // process last segment of the query list...
             lastNode.performQueries(queries.subList(rangeStart, len), observer);
-        }
-        catch (Throwable th) {
+        } catch (Throwable th) {
             BaseTransaction.getThreadTransaction().setRollbackOnly();
             throw new CayenneRuntimeException("Transaction was rolledback.", th);
         }
@@ -266,7 +259,7 @@ class DataDomainFlushAction {
                             context.getObjectStore(),
                             resultModifiedSnapshots,
                             resultDeletedIds,
-                            Collections.EMPTY_LIST,
+                            Collections.<ObjectId>emptyList(),
                             resultIndirectlyModifiedIds);
         }
 

http://git-wip-us.apache.org/repos/asf/cayenne/blob/2e0acfef/cayenne-server/src/main/java/org/apache/cayenne/access/DataDomainIndirectDiffBuilder.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/DataDomainIndirectDiffBuilder.java b/cayenne-server/src/main/java/org/apache/cayenne/access/DataDomainIndirectDiffBuilder.java
index c703ef9..e7ec2d8 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/access/DataDomainIndirectDiffBuilder.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/access/DataDomainIndirectDiffBuilder.java
@@ -49,13 +49,12 @@ final class DataDomainIndirectDiffBuilder implements GraphChangeHandler {
         this.parent = parent;
         this.indirectModifications = parent.getResultIndirectlyModifiedIds();
         this.resolver = parent.getDomain().getEntityResolver();
-        this.flattenedInserts = new HashSet<FlattenedArcKey>();
-        this.flattenedDeletes = new HashSet<FlattenedArcKey>();
+        this.flattenedInserts = new HashSet<>();
+        this.flattenedDeletes = new HashSet<>();
     }
 
     void processIndirectChanges(GraphDiff allChanges) {
-        // extract flattened and indirect changes and remove duplicate
-        // changes...
+        // extract flattened and indirect changes and remove duplicate changes...
         allChanges.apply(this);
 
         if (!flattenedInserts.isEmpty()) {
@@ -91,13 +90,10 @@ final class DataDomainIndirectDiffBuilder implements GraphChangeHandler {
                             , relationship.getName(), relationship.getSourceEntity().getName());
                 }
 
-                // Register this combination (so we can remove it later if an
-                // insert
-                // occurs before commit)
+                // Register this combination (so we can remove it later if an insert occurs before commit)
                 FlattenedArcKey key = new FlattenedArcKey((ObjectId) nodeId, (ObjectId) targetNodeId, relationship);
 
-                // If this combination has already been deleted, simply undelete
-                // it.
+                // If this combination has already been deleted, simply undelete it.
                 if (!flattenedDeletes.remove(key)) {
                     flattenedInserts.add(key);
                 }
@@ -124,14 +120,10 @@ final class DataDomainIndirectDiffBuilder implements GraphChangeHandler {
                             , relationship.getName());
                 }
 
-                // Register this combination (so we can remove it later if an
-                // insert
-                // occurs before commit)
+                // Register this combination (so we can remove it later if an insert occurs before commit)
                 FlattenedArcKey key = new FlattenedArcKey((ObjectId) nodeId, (ObjectId) targetNodeId, relationship);
 
-                // If this combination has already been inserted, simply
-                // "uninsert" it
-                // also do not delete it twice
+                // If this combination has already been inserted, simply "uninsert" it also do not delete it twice
                 if (!flattenedInserts.remove(key)) {
                     flattenedDeletes.add(key);
                 }

http://git-wip-us.apache.org/repos/asf/cayenne/blob/2e0acfef/cayenne-server/src/main/java/org/apache/cayenne/access/DataDomainInsertBucket.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/DataDomainInsertBucket.java b/cayenne-server/src/main/java/org/apache/cayenne/access/DataDomainInsertBucket.java
index 6f37109..9df6f05 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/access/DataDomainInsertBucket.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/access/DataDomainInsertBucket.java
@@ -133,10 +133,8 @@ class DataDomainInsertBucket extends DataDomainSyncBucket {
                         Class<?> javaClass = objAttr.getJavaClass();
                         if (javaClass.isPrimitive() && value instanceof Number && ((Number) value).intValue() == 0) {
                             // primitive 0 has to be treated as NULL, or
-                            // otherwise we
-                            // can't generate PK for POJO's
+                            // otherwise we can't generate PK for POJO's
                         } else {
-
                             idMap.put(dbAttrName, value);
                             continue;
                         }
@@ -154,8 +152,7 @@ class DataDomainInsertBucket extends DataDomainSyncBucket {
                 }
 
                 // only a single key can be generated from DB... if this is done
-                // already
-                // in this loop, we must bail out.
+                // already in this loop, we must bail out.
                 if (autoPkDone) {
                     throw new CayenneRuntimeException("Primary Key autogeneration only works for a single attribute.");
                 }

http://git-wip-us.apache.org/repos/asf/cayenne/blob/2e0acfef/cayenne-server/src/main/java/org/apache/cayenne/access/DataDomainSyncBucket.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/DataDomainSyncBucket.java b/cayenne-server/src/main/java/org/apache/cayenne/access/DataDomainSyncBucket.java
index 780e492..3bb8141 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/access/DataDomainSyncBucket.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/access/DataDomainSyncBucket.java
@@ -86,16 +86,8 @@ abstract class DataDomainSyncBucket {
         }
 
         if (entity.isReadOnly()) {
-
-            StringBuilder message = new StringBuilder();
-            message
-                    .append("Attempt to modify object(s) mapped to a read-only entity: ")
-                    .append(entity.getName());
-
-            message.append(" '").append(entity.getName()).append("'");
-
-            message.append(". Can't commit changes.");
-            throw new CayenneRuntimeException(message.toString());
+            throw new CayenneRuntimeException("Attempt to modify object(s) mapped to a read-only entity: '%s'. " +
+                    "Can't commit changes.", entity.getName());
         }
     }
 
@@ -108,12 +100,10 @@ abstract class DataDomainSyncBucket {
 
             // root DbEntity
             {
-                DbEntityClassDescriptor dbEntityDescriptor = new DbEntityClassDescriptor(
-                        descriptor);
+                DbEntityClassDescriptor dbEntityDescriptor = new DbEntityClassDescriptor(descriptor);
                 DbEntity dbEntity = dbEntityDescriptor.getDbEntity();
 
-                Collection<DbEntityClassDescriptor> descriptors = descriptorsByDbEntity
-                        .get(dbEntity);
+                Collection<DbEntityClassDescriptor> descriptors = descriptorsByDbEntity.get(dbEntity);
                 if (descriptors == null) {
                     descriptors = new ArrayList<>(1);
                     dbEntities.add(dbEntity);
@@ -127,8 +117,7 @@ abstract class DataDomainSyncBucket {
 
             // secondary DbEntities...
 
-            // Note that this logic won't allow flattened attributes to span multiple
-            // databases...
+            // Note that this logic won't allow flattened attributes to span multiple databases...
             for (ObjAttribute objAttribute : descriptor.getEntity().getAttributes()) {
 
                 if (objAttribute.isFlattened()) {
@@ -137,8 +126,7 @@ abstract class DataDomainSyncBucket {
                             objAttribute);
 
                     DbEntity dbEntity = dbEntityDescriptor.getDbEntity();
-                    Collection<DbEntityClassDescriptor> descriptors = descriptorsByDbEntity
-                            .get(dbEntity);
+                    Collection<DbEntityClassDescriptor> descriptors = descriptorsByDbEntity.get(dbEntity);
 
                     if (descriptors == null) {
                         descriptors = new ArrayList<>(1);
@@ -223,17 +211,13 @@ abstract class DataDomainSyncBucket {
                         }
 
                         finalId = replacementId;
-                    }
-                    else if (id.isTemporary()) {
-                        throw new CayenneRuntimeException(
-                                "Temporary ID hasn't been replaced on commit: %s", object);
-                    }
-                    else {
+                    } else if (id.isTemporary()) {
+                        throw new CayenneRuntimeException("Temporary ID hasn't been replaced on commit: %s", object);
+                    } else {
                         finalId = id;
                     }
 
-                    // do not take the snapshot until generated columns are processed (see
-                    // code above)
+                    // do not take the snapshot until generated columns are processed (see code above)
                     DataRow dataRow = parent.getContext().currentSnapshot(object);
 
                     if (object instanceof DataObject) {
@@ -246,11 +230,9 @@ abstract class DataDomainSyncBucket {
 
                     // update Map reverse relationships
                     for (ArcProperty arc : descriptor.getMapArcProperties()) {
-                        ToManyMapProperty reverseArc = (ToManyMapProperty) arc
-                                .getComplimentaryReverseArc();
+                        ToManyMapProperty reverseArc = (ToManyMapProperty) arc.getComplimentaryReverseArc();
 
-                        // must resolve faults... hopefully for to-one this will not cause
-                        // extra fetches...
+                        // must resolve faults... hopefully for to-one this will not cause extra fetches...
                         Object source = arc.readProperty(object);
                         if (source != null && !reverseArc.isFault(source)) {
                             remapTarget(reverseArc, source, object);
@@ -278,8 +260,7 @@ abstract class DataDomainSyncBucket {
         // key), as we have no control of the order in which this method is called, so
         // another object may be remapped later by the caller
 
-        // must do a slow map scan to ensure the object is not mapped under a different
-        // key...
+        // must do a slow map scan to ensure the object is not mapped under a different key...
         Iterator<Map.Entry<Object, Object>> it = map.entrySet().iterator();
         while (it.hasNext()) {
             Map.Entry<Object, Object> e = it.next();

http://git-wip-us.apache.org/repos/asf/cayenne/blob/2e0acfef/cayenne-server/src/main/java/org/apache/cayenne/access/DataDomainUpdateBucket.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/DataDomainUpdateBucket.java b/cayenne-server/src/main/java/org/apache/cayenne/access/DataDomainUpdateBucket.java
index d128f77..59e2f1f 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/access/DataDomainUpdateBucket.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/access/DataDomainUpdateBucket.java
@@ -23,13 +23,14 @@ import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.HashSet;
-import java.util.Iterator;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
+import org.apache.cayenne.ObjectId;
 import org.apache.cayenne.Persistent;
+import org.apache.cayenne.map.Attribute;
 import org.apache.cayenne.map.DbAttribute;
 import org.apache.cayenne.map.DbEntity;
 import org.apache.cayenne.map.ObjEntity;
@@ -53,9 +54,8 @@ class DataDomainUpdateBucket extends DataDomainSyncBucket {
 
         for (DbEntity dbEntity : dbEntities) {
 
-            Collection<DbEntityClassDescriptor> descriptors = descriptorsByDbEntity
-                    .get(dbEntity);
-            Map<Object, Query> batches = new LinkedHashMap<Object, Query>();
+            Collection<DbEntityClassDescriptor> descriptors = descriptorsByDbEntity.get(dbEntity);
+            Map<Object, Query> batches = new LinkedHashMap<>();
 
             for (DbEntityClassDescriptor descriptor : descriptors) {
                 ObjEntity entity = descriptor.getEntity();
@@ -64,10 +64,7 @@ class DataDomainUpdateBucket extends DataDomainSyncBucket {
                 qualifierBuilder.reset(descriptor);
                 boolean isRootDbEntity = entity.getDbEntity() == dbEntity;
 
-                Iterator<Persistent> objects = objectsByDescriptor.get(
-                        descriptor.getClassDescriptor()).iterator();
-                while (objects.hasNext()) {
-                    Persistent o = objects.next();
+                for (Persistent o : objectsByDescriptor.get(descriptor.getClassDescriptor())) {
                     ObjectDiff diff = parent.objectDiff(o.getObjectId());
 
                     Map<String, Object> snapshot = diffBuilder.buildDBDiff(diff);
@@ -81,21 +78,18 @@ class DataDomainUpdateBucket extends DataDomainSyncBucket {
                     // attempt is made to modify a read only entity
                     checkReadOnly(entity);
 
-                    Map<String, Object> qualifierSnapshot = qualifierBuilder
-                            .createQualifierSnapshot(diff);
+                    Map<String, Object> qualifierSnapshot = qualifierBuilder.createQualifierSnapshot(diff);
 
                     // organize batches by the updated columns + nulls in qualifier
-                    Set snapshotSet = snapshot.keySet();
-                    Set nullQualifierNames = new HashSet();
-                    Iterator it = qualifierSnapshot.entrySet().iterator();
-                    while (it.hasNext()) {
-                        Map.Entry entry = (Map.Entry) it.next();
+                    Set<String> snapshotSet = snapshot.keySet();
+                    Set<String> nullQualifierNames = new HashSet<>();
+                    for (Map.Entry<String, Object> entry : qualifierSnapshot.entrySet()) {
                         if (entry.getValue() == null) {
                             nullQualifierNames.add(entry.getKey());
                         }
                     }
 
-                    List batchKey = Arrays.asList(snapshotSet, nullQualifierNames);
+                    List<Set<String>> batchKey = Arrays.asList(snapshotSet, nullQualifierNames);
 
                     UpdateBatchQuery batch = (UpdateBatchQuery) batches.get(batchKey);
                     if (batch == null) {
@@ -106,8 +100,7 @@ class DataDomainUpdateBucket extends DataDomainSyncBucket {
                                 nullQualifierNames,
                                 10);
 
-                        batch.setUsingOptimisticLocking(qualifierBuilder
-                                .isUsingOptimisticLocking());
+                        batch.setUsingOptimisticLocking(qualifierBuilder.isUsingOptimisticLocking());
                         batches.put(batchKey, batch);
                     }
 
@@ -137,12 +130,12 @@ class DataDomainUpdateBucket extends DataDomainSyncBucket {
     /**
      * Creates a list of DbAttributes that are updated in a snapshot
      */
-    private List updatedAttributes(DbEntity entity, Map updatedSnapshot) {
-        List<Object> attributes = new ArrayList<>(updatedSnapshot.size());
-        Map entityAttributes = entity.getAttributeMap();
+    private List<DbAttribute> updatedAttributes(DbEntity entity, Map<String, Object> updatedSnapshot) {
+        List<DbAttribute> attributes = new ArrayList<>(updatedSnapshot.size());
+        Map<String, ? extends Attribute> entityAttributes = entity.getAttributeMap();
 
-        for (Object name : updatedSnapshot.keySet()) {
-            attributes.add(entityAttributes.get(name));
+        for (String name : updatedSnapshot.keySet()) {
+            attributes.add((DbAttribute)entityAttributes.get(name));
         }
 
         return attributes;

http://git-wip-us.apache.org/repos/asf/cayenne/blob/2e0acfef/cayenne-server/src/main/java/org/apache/cayenne/access/DataNodeSyncQualifierDescriptor.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/DataNodeSyncQualifierDescriptor.java b/cayenne-server/src/main/java/org/apache/cayenne/access/DataNodeSyncQualifierDescriptor.java
index 2f10d01..6e48a59 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/access/DataNodeSyncQualifierDescriptor.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/access/DataNodeSyncQualifierDescriptor.java
@@ -89,8 +89,7 @@ class DataNodeSyncQualifierDescriptor {
 			}
 		} else {
 
-			// TODO: andrus 12/23/2007 - only one step relationship is
-			// supported...
+			// TODO: andrus 12/23/2007 - only one step relationship is supported...
 			if (descriptor.getPathFromMaster().size() != 1) {
 				throw new CayenneRuntimeException(
 				        "Only single step dependent relationships are currently supported. Actual path length: %d"
@@ -125,7 +124,8 @@ class DataNodeSyncQualifierDescriptor {
 					// only care about first step in a flattened attribute
 					DbAttribute dbAttribute = (DbAttribute) attribute.getDbPathIterator().next();
 
-					if (!attributes.contains(dbAttribute)) {
+					// only use qualifier if dbEntities match
+					if (dbAttribute.getEntity().equals(descriptor.getDbEntity()) && !attributes.contains(dbAttribute)) {
 						attributes.add(dbAttribute);
 
 						valueTransformers.add(new Transformer() {
@@ -147,11 +147,8 @@ class DataNodeSyncQualifierDescriptor {
 					for (final DbJoin dbAttrPair : dbRelationship.getJoins()) {
 						DbAttribute dbAttribute = dbAttrPair.getSource();
 
-						// relationship transformers override attribute
-						// transformers for
-						// meaningful FK's... why meaningful FKs can go out of
-						// sync is
-						// another story (CAY-595)
+						// relationship transformers override attribute transformers for meaningful FK's...
+						// why meaningful FKs can go out of sync is another story (CAY-595)
 						int index = attributes.indexOf(dbAttribute);
 						if (index >= 0 && !dbAttribute.isForeignKey()) {
 							continue;

http://git-wip-us.apache.org/repos/asf/cayenne/blob/2e0acfef/cayenne-server/src/main/java/org/apache/cayenne/access/FlattenedArcKey.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/FlattenedArcKey.java b/cayenne-server/src/main/java/org/apache/cayenne/access/FlattenedArcKey.java
index ab5698d..2b2217d 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/access/FlattenedArcKey.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/access/FlattenedArcKey.java
@@ -105,8 +105,7 @@ final class FlattenedArcKey {
 			DbAdapter adapter = node.getAdapter();
 
 			// skip db-generated... looks like we don't care about the actual PK
-			// value
-			// here, so no need to retrieve db-generated pk back to Java.
+			// value here, so no need to retrieve db-generated pk back to Java.
 			if (adapter.supportsGeneratedKeys() && dbAttr.isGenerated()) {
 				continue;
 			}
@@ -200,7 +199,7 @@ final class FlattenedArcKey {
 		node.performQueries(Collections.singleton((Query) query), new DefaultOperationObserver() {
 
 			@Override
-			public void nextRows(Query query, List dataRows) {
+			public void nextRows(Query query, List<?> dataRows) {
 
 				if (!dataRows.isEmpty()) {
 					// decode results...

http://git-wip-us.apache.org/repos/asf/cayenne/blob/2e0acfef/cayenne-server/src/test/java/org/apache/cayenne/access/VerticalInheritanceIT.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/access/VerticalInheritanceIT.java b/cayenne-server/src/test/java/org/apache/cayenne/access/VerticalInheritanceIT.java
index 147e468..7ffb996 100644
--- a/cayenne-server/src/test/java/org/apache/cayenne/access/VerticalInheritanceIT.java
+++ b/cayenne-server/src/test/java/org/apache/cayenne/access/VerticalInheritanceIT.java
@@ -21,6 +21,7 @@ package org.apache.cayenne.access;
 import org.apache.cayenne.ObjectContext;
 import org.apache.cayenne.di.Inject;
 import org.apache.cayenne.query.ObjectSelect;
+import org.apache.cayenne.query.SelectById;
 import org.apache.cayenne.query.SelectQuery;
 import org.apache.cayenne.test.jdbc.DBHelper;
 import org.apache.cayenne.test.jdbc.TableHelper;
@@ -29,6 +30,7 @@ import org.apache.cayenne.unit.di.server.CayenneProjects;
 import org.apache.cayenne.unit.di.server.ServerCase;
 import org.apache.cayenne.unit.di.server.UseServerRuntime;
 import org.apache.cayenne.validation.ValidationException;
+import org.junit.Ignore;
 import org.junit.Test;
 
 import java.sql.SQLException;
@@ -227,6 +229,32 @@ public class VerticalInheritanceIT extends ServerCase {
 		assertEquals(1, ivSub3Table.getRowCount());
 	}
 
+	/**
+	 * @link https://issues.apache.org/jira/browse/CAY-2282
+	 */
+	@Ignore("Test case for unfixed issue CAY-2282")
+	@Test
+	public void testUpdateRelation_Sub3() throws Exception {
+		TableHelper ivRootTable = new TableHelper(dbHelper, "IV_ROOT");
+		ivRootTable.setColumns("ID", "NAME", "DISCRIMINATOR");
+		ivRootTable.insert(1, null, null);
+		ivRootTable.insert(2, null, null);
+		ivRootTable.insert(3, "name", "IvSub3");
+
+		TableHelper ivSub3Table = new TableHelper(dbHelper, "IV_SUB3");
+		ivSub3Table.setColumns("ID", "IV_ROOT_ID");
+		ivSub3Table.insert(3, 1);
+
+		IvRoot root = SelectById.query(IvRoot.class, 2).selectOne(context);
+		IvSub3 sub3 = SelectById.query(IvSub3.class, 3).selectOne(context);
+		sub3.setName("new name");
+		sub3.setIvRoot(root);
+
+		// this will create 3 queries...
+		// update for name, insert for new relationship, delete for old relationship
+		context.commitChanges();
+	}
+
     @Test
 	public void testInsert_Sub1Sub1() throws Exception {
 
@@ -646,4 +674,82 @@ public class VerticalInheritanceIT extends ServerCase {
 		assertEquals(2, ObjectSelect.query(IvImpl.class).selectCount(context));
 	}
 
+	/**
+	 * @link https://issues.apache.org/jira/browse/CAY-2282
+	 */
+	@Ignore("Test case for unfixed issue CAY-2282")
+	@Test
+	public void testUpdateTwoObjectsWithMultipleAttributeAndMultipleRelationship() throws SQLException {
+		TableHelper ivOtherTable = new TableHelper(dbHelper, "IV_OTHER");
+		ivOtherTable.setColumns("ID", "NAME").setColumnTypes(Types.INTEGER, Types.VARCHAR);
+
+		TableHelper ivBaseTable = new TableHelper(dbHelper, "IV_BASE");
+		ivBaseTable.setColumns("ID", "NAME", "TYPE")
+				.setColumnTypes(Types.INTEGER, Types.VARCHAR, Types.CHAR);
+
+		TableHelper ivImplTable = new TableHelper(dbHelper, "IV_IMPL");
+		ivImplTable.setColumns("ID", "ATTR1", "ATTR2", "OTHER1_ID", "OTHER2_ID")
+				.setColumnTypes(Types.INTEGER, Types.VARCHAR, Types.VARCHAR, Types.INTEGER, Types.INTEGER);
+
+		// Insert records we want to update
+		ivOtherTable.insert(1, "other1");
+		ivOtherTable.insert(2, "other2");
+
+		ivBaseTable.insert(1, "Impl 1", "I");
+		ivBaseTable.insert(2, "Impl 2", "I");
+
+		ivImplTable.insert(1, "attr1", "attr2", 1, 2);
+		ivImplTable.insert(2, "attr1", "attr2", 1, 2);
+
+		// Fetch and update the records
+		IvOther other1 = ObjectSelect.query(IvOther.class).where(IvOther.NAME.eq("other1")).selectOne(context);
+		IvOther other2 = ObjectSelect.query(IvOther.class).where(IvOther.NAME.eq("other2")).selectOne(context);
+
+		for(IvImpl record : ObjectSelect.query(IvImpl.class).select(context)) {
+			record.setName(record.getName() + "-Change");
+			record.setAttr1(record.getAttr1() + "-Change");
+			record.setAttr2(record.getAttr2() + "-Change");
+			record.setOther1(other2);
+			record.setOther2(other1);
+		}
+
+		context.commitChanges();
+
+		// todo: add some assertions after fixing commit bug above
+
+	}
+
+	/**
+	 * @link https://issues.apache.org/jira/browse/CAY-2282
+	 */
+	@Test
+	public void testUpdateWithOptimisticLocks() throws SQLException {
+		TableHelper ivOtherTable = new TableHelper(dbHelper, "IV_OTHER");
+		ivOtherTable.setColumns("ID", "NAME").setColumnTypes(Types.INTEGER, Types.VARCHAR);
+
+		TableHelper ivBaseWithLockTable = new TableHelper(dbHelper, "IV_BASE_WITH_LOCK");
+		ivBaseWithLockTable.setColumns("ID", "NAME", "TYPE")
+				.setColumnTypes(Types.INTEGER, Types.VARCHAR, Types.CHAR);
+
+		TableHelper ivImplWithLockTable = new TableHelper(dbHelper, "IV_IMPL_WITH_LOCK");
+		ivImplWithLockTable.setColumns("ID", "ATTR1", "OTHER1_ID")
+				.setColumnTypes(Types.INTEGER, Types.VARCHAR, Types.INTEGER);
+
+		// Insert records we want to update (will end up adding more records for final test)
+		ivOtherTable.insert(1, "other1");
+
+		ivBaseWithLockTable.insert(1, "Impl 1", "I");
+
+		ivImplWithLockTable.insert(1, "attr1", 1);
+
+		// Fetch and update the records
+		for(IvImplWithLock record : ObjectSelect.query(IvImplWithLock.class).select(context)) {
+			record.setName(record.getName() + "-Change");
+			record.setAttr1(record.getAttr1() + "-Change");
+		}
+
+		// commit should pass without any exceptions
+		context.commitChanges();
+	}
+
 }

http://git-wip-us.apache.org/repos/asf/cayenne/blob/2e0acfef/cayenne-server/src/test/java/org/apache/cayenne/testdo/inheritance_vertical/IvBaseWithLock.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/testdo/inheritance_vertical/IvBaseWithLock.java b/cayenne-server/src/test/java/org/apache/cayenne/testdo/inheritance_vertical/IvBaseWithLock.java
new file mode 100644
index 0000000..acc6a28
--- /dev/null
+++ b/cayenne-server/src/test/java/org/apache/cayenne/testdo/inheritance_vertical/IvBaseWithLock.java
@@ -0,0 +1,28 @@
+/*****************************************************************
+ *   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.testdo.inheritance_vertical;
+
+import org.apache.cayenne.testdo.inheritance_vertical.auto._IvBaseWithLock;
+
+public abstract class IvBaseWithLock extends _IvBaseWithLock {
+
+    private static final long serialVersionUID = 1L; 
+
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/2e0acfef/cayenne-server/src/test/java/org/apache/cayenne/testdo/inheritance_vertical/IvImplWithLock.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/testdo/inheritance_vertical/IvImplWithLock.java b/cayenne-server/src/test/java/org/apache/cayenne/testdo/inheritance_vertical/IvImplWithLock.java
new file mode 100644
index 0000000..9056874
--- /dev/null
+++ b/cayenne-server/src/test/java/org/apache/cayenne/testdo/inheritance_vertical/IvImplWithLock.java
@@ -0,0 +1,28 @@
+/*****************************************************************
+ *   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.testdo.inheritance_vertical;
+
+import org.apache.cayenne.testdo.inheritance_vertical.auto._IvImplWithLock;
+
+public class IvImplWithLock extends _IvImplWithLock {
+
+    private static final long serialVersionUID = 1L; 
+
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/2e0acfef/cayenne-server/src/test/java/org/apache/cayenne/testdo/inheritance_vertical/auto/_IvBaseWithLock.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/testdo/inheritance_vertical/auto/_IvBaseWithLock.java b/cayenne-server/src/test/java/org/apache/cayenne/testdo/inheritance_vertical/auto/_IvBaseWithLock.java
new file mode 100644
index 0000000..1fd3765
--- /dev/null
+++ b/cayenne-server/src/test/java/org/apache/cayenne/testdo/inheritance_vertical/auto/_IvBaseWithLock.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.testdo.inheritance_vertical.auto;
+
+import org.apache.cayenne.CayenneDataObject;
+import org.apache.cayenne.exp.Property;
+
+/**
+ * Class _IvBase was generated by Cayenne.
+ * It is probably a good idea to avoid changing this class manually,
+ * since it may be overwritten next time code is regenerated.
+ * If you need to make any customizations, please use subclass.
+ */
+public abstract class _IvBaseWithLock extends CayenneDataObject {
+
+    private static final long serialVersionUID = 1L; 
+
+    public static final String ID_PK_COLUMN = "ID";
+
+    public static final Property<String> NAME = Property.create("name", String.class);
+    public static final Property<String> TYPE = Property.create("type", String.class);
+
+    public void setName(String name) {
+        writeProperty("name", name);
+    }
+    public String getName() {
+        return (String)readProperty("name");
+    }
+
+    public void setType(String type) {
+        writeProperty("type", type);
+    }
+    public String getType() {
+        return (String)readProperty("type");
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/2e0acfef/cayenne-server/src/test/java/org/apache/cayenne/testdo/inheritance_vertical/auto/_IvImplWithLock.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/testdo/inheritance_vertical/auto/_IvImplWithLock.java b/cayenne-server/src/test/java/org/apache/cayenne/testdo/inheritance_vertical/auto/_IvImplWithLock.java
new file mode 100644
index 0000000..b6836ea
--- /dev/null
+++ b/cayenne-server/src/test/java/org/apache/cayenne/testdo/inheritance_vertical/auto/_IvImplWithLock.java
@@ -0,0 +1,57 @@
+/*****************************************************************
+ *   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.testdo.inheritance_vertical.auto;
+
+import org.apache.cayenne.exp.Property;
+import org.apache.cayenne.testdo.inheritance_vertical.IvBase;
+import org.apache.cayenne.testdo.inheritance_vertical.IvOther;
+
+/**
+ * Class _IvImpl was generated by Cayenne.
+ * It is probably a good idea to avoid changing this class manually,
+ * since it may be overwritten next time code is regenerated.
+ * If you need to make any customizations, please use subclass.
+ */
+public abstract class _IvImplWithLock extends IvBase {
+
+    private static final long serialVersionUID = 1L; 
+
+    public static final String ID_PK_COLUMN = "ID";
+
+    public static final Property<String> ATTR1 = Property.create("attr1", String.class);
+    public static final Property<IvOther> OTHER1 = Property.create("other1", IvOther.class);
+
+    public void setAttr1(String attr1) {
+        writeProperty("attr1", attr1);
+    }
+    public String getAttr1() {
+        return (String)readProperty("attr1");
+    }
+
+    public void setOther1(IvOther other1) {
+        setToOneTarget("other1", other1, true);
+    }
+
+    public IvOther getOther1() {
+        return (IvOther)readProperty("other1");
+    }
+
+
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/2e0acfef/cayenne-server/src/test/java/org/apache/cayenne/testdo/inheritance_vertical/auto/_IvOther.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/testdo/inheritance_vertical/auto/_IvOther.java b/cayenne-server/src/test/java/org/apache/cayenne/testdo/inheritance_vertical/auto/_IvOther.java
index d26c639..d3f9aa2 100644
--- a/cayenne-server/src/test/java/org/apache/cayenne/testdo/inheritance_vertical/auto/_IvOther.java
+++ b/cayenne-server/src/test/java/org/apache/cayenne/testdo/inheritance_vertical/auto/_IvOther.java
@@ -2,6 +2,10 @@ package org.apache.cayenne.testdo.inheritance_vertical.auto;
 
 import org.apache.cayenne.CayenneDataObject;
 import org.apache.cayenne.exp.Property;
+import org.apache.cayenne.testdo.inheritance_vertical.IvImpl;
+import org.apache.cayenne.testdo.inheritance_vertical.IvImplWithLock;
+
+import java.util.List;
 
 /**
  * Class _IvOther was generated by Cayenne.
@@ -16,6 +20,8 @@ public abstract class _IvOther extends CayenneDataObject {
     public static final String ID_PK_COLUMN = "ID";
 
     public static final Property<String> NAME = Property.create("name", String.class);
+    public static final Property<List<IvImpl>> IMPLS = Property.create("impls", List.class);
+    public static final Property<List<IvImplWithLock>> IMPLS_WITH_LOCK = Property.create("implsWithLock", List.class);
 
     public void setName(String name) {
         writeProperty("name", name);
@@ -24,4 +30,27 @@ public abstract class _IvOther extends CayenneDataObject {
         return (String)readProperty("name");
     }
 
+    public void addToImpls(IvImpl obj) {
+        addToManyTarget("impls", obj, true);
+    }
+    public void removeFromImpls(IvImpl obj) {
+        removeToManyTarget("impls", obj, true);
+    }
+    @SuppressWarnings("unchecked")
+    public List<IvImpl> getImpls() {
+        return (List<IvImpl>)readProperty("impls");
+    }
+
+    public void addToImplsWithLock(IvImplWithLock obj) {
+        addToManyTarget("implsWithLock", obj, true);
+    }
+    public void removeFromImplsWithLock(IvImplWithLock obj) {
+        removeToManyTarget("implsWithLock", obj, true);
+    }
+    @SuppressWarnings("unchecked")
+    public List<IvImplWithLock> getImplsWithLock() {
+        return (List<IvImplWithLock>)readProperty("implsWithLock");
+    }
+
+
 }

http://git-wip-us.apache.org/repos/asf/cayenne/blob/2e0acfef/cayenne-server/src/test/resources/inheritance-vertical.map.xml
----------------------------------------------------------------------
diff --git a/cayenne-server/src/test/resources/inheritance-vertical.map.xml b/cayenne-server/src/test/resources/inheritance-vertical.map.xml
index 14047a2..5183951 100644
--- a/cayenne-server/src/test/resources/inheritance-vertical.map.xml
+++ b/cayenne-server/src/test/resources/inheritance-vertical.map.xml
@@ -34,6 +34,11 @@
 		<db-attribute name="NAME" type="VARCHAR" isMandatory="true" length="100"/>
 		<db-attribute name="TYPE" type="CHAR" isMandatory="true" length="1"/>
 	</db-entity>
+	<db-entity name="IV_BASE_WITH_LOCK">
+		<db-attribute name="ID" type="INTEGER" isPrimaryKey="true" isMandatory="true"/>
+		<db-attribute name="NAME" type="VARCHAR" isMandatory="true" length="100"/>
+		<db-attribute name="TYPE" type="CHAR" isMandatory="true" length="1"/>
+	</db-entity>
 	<db-entity name="IV_CONCRETE">
 		<db-attribute name="ID" type="INTEGER" isPrimaryKey="true" isMandatory="true"/>
 		<db-attribute name="NAME" type="VARCHAR" length="100"/>
@@ -45,6 +50,11 @@
 		<db-attribute name="OTHER1_ID" type="INTEGER" isMandatory="true"/>
 		<db-attribute name="OTHER2_ID" type="INTEGER" isMandatory="true"/>
 	</db-entity>
+	<db-entity name="IV_IMPL_WITH_LOCK">
+		<db-attribute name="ATTR1" type="VARCHAR" isMandatory="true" length="100"/>
+		<db-attribute name="ID" type="INTEGER" isPrimaryKey="true" isMandatory="true"/>
+		<db-attribute name="OTHER1_ID" type="INTEGER" isMandatory="true"/>
+	</db-entity>
 	<db-entity name="IV_OTHER">
 		<db-attribute name="ID" type="INTEGER" isPrimaryKey="true" isMandatory="true"/>
 		<db-attribute name="NAME" type="VARCHAR" length="100"/>
@@ -95,6 +105,10 @@
 		<obj-attribute name="name" type="java.lang.String" db-attribute-path="NAME"/>
 		<obj-attribute name="type" type="java.lang.String" db-attribute-path="TYPE"/>
 	</obj-entity>
+	<obj-entity name="IvBaseWithLock" abstract="true" className="org.apache.cayenne.testdo.inheritance_vertical.IvBaseWithLock" lock-type="optimistic" dbEntityName="IV_BASE_WITH_LOCK">
+		<obj-attribute name="name" type="java.lang.String" lock="true" db-attribute-path="NAME"/>
+		<obj-attribute name="type" type="java.lang.String" db-attribute-path="TYPE"/>
+	</obj-entity>
 	<obj-entity name="IvConcrete" superEntityName="IvAbstract" className="org.apache.cayenne.testdo.inheritance_vertical.IvConcrete">
 		<qualifier><![CDATA[type = "S"]]></qualifier>
 		<obj-attribute name="name" type="java.lang.String" db-attribute-path="concrete.NAME"/>
@@ -104,6 +118,10 @@
 		<obj-attribute name="attr1" type="java.lang.String" db-attribute-path="impl.ATTR1"/>
 		<obj-attribute name="attr2" type="java.lang.String" db-attribute-path="impl.ATTR2"/>
 	</obj-entity>
+	<obj-entity name="IvImplWithLock" superEntityName="IvBaseWithLock" className="org.apache.cayenne.testdo.inheritance_vertical.IvImplWithLock">
+		<qualifier><![CDATA[type = "I"]]></qualifier>
+		<obj-attribute name="attr1" type="java.lang.String" db-attribute-path="impl.ATTR1"/>
+	</obj-entity>
 	<obj-entity name="IvOther" className="org.apache.cayenne.testdo.inheritance_vertical.IvOther" dbEntityName="IV_OTHER">
 		<obj-attribute name="name" type="java.lang.String" db-attribute-path="NAME"/>
 	</obj-entity>
@@ -162,18 +180,33 @@
 	<db-relationship name="impl" source="IV_BASE" target="IV_IMPL" toDependentPK="true" toMany="false">
 		<db-attribute-pair source="ID" target="ID"/>
 	</db-relationship>
+	<db-relationship name="impl" source="IV_BASE_WITH_LOCK" target="IV_IMPL_WITH_LOCK" toDependentPK="true" toMany="false">
+		<db-attribute-pair source="ID" target="ID"/>
+	</db-relationship>
 	<db-relationship name="abstract" source="IV_CONCRETE" target="IV_ABSTRACT" toMany="false">
 		<db-attribute-pair source="ID" target="ID"/>
 	</db-relationship>
 	<db-relationship name="base" source="IV_IMPL" target="IV_BASE" toMany="false">
 		<db-attribute-pair source="ID" target="ID"/>
 	</db-relationship>
+	<db-relationship name="base" source="IV_IMPL_WITH_LOCK" target="IV_BASE_WITH_LOCK" toMany="false">
+		<db-attribute-pair source="ID" target="ID"/>
+	</db-relationship>
 	<db-relationship name="other1" source="IV_IMPL" target="IV_OTHER" toMany="false">
 		<db-attribute-pair source="OTHER1_ID" target="ID"/>
 	</db-relationship>
 	<db-relationship name="other2" source="IV_IMPL" target="IV_OTHER" toMany="false">
 		<db-attribute-pair source="OTHER2_ID" target="ID"/>
 	</db-relationship>
+	<db-relationship name="other1" source="IV_IMPL_WITH_LOCK" target="IV_OTHER" toMany="false">
+		<db-attribute-pair source="OTHER1_ID" target="ID"/>
+	</db-relationship>
+	<db-relationship name="impls" source="IV_OTHER" target="IV_IMPL" toMany="true">
+		<db-attribute-pair source="ID" target="OTHER_ID"/>
+	</db-relationship>
+	<db-relationship name="implsWithLock" source="IV_OTHER" target="IV_IMPL_WITH_LOCK" toMany="true">
+		<db-attribute-pair source="ID" target="OTHER_ID"/>
+	</db-relationship>
 	<db-relationship name="ivSub3s" source="IV_ROOT" target="IV_SUB3" toMany="true">
 		<db-attribute-pair source="ID" target="IV_ROOT_ID"/>
 	</db-relationship>
@@ -206,5 +239,8 @@
 	<obj-relationship name="parent" source="IvConcrete" target="IvConcrete" deleteRule="Nullify" db-relationship-path="parent"/>
 	<obj-relationship name="other1" source="IvImpl" target="IvOther" deleteRule="Nullify" db-relationship-path="impl.other1"/>
 	<obj-relationship name="other2" source="IvImpl" target="IvOther" deleteRule="Nullify" db-relationship-path="impl.other2"/>
+	<obj-relationship name="other1" source="IvImplWithLock" target="IvOther" deleteRule="Nullify" db-relationship-path="impl.other1"/>
+	<obj-relationship name="impls" source="IvOther" target="IvImpl" deleteRule="Deny" db-relationship-path="impls.base"/>
+	<obj-relationship name="implsWithLock" source="IvOther" target="IvImplWithLock" deleteRule="Deny" db-relationship-path="impls.base"/>
 	<obj-relationship name="ivRoot" source="IvSub3" target="IvRoot" deleteRule="Nullify" db-relationship-path="sub3.ivRoot1"/>
 </data-map>


Mime
View raw message