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 <stari...@gmail.com>
Authored: Thu May 4 14:44:59 2017 +0300
Committer: Nikita Timofeev <stari...@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>

Reply via email to