You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cayenne.apache.org by nt...@apache.org on 2018/05/07 13:55:28 UTC
cayenne git commit: CAY-2282 Various Update Issues With Vertical
Inheritance
Repository: cayenne
Updated Branches:
refs/heads/master feaa5da08 -> f0b2ed009
CAY-2282 Various Update Issues With Vertical Inheritance
Project: http://git-wip-us.apache.org/repos/asf/cayenne/repo
Commit: http://git-wip-us.apache.org/repos/asf/cayenne/commit/f0b2ed00
Tree: http://git-wip-us.apache.org/repos/asf/cayenne/tree/f0b2ed00
Diff: http://git-wip-us.apache.org/repos/asf/cayenne/diff/f0b2ed00
Branch: refs/heads/master
Commit: f0b2ed009e0ffec167f002ed046bc9228e11993c
Parents: feaa5da
Author: Nikita Timofeev <st...@gmail.com>
Authored: Mon May 7 16:55:14 2018 +0300
Committer: Nikita Timofeev <st...@gmail.com>
Committed: Mon May 7 16:55:14 2018 +0300
----------------------------------------------------------------------
.../cayenne/access/DataDomainDBDiffBuilder.java | 24 +-
.../access/DataDomainIndirectDiffBuilder.java | 40 ++-
.../cayenne/access/DataDomainInsertBucket.java | 45 +++
.../apache/cayenne/access/ObjectResolver.java | 39 ++-
.../org/apache/cayenne/access/ObjectStore.java | 46 ++-
.../select/DefaultSelectTranslator.java | 13 +
.../apache/cayenne/reflect/ClassDescriptor.java | 12 +
.../reflect/LazyClassDescriptorDecorator.java | 7 +
.../cayenne/reflect/PersistentDescriptor.java | 24 ++
.../reflect/PersistentDescriptorFactory.java | 59 +++-
.../org/apache/cayenne/CDOOneToOneFKIT.java | 11 +
.../cayenne/access/VerticalInheritanceIT.java | 49 +--
.../VerticalInheritanceMultipleAttributes.java | 310 +++++++++++++++++++
.../resources/cayenne-inheritance-vertical.xml | 2 +
.../test/resources/inheritance-vertical.map.xml | 8 +-
15 files changed, 608 insertions(+), 81 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cayenne/blob/f0b2ed00/cayenne-server/src/main/java/org/apache/cayenne/access/DataDomainDBDiffBuilder.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/DataDomainDBDiffBuilder.java b/cayenne-server/src/main/java/org/apache/cayenne/access/DataDomainDBDiffBuilder.java
index d1da31e..80db9c5 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/access/DataDomainDBDiffBuilder.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/access/DataDomainDBDiffBuilder.java
@@ -24,7 +24,13 @@ import org.apache.cayenne.access.DataDomainSyncBucket.PropagatedValueFactory;
import org.apache.cayenne.exp.parser.ASTDbPath;
import org.apache.cayenne.graph.GraphChangeHandler;
import org.apache.cayenne.graph.GraphDiff;
-import org.apache.cayenne.map.*;
+import org.apache.cayenne.map.DbAttribute;
+import org.apache.cayenne.map.DbEntity;
+import org.apache.cayenne.map.DbJoin;
+import org.apache.cayenne.map.DbRelationship;
+import org.apache.cayenne.map.ObjAttribute;
+import org.apache.cayenne.map.ObjEntity;
+import org.apache.cayenne.map.ObjRelationship;
import java.util.HashMap;
import java.util.Map;
@@ -112,14 +118,15 @@ class DataDomainDBDiffBuilder implements GraphChangeHandler {
if (relation == null) {
dbRelation = dbEntity.getRelationship(arcIdString.substring(ASTDbPath.DB_PREFIX.length()));
} else {
- dbRelation = relation.getDbRelationships().get(0);
+ dbRelation = relation.getDbRelationships().get(relation.getDbRelationships().size() - 1);
}
// In case of a vertical inheritance, ensure that it belongs to this bucket...
if (dbRelation.getSourceEntity() == dbEntity) {
ObjectId targetId = (ObjectId) entry.getValue();
for (DbJoin join : dbRelation.getJoins()) {
- Object value = (targetId != null) ? new PropagatedValueFactory(targetId, join.getTargetName())
+ Object value = (targetId != null)
+ ? new PropagatedValueFactory(targetId, join.getTargetName())
: null;
dbDiff.put(join.getSourceName(), value);
@@ -161,9 +168,8 @@ class DataDomainDBDiffBuilder implements GraphChangeHandler {
if (relationship == null) {
// phantom FK
if (arcIdString.startsWith(ASTDbPath.DB_PREFIX)) {
-
- DbRelationship dbRelationship = dbEntity.getRelationship(arcIdString.substring(ASTDbPath.DB_PREFIX
- .length()));
+ String relName = arcIdString.substring(ASTDbPath.DB_PREFIX.length());
+ DbRelationship dbRelationship = dbEntity.getRelationship(relName);
if (!dbRelationship.isSourceIndependentFromTargetChange()) {
doArcCreated(targetNodeId, arcId);
}
@@ -171,8 +177,10 @@ class DataDomainDBDiffBuilder implements GraphChangeHandler {
throw new IllegalArgumentException("Bad arcId: " + arcId);
}
- } else if (!relationship.isSourceIndependentFromTargetChange()) {
- doArcCreated(targetNodeId, arcId);
+ } else {
+ if (!relationship.isToMany() && relationship.isToPK()) {
+ doArcCreated(targetNodeId, arcId);
+ }
}
}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/f0b2ed00/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 e7ec2d8..1c8e4d7 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
@@ -27,6 +27,7 @@ import org.apache.cayenne.ObjectId;
import org.apache.cayenne.graph.GraphChangeHandler;
import org.apache.cayenne.graph.GraphDiff;
import org.apache.cayenne.map.DbEntity;
+import org.apache.cayenne.map.DbRelationship;
import org.apache.cayenne.map.EntityResolver;
import org.apache.cayenne.map.ObjEntity;
import org.apache.cayenne.map.ObjRelationship;
@@ -90,12 +91,20 @@ 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)
- FlattenedArcKey key = new FlattenedArcKey((ObjectId) nodeId, (ObjectId) targetNodeId, relationship);
+ String path = relationship.getDbRelationshipPath();
+ int lastDot = path.lastIndexOf('.');
+ if(lastDot > -1) {
+ path = path.substring(0, lastDot);
+ }
+
+ if(!parent.getContext().getObjectStore().hasFlattenedPath(nodeObjectId, path)) {
+ // Register this combination (so we can remove it later if an insert occurs before commit)
+ FlattenedArcKey key = new FlattenedArcKey(nodeObjectId, (ObjectId) targetNodeId, relationship);
- // If this combination has already been deleted, simply undelete it.
- if (!flattenedDeletes.remove(key)) {
- flattenedInserts.add(key);
+ // If this combination has already been deleted, simply undelete it.
+ if (!flattenedDeletes.remove(key)) {
+ flattenedInserts.add(key);
+ }
}
}
}
@@ -120,12 +129,23 @@ final class DataDomainIndirectDiffBuilder implements GraphChangeHandler {
, relationship.getName());
}
- // Register this combination (so we can remove it later if an insert occurs before commit)
- FlattenedArcKey key = new FlattenedArcKey((ObjectId) nodeId, (ObjectId) targetNodeId, relationship);
+ // build path without last segment
+ StringBuilder path = new StringBuilder();
+ for(int i=0; i<relationship.getDbRelationships().size() - 1; i++) {
+ if(path.length() > 0) {
+ path.append('.');
+ }
+ path.append(relationship.getDbRelationships().get(i).getName());
+ }
+
+ if(!parent.getContext().getObjectStore().hasFlattenedPath(nodeObjectId, path.toString())) {
+ // Register this combination (so we can remove it later if an insert occurs before commit)
+ FlattenedArcKey key = new FlattenedArcKey(nodeObjectId, (ObjectId) targetNodeId, relationship);
- // If this combination has already been inserted, simply "uninsert" it also do not delete it twice
- if (!flattenedInserts.remove(key)) {
- flattenedDeletes.add(key);
+ // 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/f0b2ed00/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 9df6f05..eee50ab 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
@@ -21,6 +21,7 @@ package org.apache.cayenne.access;
import java.util.Collection;
import java.util.HashMap;
+import java.util.LinkedList;
import java.util.List;
import java.util.Map;
@@ -43,6 +44,8 @@ import org.apache.cayenne.query.Query;
*/
class DataDomainInsertBucket extends DataDomainSyncBucket {
+ List<FlattenedInsert> flattenedInserts;
+
DataDomainInsertBucket(DataDomainFlushAction parent) {
super(parent);
}
@@ -83,6 +86,9 @@ class DataDomainInsertBucket extends DataDomainSyncBucket {
}
batch.add(snapshot, o.getObjectId());
+ if(!descriptor.isMaster()) {
+ trackFlattenedInsert(descriptor, o);
+ }
}
}
@@ -186,4 +192,43 @@ class DataDomainInsertBucket extends DataDomainSyncBucket {
return false;
}
+
+ void trackFlattenedInsert(DbEntityClassDescriptor descriptor, Persistent object) {
+ if(flattenedInserts == null) {
+ flattenedInserts = new LinkedList<>();
+ }
+
+ StringBuilder sb = new StringBuilder();
+ for(DbRelationship rel : descriptor.getPathFromMaster()) {
+ if(sb.length() > 0) {
+ sb.append('.');
+ }
+ sb.append(rel.getName());
+ }
+
+ flattenedInserts.add(new FlattenedInsert(sb.toString(), object));
+ }
+
+ @Override
+ void postprocess() {
+ super.postprocess();
+ if(flattenedInserts != null) {
+ for(FlattenedInsert insert : flattenedInserts) {
+ insert.register(parent.getContext().getObjectStore());
+ }
+ }
+ }
+
+ private static class FlattenedInsert {
+ private final String path;
+ private final Persistent object;
+ private FlattenedInsert(String path, Persistent object) {
+ this.path = path;
+ this.object = object;
+ }
+
+ private void register(ObjectStore objectStore) {
+ objectStore.markFlattenedPath(object.getObjectId(), path);
+ }
+ }
}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/f0b2ed00/cayenne-server/src/main/java/org/apache/cayenne/access/ObjectResolver.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/ObjectResolver.java b/cayenne-server/src/main/java/org/apache/cayenne/access/ObjectResolver.java
index dd1620b..d658abf 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/access/ObjectResolver.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/access/ObjectResolver.java
@@ -145,6 +145,9 @@ class ObjectResolver {
// this will create a HOLLOW object if it is not registered yet
Persistent object = context.findOrCreateObject(anId);
+ // resolve additional Object IDs for flattened attributes
+ resolveAdditionalIds(row, object, classDescriptor);
+
// deal with object state
int state = object.getPersistenceState();
switch (state) {
@@ -179,7 +182,24 @@ class ObjectResolver {
return object;
}
- ObjEntity getEntity() {
+ private void resolveAdditionalIds(DataRow row, Persistent object, ClassDescriptor classDescriptor) {
+ if(classDescriptor.getAdditionalDbEntities().isEmpty()) {
+ return;
+ }
+
+ for(Map.Entry<String, DbEntity> entry : classDescriptor.getAdditionalDbEntities().entrySet()) {
+ DbEntity dbEntity = entry.getValue();
+ String path = entry.getKey();
+ int lastDot = path.lastIndexOf('.');
+ String prefix = lastDot == -1 ? path : path.substring(lastDot + 1);
+ ObjectId objectId = createObjectId(row, dbEntity.getName(), dbEntity.getPrimaryKeys(), prefix + '.', false);
+ if(objectId != null) {
+ context.getObjectStore().markFlattenedPath(object.getObjectId(), path);
+ }
+ }
+ }
+
+ ObjEntity getEntity() {
return descriptor.getEntity();
}
@@ -196,10 +216,13 @@ class ObjectResolver {
}
ObjectId createObjectId(DataRow dataRow, ObjEntity objEntity, String namePrefix) {
+ Collection<DbAttribute> pk = objEntity == this.descriptor.getEntity()
+ ? this.primaryKey
+ : objEntity.getDbEntity().getPrimaryKeys();
+ return createObjectId(dataRow, objEntity.getName(), pk, namePrefix, true);
+ }
- Collection<DbAttribute> pk = objEntity == this.descriptor.getEntity() ? this.primaryKey : objEntity
- .getDbEntity().getPrimaryKeys();
-
+ ObjectId createObjectId(DataRow dataRow, String name, Collection<DbAttribute> pk, String namePrefix, boolean strict) {
boolean prefix = namePrefix != null && namePrefix.length() > 0;
// ... handle special case - PK.size == 1
@@ -214,14 +237,14 @@ class ObjectResolver {
// this is possible when processing left outer joint prefetches
if (val == null) {
- if(!dataRow.containsKey(key)) {
+ if(strict && !dataRow.containsKey(key)) {
throw new CayenneRuntimeException("No PK column '%s' found in data row.", key);
}
return null;
}
// PUT without a prefix
- return new ObjectId(objEntity.getName(), attribute.getName(), val);
+ return new ObjectId(name, attribute.getName(), val);
}
// ... handle generic case - PK.size > 1
@@ -235,7 +258,7 @@ class ObjectResolver {
// this is possible when processing left outer joint prefetches
if (val == null) {
- if(!dataRow.containsKey(key)) {
+ if(strict && !dataRow.containsKey(key)) {
throw new CayenneRuntimeException("No PK column '%s' found in data row.", key);
}
return null;
@@ -245,7 +268,7 @@ class ObjectResolver {
idMap.put(attribute.getName(), val);
}
- return new ObjectId(objEntity.getName(), idMap);
+ return new ObjectId(name, idMap);
}
interface DescriptorResolutionStrategy {
http://git-wip-us.apache.org/repos/asf/cayenne/blob/f0b2ed00/cayenne-server/src/main/java/org/apache/cayenne/access/ObjectStore.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/ObjectStore.java b/cayenne-server/src/main/java/org/apache/cayenne/access/ObjectStore.java
index bebada0..4bb72c2 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/access/ObjectStore.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/access/ObjectStore.java
@@ -52,6 +52,8 @@ import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
/**
* ObjectStore stores objects using their ObjectId as a key. It works as a dedicated
@@ -66,6 +68,13 @@ public class ObjectStore implements Serializable, SnapshotEventListener, GraphMa
protected Map<Object, Persistent> objectMap;
protected Map<Object, ObjectDiff> changes;
+ /**
+ * Map that tracks flattened paths for given object Id that is present in db.
+ * Presence of path in this map is used to separate insert from update case of flattened records.
+ * @since 4.1
+ */
+ protected Map<Object, Set<String>> trackedFlattenedPaths;
+
// a sequential id used to tag GraphDiffs so that they can later be sorted in the
// original creation order
int currentDiffId;
@@ -293,6 +302,9 @@ public class ObjectStore implements Serializable, SnapshotEventListener, GraphMa
// remove object but not snapshot
objectMap.remove(id);
changes.remove(id);
+ if(id != null && trackedFlattenedPaths != null) {
+ trackedFlattenedPaths.remove(id);
+ }
ids.add(id);
object.setObjectContext(null);
@@ -589,6 +601,13 @@ public class ObjectStore implements Serializable, SnapshotEventListener, GraphMa
changes.put(newId, change);
}
}
+
+ if(trackedFlattenedPaths != null) {
+ Set<String> paths = trackedFlattenedPaths.remove(nodeId);
+ if(paths != null) {
+ trackedFlattenedPaths.put(newId, paths);
+ }
+ }
}
/**
@@ -965,7 +984,32 @@ public class ObjectStore implements Serializable, SnapshotEventListener, GraphMa
registerLifecycleEventInducedChange(diff);
}
- registerDiff((ObjectId)nodeId, diff);
+ registerDiff(nodeId, diff);
+ }
+
+ /**
+ * Check that flattened path for given object ID has data row in DB.
+ * @since 4.1
+ */
+ boolean hasFlattenedPath(ObjectId objectId, String path) {
+ if(trackedFlattenedPaths == null) {
+ return false;
+ }
+ return trackedFlattenedPaths
+ .getOrDefault(objectId, Collections.emptySet()).contains(path);
+ }
+
+ /**
+ * Mark that flattened path for object has data row in DB.
+ * @since 4.1
+ */
+ void markFlattenedPath(ObjectId objectId, String path) {
+ if(trackedFlattenedPaths == null) {
+ trackedFlattenedPaths = new ConcurrentHashMap<>();
+ }
+ trackedFlattenedPaths
+ .computeIfAbsent(objectId, o -> Collections.newSetFromMap(new ConcurrentHashMap<>()))
+ .add(path);
}
// an ObjectIdQuery optimized for retrieval of multiple snapshots - it can be reset
http://git-wip-us.apache.org/repos/asf/cayenne/blob/f0b2ed00/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/DefaultSelectTranslator.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/DefaultSelectTranslator.java b/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/DefaultSelectTranslator.java
index b48329e..6cbf284 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/DefaultSelectTranslator.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/DefaultSelectTranslator.java
@@ -616,6 +616,19 @@ public class DefaultSelectTranslator extends QueryAssembler implements SelectTra
public boolean visitToOne(ToOneProperty property) {
visitRelationship(property);
+
+ // add PKs for flattened tables in flattened path
+ ObjRelationship rel = property.getRelationship();
+ for(int i=0; i<rel.getDbRelationships().size() - 1; i++) {
+ DbRelationship dbRel = rel.getDbRelationships().get(i);
+ dbRelationshipAdded(dbRel, JoinType.LEFT_OUTER, null);
+
+ // append path PK attributes
+ for(DbAttribute dba : dbRel.getTargetEntity().getPrimaryKeys()) {
+ appendColumn(columns, null, dba, attributes, dbRel.getName() + '.' + dba.getName());
+ }
+ }
+
return true;
}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/f0b2ed00/cayenne-server/src/main/java/org/apache/cayenne/reflect/ClassDescriptor.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/reflect/ClassDescriptor.java b/cayenne-server/src/main/java/org/apache/cayenne/reflect/ClassDescriptor.java
index 597b76e..d2e39fb 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/reflect/ClassDescriptor.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/reflect/ClassDescriptor.java
@@ -20,6 +20,7 @@
package org.apache.cayenne.reflect;
import java.util.Collection;
+import java.util.Map;
import org.apache.cayenne.exp.Expression;
import org.apache.cayenne.map.DbEntity;
@@ -52,6 +53,17 @@ public interface ClassDescriptor {
Collection<DbEntity> getRootDbEntities();
/**
+ * Returns information about additional db entities that is used for this ObjEntity (i.e. for flattened attributes).
+ * <p>
+ * Keys are full paths for corresponding flattened attributes.
+ * <p>
+ *
+ * @since 4.1
+ * @return information about additional db entities
+ */
+ Map<String, DbEntity> getAdditionalDbEntities();
+
+ /**
* @since 3.0
*/
EntityInheritanceTree getEntityInheritanceTree();
http://git-wip-us.apache.org/repos/asf/cayenne/blob/f0b2ed00/cayenne-server/src/main/java/org/apache/cayenne/reflect/LazyClassDescriptorDecorator.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/reflect/LazyClassDescriptorDecorator.java b/cayenne-server/src/main/java/org/apache/cayenne/reflect/LazyClassDescriptorDecorator.java
index 518e829..5fe618d 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/reflect/LazyClassDescriptorDecorator.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/reflect/LazyClassDescriptorDecorator.java
@@ -19,6 +19,7 @@
package org.apache.cayenne.reflect;
import java.util.Collection;
+import java.util.Map;
import org.apache.cayenne.exp.Expression;
import org.apache.cayenne.map.DbEntity;
@@ -88,6 +89,12 @@ public class LazyClassDescriptorDecorator implements ClassDescriptor {
return descriptor.getRootDbEntities();
}
+ @Override
+ public Map<String, DbEntity> getAdditionalDbEntities() {
+ checkDescriptorInitialized();
+ return descriptor.getAdditionalDbEntities();
+ }
+
public EntityInheritanceTree getEntityInheritanceTree() {
checkDescriptorInitialized();
return descriptor.getEntityInheritanceTree();
http://git-wip-us.apache.org/repos/asf/cayenne/blob/f0b2ed00/cayenne-server/src/main/java/org/apache/cayenne/reflect/PersistentDescriptor.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/reflect/PersistentDescriptor.java b/cayenne-server/src/main/java/org/apache/cayenne/reflect/PersistentDescriptor.java
index 548ea90..ba17201 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/reflect/PersistentDescriptor.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/reflect/PersistentDescriptor.java
@@ -60,6 +60,8 @@ public class PersistentDescriptor implements ClassDescriptor {
protected ObjEntity entity;
protected Collection<DbEntity> rootDbEntities;
+ protected Map<String, DbEntity> additionalDbEntities;
+
protected EntityInheritanceTree entityInheritanceTree;
// combines declared and super properties
@@ -118,6 +120,20 @@ public class PersistentDescriptor implements ClassDescriptor {
this.rootDbEntities.add(dbEntity);
}
+ /**
+ * Adds additional DbEntity for this descriptor.
+ *
+ * @param path path for entity
+ * @param targetEntity additional entity
+ */
+ void addAdditionalDbEntity(String path, DbEntity targetEntity) {
+ if(additionalDbEntities == null) {
+ additionalDbEntities = new HashMap<>();
+ }
+
+ additionalDbEntities.put(path, targetEntity);
+ }
+
void sortProperties() {
// ensure properties are stored in predictable order per CAY-1729
@@ -213,6 +229,14 @@ public class PersistentDescriptor implements ClassDescriptor {
return rootDbEntities;
}
+ @Override
+ public Map<String, DbEntity> getAdditionalDbEntities() {
+ if(additionalDbEntities == null) {
+ return Collections.emptyMap();
+ }
+ return additionalDbEntities;
+ }
+
public boolean isFault(Object object) {
if (superclassDescriptor != null) {
return superclassDescriptor.isFault(object);
http://git-wip-us.apache.org/repos/asf/cayenne/blob/f0b2ed00/cayenne-server/src/main/java/org/apache/cayenne/reflect/PersistentDescriptorFactory.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/reflect/PersistentDescriptorFactory.java b/cayenne-server/src/main/java/org/apache/cayenne/reflect/PersistentDescriptorFactory.java
index dbc05c7..b2e2e87 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/reflect/PersistentDescriptorFactory.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/reflect/PersistentDescriptorFactory.java
@@ -19,6 +19,8 @@
package org.apache.cayenne.reflect;
import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
import java.util.Map;
import org.apache.cayenne.CayenneRuntimeException;
@@ -27,11 +29,13 @@ import org.apache.cayenne.exp.Expression;
import org.apache.cayenne.exp.TraversalHelper;
import org.apache.cayenne.map.DbAttribute;
import org.apache.cayenne.map.DbEntity;
+import org.apache.cayenne.map.DbRelationship;
import org.apache.cayenne.map.EmbeddedAttribute;
import org.apache.cayenne.map.EntityInheritanceTree;
import org.apache.cayenne.map.ObjAttribute;
import org.apache.cayenne.map.ObjEntity;
import org.apache.cayenne.map.ObjRelationship;
+import org.apache.cayenne.util.CayenneMapEntry;
/**
* A convenience superclass for {@link ClassDescriptorFactory} implementors.
@@ -104,8 +108,7 @@ public abstract class PersistentDescriptorFactory implements ClassDescriptorFact
}
}
- EntityInheritanceTree inheritanceTree = descriptorMap.getResolver().getInheritanceTree(
- descriptor.getEntity().getName());
+ EntityInheritanceTree inheritanceTree = descriptorMap.getResolver().getInheritanceTree(descriptor.getEntity().getName());
descriptor.setEntityInheritanceTree(inheritanceTree);
indexSubclassDescriptors(descriptor, inheritanceTree);
indexQualifiers(descriptor, inheritanceTree);
@@ -114,6 +117,7 @@ public abstract class PersistentDescriptorFactory implements ClassDescriptorFact
indexRootDbEntities(descriptor, inheritanceTree);
indexSuperclassProperties(descriptor);
+ indexAdditionalDbEntities(descriptor);
descriptor.sortProperties();
@@ -290,6 +294,57 @@ public abstract class PersistentDescriptorFactory implements ClassDescriptorFact
}
}
+ protected void indexAdditionalDbEntities(final PersistentDescriptor descriptor) {
+ descriptor.visitProperties(new PropertyVisitor() {
+ @Override
+ public boolean visitAttribute(AttributeProperty property) {
+ if(!property.getAttribute().isFlattened()) {
+ return true;
+ }
+
+ Iterator<CayenneMapEntry> it = property.getAttribute().getDbPathIterator();
+ StringBuilder sb = new StringBuilder();
+ while(it.hasNext()) {
+ CayenneMapEntry next = it.next();
+ if(next instanceof DbRelationship) {
+ DbRelationship rel = (DbRelationship)next;
+ if(sb.length() > 0) {
+ sb.append('.');
+ }
+ sb.append(rel.getName());
+ descriptor.addAdditionalDbEntity(sb.toString(), rel.getTargetEntity());
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public boolean visitToOne(ToOneProperty property) {
+ if(!property.getRelationship().isFlattened()) {
+ return true;
+ }
+
+ List<DbRelationship> dbRelationships = property.getRelationship().getDbRelationships();
+ StringBuilder sb = new StringBuilder();
+ int count = dbRelationships.size();
+ for(int i=0; i<count-1; i++) {
+ DbRelationship rel = dbRelationships.get(i);
+ if(sb.length() > 0) {
+ sb.append('.');
+ }
+ sb.append(rel.getName());
+ descriptor.addAdditionalDbEntity(sb.toString(), rel.getTargetEntity());
+ }
+ return true;
+ }
+
+ @Override
+ public boolean visitToMany(ToManyProperty property) {
+ return true;
+ }
+ });
+ }
+
/**
* Creates an accessor for the property.
*/
http://git-wip-us.apache.org/repos/asf/cayenne/blob/f0b2ed00/cayenne-server/src/test/java/org/apache/cayenne/CDOOneToOneFKIT.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/CDOOneToOneFKIT.java b/cayenne-server/src/test/java/org/apache/cayenne/CDOOneToOneFKIT.java
index ea21563..8c54f69 100644
--- a/cayenne-server/src/test/java/org/apache/cayenne/CDOOneToOneFKIT.java
+++ b/cayenne-server/src/test/java/org/apache/cayenne/CDOOneToOneFKIT.java
@@ -97,11 +97,22 @@ public class CDOOneToOneFKIT extends ServerCase {
false,
ObjectIdQuery.CACHE_REFRESH);
ToOneFK2 src2 = (ToOneFK2) Cayenne.objectForQuery(context1, refetch);
+ assertNull(src2.getToOneToFK());
assertEquals(src.getObjectId(), src2.getObjectId());
// *** TESTING THIS ***
src2.setToOneToFK(null);
assertNull(src2.getToOneToFK());
+
+ context.commitChanges();
+
+ refetch = new ObjectIdQuery(
+ src.getObjectId(),
+ false,
+ ObjectIdQuery.CACHE_REFRESH);
+ src2 = (ToOneFK2) Cayenne.objectForQuery(context1, refetch);
+ assertNull(src2.getToOneToFK());
+ assertEquals(src.getObjectId(), src2.getObjectId());
}
@Test
http://git-wip-us.apache.org/repos/asf/cayenne/blob/f0b2ed00/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 fb01ff2..f5da5bb 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
@@ -30,7 +30,6 @@ import org.apache.cayenne.testdo.inheritance_vertical.*;
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;
@@ -234,7 +233,6 @@ public class VerticalInheritanceIT extends ServerCase {
/**
* @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");
@@ -614,7 +612,7 @@ public class VerticalInheritanceIT extends ServerCase {
context.commitChanges();
}
- @Test(expected = ValidationException.class) // other2 is missing now
+ @Test//(expected = ValidationException.class) // other2 is not mandatory for now
public void testInsertWithAttributeAndRelationship() {
IvOther other = context.newObject(IvOther.class);
other.setName("other");
@@ -679,51 +677,6 @@ public class VerticalInheritanceIT extends ServerCase {
/**
* @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");
http://git-wip-us.apache.org/repos/asf/cayenne/blob/f0b2ed00/cayenne-server/src/test/java/org/apache/cayenne/access/VerticalInheritanceMultipleAttributes.java
----------------------------------------------------------------------
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/access/VerticalInheritanceMultipleAttributes.java b/cayenne-server/src/test/java/org/apache/cayenne/access/VerticalInheritanceMultipleAttributes.java
new file mode 100644
index 0000000..8969f67
--- /dev/null
+++ b/cayenne-server/src/test/java/org/apache/cayenne/access/VerticalInheritanceMultipleAttributes.java
@@ -0,0 +1,310 @@
+/*****************************************************************
+ * 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.access;
+
+import java.sql.SQLException;
+import java.sql.Types;
+import java.util.List;
+
+import org.apache.cayenne.ObjectContext;
+import org.apache.cayenne.configuration.server.ServerRuntime;
+import org.apache.cayenne.di.Inject;
+import org.apache.cayenne.query.ObjectSelect;
+import org.apache.cayenne.test.jdbc.DBHelper;
+import org.apache.cayenne.test.jdbc.TableHelper;
+import org.apache.cayenne.testdo.inheritance_vertical.IvImpl;
+import org.apache.cayenne.testdo.inheritance_vertical.IvOther;
+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.junit.Before;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * @since 4.1
+ */
+@UseServerRuntime(CayenneProjects.INHERITANCE_VERTICAL_PROJECT)
+public class VerticalInheritanceMultipleAttributes extends ServerCase {
+
+ @Inject
+ protected ObjectContext context;
+
+ @Inject
+ protected DBHelper dbHelper;
+
+ @Inject
+ protected ServerRuntime runtime;
+
+ TableHelper ivOtherTable, ivBaseTable, ivImplTable;
+
+ @Before
+ public void setupTableHelpers() throws Exception {
+ ivOtherTable = new TableHelper(dbHelper, "IV_OTHER");
+ ivOtherTable.setColumns("ID", "NAME")
+ .setColumnTypes(Types.INTEGER, Types.VARCHAR);
+
+ ivBaseTable = new TableHelper(dbHelper, "IV_BASE");
+ ivBaseTable.setColumns("ID", "NAME", "TYPE")
+ .setColumnTypes(Types.INTEGER, Types.VARCHAR, Types.CHAR);
+
+ 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);
+
+ ivImplTable.deleteAll();
+ ivBaseTable.deleteAll();
+ ivOtherTable.deleteAll();
+ }
+
+ /**
+ * @link https://issues.apache.org/jira/browse/CAY-2282
+ */
+ @Test
+ public void testUpdateTwoObjects() throws SQLException {
+ // 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);
+
+ List<IvImpl> implResult = ObjectSelect.query(IvImpl.class).select(context);
+ assertEquals(2, implResult.size());
+ for(IvImpl record : implResult) {
+ record.setName(record.getName() + "-Change");
+ record.setAttr1(record.getAttr1() + "-Change");
+ record.setAttr2(record.getAttr2() + "-Change");
+ record.setOther1(other2);
+ record.setOther2(other1);
+ }
+
+ context.commitChanges();
+
+ // Check result via clean context
+ ObjectContext cleanContext = runtime.newContext();
+ implResult = ObjectSelect.query(IvImpl.class).select(cleanContext);
+ assertEquals(2, implResult.size());
+ for(IvImpl record : implResult) {
+ assertTrue(record.getName().endsWith("-Change"));
+ assertTrue(record.getAttr1().endsWith("-Change"));
+ assertTrue(record.getAttr2().endsWith("-Change"));
+ assertEquals(other2.getObjectId(), record.getOther1().getObjectId());
+ assertEquals(other1.getObjectId(), record.getOther2().getObjectId());
+ }
+ }
+
+ @Test
+ public void testCreateObjectsWithData() throws SQLException {
+ ivOtherTable.insert(1, "other1");
+ ivOtherTable.insert(2, "other2");
+
+ 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);
+
+ IvImpl impl1 = context.newObject(IvImpl.class);
+ impl1.setName("name");
+ impl1.setAttr1("attr1");
+ impl1.setAttr2("attr2");
+ impl1.setOther1(other1);
+ impl1.setOther2(other2);
+
+ IvImpl impl2 = context.newObject(IvImpl.class);
+ impl2.setName("name");
+ impl2.setAttr1("attr1");
+ impl2.setAttr2("attr2");
+ impl2.setOther1(other1);
+ impl2.setOther2(other2);
+
+ context.commitChanges();
+
+ // Check result via clean context
+ ObjectContext cleanContext = runtime.newContext();
+ List<IvImpl> implResult = ObjectSelect.query(IvImpl.class).select(cleanContext);
+ assertEquals(2, implResult.size());
+ for(IvImpl record : implResult) {
+ assertEquals("name", record.getName());
+ assertEquals("attr1", record.getAttr1());
+ assertEquals("attr2", record.getAttr2());
+ assertEquals(other1.getObjectId(), record.getOther1().getObjectId());
+ assertEquals(other2.getObjectId(), record.getOther2().getObjectId());
+ }
+ }
+
+ @Test
+ public void testCreateEmptyObjects() throws SQLException {
+ IvImpl impl1 = context.newObject(IvImpl.class);
+ impl1.setName("name");
+
+ IvImpl impl2 = context.newObject(IvImpl.class);
+ impl2.setName("name");
+
+ context.commitChanges();
+
+ ObjectContext cleanContext = runtime.newContext();
+ List<IvImpl> implResult = ObjectSelect.query(IvImpl.class).select(cleanContext);
+ assertEquals(2, implResult.size());
+ for(IvImpl record : implResult) {
+ assertEquals("name", record.getName());
+ assertNull(record.getAttr1());
+ assertNull(record.getAttr2());
+ assertNull(record.getOther1());
+ assertNull(record.getOther2());
+ }
+ }
+
+ @Test
+ public void testCreateEmptyObjectsWithUpdate() throws SQLException {
+ ivOtherTable.insert(1, "other1");
+ ivOtherTable.insert(2, "other2");
+
+ 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);
+
+ IvImpl impl1 = context.newObject(IvImpl.class);
+ impl1.setName("name");
+
+ IvImpl impl2 = context.newObject(IvImpl.class);
+ impl2.setName("name");
+
+ context.commitChanges();
+
+ ObjectContext cleanContext = runtime.newContext();
+ List<IvImpl> implResult = ObjectSelect.query(IvImpl.class).select(cleanContext);
+ assertEquals(2, implResult.size());
+ for(IvImpl record : implResult) {
+ assertEquals("name", record.getName());
+ assertNull(record.getAttr1());
+ assertNull(record.getAttr2());
+ assertNull(record.getOther1());
+ assertNull(record.getOther2());
+ }
+
+ impl1.setAttr1("attr1");
+ impl1.setAttr2("attr2");
+ impl1.setOther1(other1);
+ impl1.setOther2(other2);
+
+ impl2.setAttr1("attr1");
+ impl2.setAttr2("attr2");
+ impl2.setOther1(other1);
+ impl2.setOther2(other2);
+
+ context.commitChanges();
+
+ cleanContext = runtime.newContext();
+ implResult = ObjectSelect.query(IvImpl.class).select(cleanContext);
+ assertEquals(2, implResult.size());
+ for(IvImpl record : implResult) {
+ assertEquals("name", record.getName());
+ assertEquals("attr1", record.getAttr1());
+ assertEquals("attr2", record.getAttr2());
+ assertEquals(other1.getObjectId(), record.getOther1().getObjectId());
+ assertEquals(other2.getObjectId(), record.getOther2().getObjectId());
+ }
+ }
+
+ @Test
+ public void testPartialCreateObjectsWithUpdate() throws SQLException {
+ ivOtherTable.insert(1, "other1");
+ ivOtherTable.insert(2, "other2");
+
+ 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);
+
+ IvImpl impl1 = context.newObject(IvImpl.class);
+ impl1.setName("name");
+ impl1.setAttr1("attr1");
+
+ IvImpl impl2 = context.newObject(IvImpl.class);
+ impl2.setName("name");
+ impl2.setAttr1("attr1");
+
+ context.commitChanges();
+
+ ObjectContext cleanContext = runtime.newContext();
+ List<IvImpl> implResult = ObjectSelect.query(IvImpl.class).select(cleanContext);
+ assertEquals(2, implResult.size());
+ for(IvImpl record : implResult) {
+ assertEquals("name", record.getName());
+ assertEquals("attr1", record.getAttr1());
+ assertNull(record.getAttr2());
+ assertNull(record.getOther1());
+ assertNull(record.getOther2());
+ }
+
+ impl1.setAttr1("attr1");
+ impl1.setAttr2("attr2");
+ impl1.setOther1(other1);
+ impl1.setOther2(other2);
+
+ impl2.setAttr1("attr1");
+ impl2.setAttr2("attr2");
+ impl2.setOther1(other1);
+ impl2.setOther2(other2);
+
+ context.commitChanges();
+
+ cleanContext = runtime.newContext();
+ implResult = ObjectSelect.query(IvImpl.class).select(cleanContext);
+ assertEquals(2, implResult.size());
+ for(IvImpl record : implResult) {
+ assertEquals("name", record.getName());
+ assertEquals("attr1", record.getAttr1());
+ assertEquals("attr2", record.getAttr2());
+ assertEquals(other1.getObjectId(), record.getOther1().getObjectId());
+ assertEquals(other2.getObjectId(), record.getOther2().getObjectId());
+ }
+ }
+
+ @Test
+ public void testDeleteObjects() throws SQLException {
+ // 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);
+
+ List<IvImpl> implResult = ObjectSelect.query(IvImpl.class).select(context);
+ assertEquals(2, implResult.size());
+
+ for(IvImpl iv : implResult) {
+ context.deleteObject(iv);
+ }
+
+ context.commitChanges();
+
+ assertEquals(0L, ObjectSelect.query(IvImpl.class).selectCount(context));
+ }
+}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/f0b2ed00/cayenne-server/src/test/resources/cayenne-inheritance-vertical.xml
----------------------------------------------------------------------
diff --git a/cayenne-server/src/test/resources/cayenne-inheritance-vertical.xml b/cayenne-server/src/test/resources/cayenne-inheritance-vertical.xml
index 9d8da09..3118079 100644
--- a/cayenne-server/src/test/resources/cayenne-inheritance-vertical.xml
+++ b/cayenne-server/src/test/resources/cayenne-inheritance-vertical.xml
@@ -1,5 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<domain xmlns="http://cayenne.apache.org/schema/10/domain"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://cayenne.apache.org/schema/10/domain http://cayenne.apache.org/schema/10/domain.xsd"
project-version="10">
<map name="inheritance-vertical"/>
</domain>
http://git-wip-us.apache.org/repos/asf/cayenne/blob/f0b2ed00/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 a437439..4af9df1 100644
--- a/cayenne-server/src/test/resources/inheritance-vertical.map.xml
+++ b/cayenne-server/src/test/resources/inheritance-vertical.map.xml
@@ -44,11 +44,11 @@
<db-attribute name="NAME" type="VARCHAR" length="100"/>
</db-entity>
<db-entity name="IV_IMPL">
- <db-attribute name="ATTR1" type="VARCHAR" isMandatory="true" length="100"/>
- <db-attribute name="ATTR2" type="VARCHAR" isMandatory="true" length="100"/>
+ <db-attribute name="ATTR1" type="VARCHAR" length="100"/>
+ <db-attribute name="ATTR2" type="VARCHAR" length="100"/>
<db-attribute name="ID" type="INTEGER" isPrimaryKey="true" isMandatory="true"/>
- <db-attribute name="OTHER1_ID" type="INTEGER" isMandatory="true"/>
- <db-attribute name="OTHER2_ID" type="INTEGER" isMandatory="true"/>
+ <db-attribute name="OTHER1_ID" type="INTEGER"/>
+ <db-attribute name="OTHER2_ID" type="INTEGER"/>
</db-entity>
<db-entity name="IV_IMPL_WITH_LOCK">
<db-attribute name="ATTR1" type="VARCHAR" isMandatory="true" length="100"/>