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>