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>