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 2019/05/17 07:44:08 UTC

[cayenne] branch master updated: Filter out phantom toMany relationship changes

This is an automated email from the ASF dual-hosted git repository.

ntimofeev pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/cayenne.git


The following commit(s) were added to refs/heads/master by this push:
     new ec35c17  Filter out phantom toMany relationship changes
ec35c17 is described below

commit ec35c178bbcdf0662823a37c0013fe85caa18e4c
Author: Nikita Timofeev <st...@gmail.com>
AuthorDate: Fri May 17 10:43:48 2019 +0300

    Filter out phantom toMany relationship changes
---
 .../cayenne/access/flush/DbRowOpFactory.java       |  4 ++--
 .../access/flush/DefaultDataDomainFlushAction.java | 23 +++++++++++++++++++++-
 .../cayenne/access/flush/ReplacementIdVisitor.java |  2 +-
 .../cayenne/access/flush/operation/Values.java     | 12 +++++++++++
 .../cayenne/CayenneDataObjectRelationshipsIT.java  | 18 +++++++++++++++++
 5 files changed, 55 insertions(+), 4 deletions(-)

diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/flush/DbRowOpFactory.java b/cayenne-server/src/main/java/org/apache/cayenne/access/flush/DbRowOpFactory.java
index 47f4de1..3e967e6 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/access/flush/DbRowOpFactory.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/access/flush/DbRowOpFactory.java
@@ -66,7 +66,7 @@ class DbRowOpFactory {
         this.rootRowOpProcessor = new RootRowOpProcessor(this);
     }
 
