Repository: cayenne
Updated Branches:
  refs/heads/master 42e7b897b -> 377a1ef27


CAY-2122 Vertical Inheritance: Cannot Insert Record For Implementing Class with 
Attribute And Relationship

patch by Matt Watson


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

Branch: refs/heads/master
Commit: 5a76895d01dda45568f564b0f254b637d7f5b239
Parents: 42e7b89
Author: Andrus Adamchik <and...@objectstyle.com>
Authored: Sat Oct 8 16:43:34 2016 +0300
Committer: Andrus Adamchik <and...@objectstyle.com>
Committed: Sat Oct 8 17:10:16 2016 +0300

----------------------------------------------------------------------
 .../access/DataDomainFlattenedBucket.java       | 89 +++++++++++++++++---
 .../cayenne/access/DataDomainFlushAction.java   |  2 +-
 .../cayenne/access/VerticalInheritanceIT.java   | 13 +++
 .../testdo/inheritance_vertical/IvBase.java     |  9 ++
 .../testdo/inheritance_vertical/IvImpl.java     |  9 ++
 .../testdo/inheritance_vertical/IvOther.java    |  9 ++
 .../inheritance_vertical/auto/_IvBase.java      | 35 ++++++++
 .../inheritance_vertical/auto/_IvImpl.java      | 38 +++++++++
 .../inheritance_vertical/auto/_IvOther.java     | 42 +++++++++
 .../test/resources/inheritance-vertical.map.xml | 39 +++++++++
 10 files changed, 270 insertions(+), 15 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cayenne/blob/5a76895d/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 abfd0e8..6ac7db5 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
@@ -27,8 +27,10 @@ import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 
+import org.apache.cayenne.ObjectId;
 import org.apache.cayenne.map.DbAttribute;
 import org.apache.cayenne.map.DbEntity;
+import org.apache.cayenne.query.BatchQueryRow;
 import org.apache.cayenne.query.DeleteBatchQuery;
 import org.apache.cayenne.query.InsertBatchQuery;
 import org.apache.cayenne.query.Query;
@@ -41,31 +43,28 @@ import org.apache.cayenne.query.Query;
 class DataDomainFlattenedBucket {
 
     final DataDomainFlushAction parent;
-    final Map<DbEntity, InsertBatchQuery> flattenedInsertQueries;
+    final Map<DbEntity, List<FlattenedArcKey>> insertArcKeys;
     final Map<DbEntity, DeleteBatchQuery> flattenedDeleteQueries;
 
     DataDomainFlattenedBucket(DataDomainFlushAction parent) {
         this.parent = parent;
-        this.flattenedInsertQueries = new HashMap<DbEntity, 
InsertBatchQuery>();
+        this.insertArcKeys = new HashMap<DbEntity, List<FlattenedArcKey>>();
         this.flattenedDeleteQueries = new HashMap<DbEntity, 
DeleteBatchQuery>();
     }
 
     boolean isEmpty() {
-        return flattenedInsertQueries.isEmpty() && 
flattenedDeleteQueries.isEmpty();
+        return insertArcKeys.isEmpty() && flattenedDeleteQueries.isEmpty();
     }
 
-    void addFlattenedInsert(DbEntity flattenedEntity, FlattenedArcKey 
flattenedArcKey) {
+    void addInsertArcKey(DbEntity flattenedEntity, FlattenedArcKey 
flattenedArcKey) {
+        List<FlattenedArcKey> arcKeys = insertArcKeys.get(flattenedEntity);
 
-        InsertBatchQuery relationInsertQuery = 
flattenedInsertQueries.get(flattenedEntity);
-
-        if (relationInsertQuery == null) {
-            relationInsertQuery = new InsertBatchQuery(flattenedEntity, 50);
-            flattenedInsertQueries.put(flattenedEntity, relationInsertQuery);
+        if (arcKeys == null) {
+            arcKeys = new ArrayList<FlattenedArcKey>();
+            insertArcKeys.put(flattenedEntity, arcKeys);
         }
 
-        DataNode node = 
parent.getDomain().lookupDataNode(flattenedEntity.getDataMap());
-        Map flattenedSnapshot = 
flattenedArcKey.buildJoinSnapshotForInsert(node);
-        relationInsertQuery.add(flattenedSnapshot);
+        arcKeys.add(flattenedArcKey);
     }
 
     void addFlattenedDelete(DbEntity flattenedEntity, FlattenedArcKey 
flattenedDeleteInfo) {
@@ -90,9 +89,50 @@ class DataDomainFlattenedBucket {
         }
     }
 
+    /**
+     * responsible for adding the flattened Insert Queries. Its possible an 
insert query for the same DbEntity/ObjectId
+     * already has been added from the insert bucket queries if that Object 
also has an attribute. So we want to merge
+     * the data for each insert into a single insert.
+     *
+     * @param queries
+     */
     void appendInserts(Collection<Query> queries) {
-        if (!flattenedInsertQueries.isEmpty()) {
-            queries.addAll(flattenedInsertQueries.values());
+        for (Map.Entry<DbEntity, List<FlattenedArcKey>> entry : 
insertArcKeys.entrySet()) {
+            DbEntity dbEntity = entry.getKey();
+            List<FlattenedArcKey> flattenedArcKeys = entry.getValue();
+
+            DataNode node = 
parent.getDomain().lookupDataNode(dbEntity.getDataMap());
+
+            InsertBatchQuery existingQuery = findInsertBatchQuery(queries, 
dbEntity);
+            InsertBatchQuery newQuery = new InsertBatchQuery(dbEntity, 50);
+
+            for (FlattenedArcKey flattenedArcKey : flattenedArcKeys) {
+                Map<String, Object> snapshot = 
flattenedArcKey.buildJoinSnapshotForInsert(node);
+
+                if (existingQuery != null) {
+                    BatchQueryRow existingRow = 
findRowForObjectId(existingQuery.getRows(), flattenedArcKey.id1.getSourceId());
+                    // todo: do we need to worry about flattenedArcKey.id2 ?
+
+                    if (existingRow != null) {
+                        List<DbAttribute> existingQueryDbAttributes = 
existingQuery.getDbAttributes();
+
+                        for(int i=0; i < existingQueryDbAttributes.size(); 
i++) {
+                            Object value = existingRow.getValue(i);
+                            if (value != null) {
+                                
snapshot.put(existingQueryDbAttributes.get(i).getName(), value);
+                            }
+                        }
+                    }
+                }
+
+                newQuery.add(snapshot);
+            }
+
+            if (existingQuery != null) {
+                queries.remove(existingQuery);
+            }
+
+            queries.add(newQuery);
         }
     }
 
@@ -101,4 +141,25 @@ class DataDomainFlattenedBucket {
             queries.addAll(flattenedDeleteQueries.values());
         }
     }
+
+    private InsertBatchQuery findInsertBatchQuery(Collection<Query> queries, 
DbEntity dbEntity) {
+        for(Query query : queries) {
+            if (query instanceof InsertBatchQuery) {
+                InsertBatchQuery insertBatchQuery = (InsertBatchQuery)query;
+                if (insertBatchQuery.getDbEntity().equals(dbEntity)) {
+                    return insertBatchQuery;
+                }
+            }
+        }
+        return null;
+    }
+
+    private BatchQueryRow findRowForObjectId(List<BatchQueryRow> rows, 
ObjectId objectId) {
+        for (BatchQueryRow row : rows) {
+            if (row.getObjectId().equals(objectId)) {
+                return row;
+            }
+        }
+        return null;
+    }
 }

http://git-wip-us.apache.org/repos/asf/cayenne/blob/5a76895d/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 75198b2..cebfe67 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
@@ -111,7 +111,7 @@ class DataDomainFlushAction {
     }
 
     void addFlattenedInsert(DbEntity flattenedEntity, FlattenedArcKey 
flattenedInsertInfo) {
-        flattenedBucket.addFlattenedInsert(flattenedEntity, 
flattenedInsertInfo);
+        flattenedBucket.addInsertArcKey(flattenedEntity, flattenedInsertInfo);
     }
 
     void addFlattenedDelete(DbEntity flattenedEntity, FlattenedArcKey 
flattenedDeleteInfo) {

http://git-wip-us.apache.org/repos/asf/cayenne/blob/5a76895d/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 71f0bf9..c5109a4 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
@@ -544,4 +544,17 @@ public class VerticalInheritanceIT extends ServerCase {
                context.commitChanges();
        }
 
+       @Test
+       public void testInsertWithAttributeAndRelationship() {
+               IvOther other = context.newObject(IvOther.class);
+               other.setName("other");
+
+               IvImpl impl = context.newObject(IvImpl.class);
+               impl.setName("Impl 1");
+               impl.setAttr1("attr1");
+               impl.setOther(other);
+
+               context.commitChanges();
+       }
+
 }

http://git-wip-us.apache.org/repos/asf/cayenne/blob/5a76895d/cayenne-server/src/test/java/org/apache/cayenne/testdo/inheritance_vertical/IvBase.java
----------------------------------------------------------------------
diff --git 
a/cayenne-server/src/test/java/org/apache/cayenne/testdo/inheritance_vertical/IvBase.java
 
b/cayenne-server/src/test/java/org/apache/cayenne/testdo/inheritance_vertical/IvBase.java
new file mode 100644
index 0000000..b5402e3
--- /dev/null
+++ 
b/cayenne-server/src/test/java/org/apache/cayenne/testdo/inheritance_vertical/IvBase.java
@@ -0,0 +1,9 @@
+package org.apache.cayenne.testdo.inheritance_vertical;
+
+import org.apache.cayenne.testdo.inheritance_vertical.auto._IvBase;
+
+public abstract class IvBase extends _IvBase {
+
+    private static final long serialVersionUID = 1L; 
+
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/5a76895d/cayenne-server/src/test/java/org/apache/cayenne/testdo/inheritance_vertical/IvImpl.java
----------------------------------------------------------------------
diff --git 
a/cayenne-server/src/test/java/org/apache/cayenne/testdo/inheritance_vertical/IvImpl.java
 
b/cayenne-server/src/test/java/org/apache/cayenne/testdo/inheritance_vertical/IvImpl.java
new file mode 100644
index 0000000..a125df5
--- /dev/null
+++ 
b/cayenne-server/src/test/java/org/apache/cayenne/testdo/inheritance_vertical/IvImpl.java
@@ -0,0 +1,9 @@
+package org.apache.cayenne.testdo.inheritance_vertical;
+
+import org.apache.cayenne.testdo.inheritance_vertical.auto._IvImpl;
+
+public class IvImpl extends _IvImpl {
+
+    private static final long serialVersionUID = 1L; 
+
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/5a76895d/cayenne-server/src/test/java/org/apache/cayenne/testdo/inheritance_vertical/IvOther.java
----------------------------------------------------------------------
diff --git 
a/cayenne-server/src/test/java/org/apache/cayenne/testdo/inheritance_vertical/IvOther.java
 
b/cayenne-server/src/test/java/org/apache/cayenne/testdo/inheritance_vertical/IvOther.java
new file mode 100644
index 0000000..bf106ac
--- /dev/null
+++ 
b/cayenne-server/src/test/java/org/apache/cayenne/testdo/inheritance_vertical/IvOther.java
@@ -0,0 +1,9 @@
+package org.apache.cayenne.testdo.inheritance_vertical;
+
+import org.apache.cayenne.testdo.inheritance_vertical.auto._IvOther;
+
+public class IvOther extends _IvOther {
+
+    private static final long serialVersionUID = 1L; 
+
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/5a76895d/cayenne-server/src/test/java/org/apache/cayenne/testdo/inheritance_vertical/auto/_IvBase.java
----------------------------------------------------------------------
diff --git 
a/cayenne-server/src/test/java/org/apache/cayenne/testdo/inheritance_vertical/auto/_IvBase.java
 
b/cayenne-server/src/test/java/org/apache/cayenne/testdo/inheritance_vertical/auto/_IvBase.java
new file mode 100644
index 0000000..a7325ff
--- /dev/null
+++ 
b/cayenne-server/src/test/java/org/apache/cayenne/testdo/inheritance_vertical/auto/_IvBase.java
@@ -0,0 +1,35 @@
+package org.apache.cayenne.testdo.inheritance_vertical.auto;
+
+import org.apache.cayenne.CayenneDataObject;
+import org.apache.cayenne.exp.Property;
+
+/**
+ * Class _IvAbstract 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 _IvBase extends CayenneDataObject {
+
+    private static final long serialVersionUID = 1L; 
+
+    public static final String ID_PK_COLUMN = "ID";
+
+    public static final Property<String> TYPE = new Property<String>("type");
+    public static final Property<String> NAME = new Property<String>("name");
+
+    public void setType(String type) {
+        writeProperty("type", type);
+    }
+    public String getType() {
+        return (String)readProperty("type");
+    }
+
+    public void setName(String name) {
+        writeProperty("name", name);
+    }
+    public String getName() {
+        return (String)readProperty("name");
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/5a76895d/cayenne-server/src/test/java/org/apache/cayenne/testdo/inheritance_vertical/auto/_IvImpl.java
----------------------------------------------------------------------
diff --git 
a/cayenne-server/src/test/java/org/apache/cayenne/testdo/inheritance_vertical/auto/_IvImpl.java
 
b/cayenne-server/src/test/java/org/apache/cayenne/testdo/inheritance_vertical/auto/_IvImpl.java
new file mode 100644
index 0000000..4e05db3
--- /dev/null
+++ 
b/cayenne-server/src/test/java/org/apache/cayenne/testdo/inheritance_vertical/auto/_IvImpl.java
@@ -0,0 +1,38 @@
+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 _IvConcrete 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 _IvImpl extends IvBase {
+
+    private static final long serialVersionUID = 1L; 
+
+    public static final String ID_PK_COLUMN = "ID";
+
+    public static final Property<String> ATTR1 = new Property<String>("attr1");
+    public static final Property<IvOther> OTHER = new 
Property<IvOther>("other");
+
+    public void setAttr1(String attr1) {
+        writeProperty("attr1", attr1);
+    }
+    public String getAttr1() {
+        return (String)readProperty("attr1");
+    }
+
+    public void setOther(IvOther other) {
+        setToOneTarget("other", other, true);
+    }
+
+    public IvOther getOther() {
+        return (IvOther)readProperty("other");
+    }
+
+
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/5a76895d/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
new file mode 100644
index 0000000..52be2ea
--- /dev/null
+++ 
b/cayenne-server/src/test/java/org/apache/cayenne/testdo/inheritance_vertical/auto/_IvOther.java
@@ -0,0 +1,42 @@
+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 java.util.List;
+
+/**
+ * Class _IvConcrete 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 _IvOther extends CayenneDataObject {
+
+    private static final long serialVersionUID = 1L; 
+
+    public static final String ID_PK_COLUMN = "ID";
+
+    public static final Property<String> NAME = new Property<String>("name");
+    public static final Property<List<IvImpl>> IMPLS = new 
Property<List<IvImpl>>("impls");
+
+    public void setName(String name) {
+        writeProperty("name", name);
+    }
+    public String getName() {
+        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");
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/5a76895d/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 1a48639..ad8fba4 100644
--- a/cayenne-server/src/test/resources/inheritance-vertical.map.xml
+++ b/cayenne-server/src/test/resources/inheritance-vertical.map.xml
@@ -51,6 +51,20 @@
                <db-attribute name="TYPE" type="CHAR" isMandatory="true" 
length="1"/>
                <db-attribute name="ID" type="INTEGER" isPrimaryKey="true" 
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"/>
+       </db-entity>
+       <db-entity name="IV_BASE">
+               <db-attribute name="TYPE" type="CHAR" isMandatory="true" 
length="1"/>
+               <db-attribute name="ID" type="INTEGER" isPrimaryKey="true" 
isMandatory="true"/>
+               <db-attribute name="NAME" type="VARCHAR" length="100" 
isMandatory="true"/>
+       </db-entity>
+       <db-entity name="IV_IMPL">
+               <db-attribute name="ID" type="INTEGER" isPrimaryKey="true" 
isMandatory="true"/>
+               <db-attribute name="ATTR1" type="VARCHAR" length="100" 
isMandatory="true"/>
+               <db-attribute name="OTHER_ID" type="INTEGER" 
isMandatory="true"/>
+       </db-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"/>
@@ -96,6 +110,17 @@
                <obj-attribute name="sub2Attr" type="java.lang.String" 
db-attribute-path="sub2.SUB2_ATTR"/>
                <obj-attribute name="sub2Name" type="java.lang.String" 
db-attribute-path="sub2.SUB2_NAME"/>
        </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>
+       <obj-entity name="IvBase" abstract="true" 
className="org.apache.cayenne.testdo.inheritance_vertical.IvBase" 
dbEntityName="IV_BASE">
+               <obj-attribute name="type" type="java.lang.String" 
db-attribute-path="TYPE"/>
+               <obj-attribute name="name" type="java.lang.String" 
db-attribute-path="NAME"/>
+       </obj-entity>
+       <obj-entity name="IvImpl" superEntityName="IvBase" 
className="org.apache.cayenne.testdo.inheritance_vertical.IvImpl">
+               <qualifier><![CDATA[type = "I"]]></qualifier>
+               <obj-attribute name="attr1" type="java.lang.String" 
db-attribute-path="impl.ATTR1"/>
+       </obj-entity>
        <db-relationship name="abstract" source="IV_CONCRETE" 
target="IV_ABSTRACT" toMany="false">
                <db-attribute-pair source="ID" target="ID"/>
        </db-relationship>
@@ -144,7 +169,21 @@
        <db-relationship name="master" source="IV_SUB2" target="IV_ROOT" 
toMany="false">
                <db-attribute-pair source="ID" target="ID"/>
        </db-relationship>
+       <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="base" source="IV_IMPL" target="IV_BASE" 
toMany="false">
+               <db-attribute-pair source="ID" target="ID"/>
+       </db-relationship>
+       <db-relationship name="other" source="IV_IMPL" target="IV_OTHER" 
toMany="false">
+               <db-attribute-pair source="OTHER_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>
        <obj-relationship name="x" source="Iv2Sub1" target="Iv2X" 
deleteRule="Nullify" db-relationship-path="sub1.x"/>
        <obj-relationship name="parent" source="IvConcrete" target="IvConcrete" 
deleteRule="Nullify" db-relationship-path="parent"/>
        <obj-relationship name="children" source="IvConcrete" 
target="IvConcrete" deleteRule="Deny" db-relationship-path="children"/>
+       <obj-relationship name="other" source="IvImpl" target="IvOther" 
deleteRule="Nullify" db-relationship-path="impl.other"/>
+       <obj-relationship name="impls" source="IvOther" target="IvImpl" 
deleteRule="Deny" db-relationship-path="impls.base"/>
 </data-map>

Reply via email to