-    private void udpateDiff(ObjectDiff diff) {
+    private void updateDiff(ObjectDiff diff) {
         ObjectId id = (ObjectId)diff.getNodeId();
         this.diff = diff;
         this.descriptor = resolver.getClassDescriptor(id.getEntityName());
@@ -75,7 +75,7 @@ class DbRowOpFactory {
     }
 
     Collection<? extends DbRowOp> createRows(ObjectDiff diff) {
-        udpateDiff(diff);
+        updateDiff(diff);
         DbEntity rootEntity = descriptor.getEntity().getDbEntity();
         DbRowOp row = getOrCreate(rootEntity, object.getObjectId(), DbRowOpType.forObject(object));
         rootRowOpProcessor.setDiff(diff);
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/flush/DefaultDataDomainFlushAction.java b/cayenne-server/src/main/java/org/apache/cayenne/access/flush/DefaultDataDomainFlushAction.java
index 25511db..14d63a8 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/access/flush/DefaultDataDomainFlushAction.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/access/flush/DefaultDataDomainFlushAction.java
@@ -41,6 +41,7 @@ import org.apache.cayenne.access.flush.operation.DbRowOpMerger;
 import org.apache.cayenne.access.flush.operation.DbRowOpSorter;
 import org.apache.cayenne.access.flush.operation.DbRowOp;
 import org.apache.cayenne.access.flush.operation.DbRowOpVisitor;
+import org.apache.cayenne.access.flush.operation.UpdateDbRowOp;
 import org.apache.cayenne.graph.CompoundDiff;
 import org.apache.cayenne.graph.GraphDiff;
 import org.apache.cayenne.log.JdbcEventLogger;
@@ -82,7 +83,8 @@ public class DefaultDataDomainFlushAction implements DataDomainFlushAction {
         List<DbRowOp> dbRowOps = createDbRowOps(objectStore, objectStoreGraphDiff);
         updateObjectIds(dbRowOps);
         List<DbRowOp> deduplicatedOps = mergeSameObjectIds(dbRowOps);
-        List<DbRowOp> sortedOps = sort(deduplicatedOps);
+        List<DbRowOp> filteredOps = filterOps(deduplicatedOps);
+        List<DbRowOp> sortedOps = sort(filteredOps);
         List<? extends Query> queries = createQueries(sortedOps);
         executeQueries(queries);
         createReplacementIds(objectStore, afterCommitDiff, sortedOps);
@@ -133,6 +135,12 @@ public class DefaultDataDomainFlushAction implements DataDomainFlushAction {
         return dbRowOps;
     }
 
+    protected List<DbRowOp> filterOps(List<DbRowOp> dbRowOps) {
+        // clear phantom update (this can be from insert/delete of arc with transient object)
+        dbRowOps.forEach(row -> row.accept(PhantomDbRowOpCleaner.INSTANCE));
+        return dbRowOps;
+    }
+
     /**
      * Sort all operations
      * @param dbRowOps collection of {@link DbRowOp}
@@ -207,4 +215,17 @@ public class DefaultDataDomainFlushAction implements DataDomainFlushAction {
         objectStore.postprocessAfterCommit(afterCommitDiff);
     }
 
+    protected static class PhantomDbRowOpCleaner implements DbRowOpVisitor<Void> {
+
+        protected static final DbRowOpVisitor<Void> INSTANCE = new PhantomDbRowOpCleaner();
+
+        @Override
+        public Void visitUpdate(UpdateDbRowOp dbRow) {
+            //
+            if(dbRow.getChangeId().isTemporary() && !dbRow.getChangeId().isReplacementIdAttached()) {
+                dbRow.getValues().clear();
+            }
+            return null;
+        }
+    }
 }
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/flush/ReplacementIdVisitor.java b/cayenne-server/src/main/java/org/apache/cayenne/access/flush/ReplacementIdVisitor.java
index 405eec6..a608832 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/access/flush/ReplacementIdVisitor.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/access/flush/ReplacementIdVisitor.java
@@ -80,7 +80,7 @@ class ReplacementIdVisitor implements DbRowOpVisitor<Void> {
     private void updateId(DbRowOp dbRow) {
         ObjectId id = dbRow.getChangeId();
         if (!id.isReplacementIdAttached()) {
-            if (id.isTemporary()) {
+            if (id == dbRow.getObject().getObjectId() && id.isTemporary()) {
                 throw new CayenneRuntimeException("PK for the object %s is not set during insert.", dbRow.getObject());
             }
             return;
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/flush/operation/Values.java b/cayenne-server/src/main/java/org/apache/cayenne/access/flush/operation/Values.java
index 248d891..4fa8568 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/access/flush/operation/Values.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/access/flush/operation/Values.java
@@ -139,6 +139,18 @@ public class Values {
         return snapshot == null || snapshot.isEmpty();
     }
 
+    public void clear() {
+        if(snapshot != null) {
+            snapshot.clear();
+        }
+        if(updatedAttributes != null) {
+            updatedAttributes.clear();
+        }
+        if(flattenedIds != null) {
+            flattenedIds.clear();
+        }
+    }
+
     public boolean isSameBatch(Values other) {
         if(snapshot == null) {
             return other.snapshot == null;
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/CayenneDataObjectRelationshipsIT.java b/cayenne-server/src/test/java/org/apache/cayenne/CayenneDataObjectRelationshipsIT.java
index 6c13706..72d020e 100644
--- a/cayenne-server/src/test/java/org/apache/cayenne/CayenneDataObjectRelationshipsIT.java
+++ b/cayenne-server/src/test/java/org/apache/cayenne/CayenneDataObjectRelationshipsIT.java
@@ -22,6 +22,7 @@ package org.apache.cayenne;
 import org.apache.cayenne.access.ToManyList;
 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.testmap.ArtGroup;
@@ -328,4 +329,21 @@ public class CayenneDataObjectRelationshipsIT extends ServerCase {
 
         assertFalse(list.isFault());
     }
+
+    @Test
+    public void testTransientInsertAndDeleteOfToManyRelationship() throws Exception {
+        createArtistWithPaintingDataSet();
+
+        Artist artist = ObjectSelect.query(Artist.class).selectOne(context);
+
+        // create and then immediately delete a to-many relationship value
+        Painting object2 = context.newObject(Painting.class);
+        artist.addToPaintingArray(object2);
+        artist.removeFromPaintingArray(object2);
+        context.deleteObject(object2);
+        assertEquals(1, artist.getPaintingArray().size());
+
+        artist.setArtistName("updated artist name"); // this will force the commit to actually execute some SQL
+        context.commitChanges();
+    }
 }