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/03/21 12:14:11 UTC

[cayenne] 01/02: CAY-2555 Use explicit ArcId in GraphChangeHandler methods

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

commit 05bb9ce1b22711d2e0c1ce31bc4c3de769452660
Author: Nikita Timofeev <st...@gmail.com>
AuthorDate: Thu Mar 21 14:09:52 2019 +0300

    CAY-2555 Use explicit ArcId in GraphChangeHandler methods
---
 .../org/apache/cayenne/NestedCayenneContextIT.java | 25 +++++--
 .../cayenne/NestedCayenneContextTooneIT.java       | 16 ++--
 .../cayenne/remote/ClientChannelServerDiffsIT.java | 11 ++-
 .../cayenne/commitlog/DeletedDiffProcessor.java    |  5 +-
 .../org/apache/cayenne/commitlog/DiffFilter.java   |  5 +-
 .../apache/cayenne/commitlog/DiffProcessor.java    |  5 +-
 .../cayenne/CayenneContextChildDiffLoader.java     |  9 ++-
 .../apache/cayenne/CayenneContextGraphManager.java | 15 +++-
 .../apache/cayenne/CayenneContextMergeHandler.java | 14 +++-
 .../cayenne/DataChannelSyncCallbackAction.java     | 11 ++-
 .../apache/cayenne/ObjectContextDeleteAction.java  |  6 +-
 .../org/apache/cayenne/ObjectContextStateLog.java  | 12 ++-
 .../cayenne/access/ClientReturnDiffFilter.java     | 11 ++-
 .../org/apache/cayenne/access/DataContext.java     | 15 ++--
 .../cayenne/access/DataContextMergeHandler.java    | 14 +++-
 .../cayenne/access/DataDomainDBDiffBuilder.java    | 15 +++-
 .../access/DataDomainIndirectDiffBuilder.java      |  5 +-
 .../java/org/apache/cayenne/access/ObjectDiff.java | 23 +++---
 .../org/apache/cayenne/access/ObjectStore.java     | 13 ++--
 .../apache/cayenne/graph/ArcCreateOperation.java   |  6 +-
 .../apache/cayenne/graph/ArcDeleteOperation.java   |  6 +-
 .../main/java/org/apache/cayenne/graph/ArcId.java  | 87 ++++++++++++++++++++++
 .../org/apache/cayenne/graph/ChildDiffLoader.java  | 10 ++-
 .../apache/cayenne/graph/GraphChangeHandler.java   |  4 +-
 .../apache/cayenne/graph/GraphDiffCompressor.java  | 24 +++---
 .../java/org/apache/cayenne/graph/GraphMap.java    | 16 +++-
 .../cayenne/util/ObjectContextGraphAction.java     |  5 +-
 .../main/java/org/apache/cayenne/util/Util.java    |  5 +-
 .../org/apache/cayenne/access/DeleteRulesIT.java   | 18 ++---
 .../cayenne/unit/di/DataChannelSyncStats.java      | 11 ++-
 30 files changed, 310 insertions(+), 112 deletions(-)

diff --git a/cayenne-client/src/test/java/org/apache/cayenne/NestedCayenneContextIT.java b/cayenne-client/src/test/java/org/apache/cayenne/NestedCayenneContextIT.java
index 83fad72..8f40904 100644
--- a/cayenne-client/src/test/java/org/apache/cayenne/NestedCayenneContextIT.java
+++ b/cayenne-client/src/test/java/org/apache/cayenne/NestedCayenneContextIT.java
@@ -20,6 +20,7 @@ package org.apache.cayenne;
 
 import org.apache.cayenne.configuration.rop.client.ClientRuntime;
 import org.apache.cayenne.di.Inject;
+import org.apache.cayenne.graph.ArcId;
 import org.apache.cayenne.graph.GraphChangeHandler;
 import org.apache.cayenne.graph.GraphDiff;
 import org.apache.cayenne.query.SelectById;
@@ -261,24 +262,30 @@ public class NestedCayenneContextIT extends RemoteCayenneCase {
 			final int[] arcDiffs = new int[1];
 
 			diffs.apply(new GraphChangeHandler() {
-				public void arcCreated(Object nodeId, Object targetNodeId, Object arcId) {
+				@Override
+				public void arcCreated(Object nodeId, Object targetNodeId, ArcId arcId) {
 					arcDiffs[0]++;
 				}
 
-				public void arcDeleted(Object nodeId, Object targetNodeId, Object arcId) {
+				@Override
+				public void arcDeleted(Object nodeId, Object targetNodeId, ArcId arcId) {
 					arcDiffs[0]--;
 				}
 
+				@Override
 				public void nodeCreated(Object nodeId) {
 				}
 
+				@Override
 				public void nodeIdChanged(Object nodeId, Object newId) {
 				}
 
+				@Override
 				public void nodePropertyChanged(Object nodeId,
 						String property, Object oldValue, Object newValue) {
 				}
 
+				@Override
 				public void nodeRemoved(Object nodeId) {
 				}
 			});
@@ -356,11 +363,11 @@ public class NestedCayenneContextIT extends RemoteCayenneCase {
 			diffs.apply(new GraphChangeHandler() {
 
 				@Override
-				public void arcCreated(Object nodeId, Object targetNodeId, Object arcId) {
+				public void arcCreated(Object nodeId, Object targetNodeId, ArcId arcId) {
 				}
 
 				@Override
-				public void arcDeleted(Object nodeId, Object targetNodeId, Object arcId) {
+				public void arcDeleted(Object nodeId, Object targetNodeId, ArcId arcId) {
 				}
 
 				@Override
@@ -644,24 +651,30 @@ public class NestedCayenneContextIT extends RemoteCayenneCase {
 		final int[] newNodes = new int[1];
 
 		diffs.apply(new GraphChangeHandler() {
-			public void arcCreated(Object nodeId, Object targetNodeId, Object arcId) {
+			@Override
+			public void arcCreated(Object nodeId, Object targetNodeId, ArcId arcId) {
 				arcDiffs[0]++;
 			}
 
-			public void arcDeleted(Object nodeId, Object targetNodeId, Object arcId) {
+			@Override
+			public void arcDeleted(Object nodeId, Object targetNodeId, ArcId arcId) {
 				arcDiffs[0]--;
 			}
 
+			@Override
 			public void nodeCreated(Object nodeId) {
 				newNodes[0]++;
 			}
 
+			@Override
 			public void nodeIdChanged(Object nodeId, Object newId) {
 			}
 
+			@Override
 			public void nodePropertyChanged(Object nodeId, String property, Object oldValue, Object newValue) {
 			}
 
+			@Override
 			public void nodeRemoved(Object nodeId) {
 				newNodes[0]--;
 			}
diff --git a/cayenne-client/src/test/java/org/apache/cayenne/NestedCayenneContextTooneIT.java b/cayenne-client/src/test/java/org/apache/cayenne/NestedCayenneContextTooneIT.java
index 28f9875..862a50f 100644
--- a/cayenne-client/src/test/java/org/apache/cayenne/NestedCayenneContextTooneIT.java
+++ b/cayenne-client/src/test/java/org/apache/cayenne/NestedCayenneContextTooneIT.java
@@ -21,6 +21,7 @@ package org.apache.cayenne;
 
 import org.apache.cayenne.configuration.rop.client.ClientRuntime;
 import org.apache.cayenne.di.Inject;
+import org.apache.cayenne.graph.ArcId;
 import org.apache.cayenne.graph.GraphChangeHandler;
 import org.apache.cayenne.graph.GraphDiff;
 import org.apache.cayenne.query.SelectQuery;
@@ -190,27 +191,30 @@ public class NestedCayenneContextTooneIT extends RemoteCayenneCase {
 
         diffs.apply(new GraphChangeHandler() {
 
-            public void arcCreated(Object nodeId, Object targetNodeId,
-                                   Object arcId) {
+            @Override
+            public void arcCreated(Object nodeId, Object targetNodeId, ArcId arcId) {
                 arcDiffs[0]++;
             }
 
-            public void arcDeleted(Object nodeId, Object targetNodeId,
-                                   Object arcId) {
+            @Override
+            public void arcDeleted(Object nodeId, Object targetNodeId, ArcId arcId) {
                 arcDiffs[0]--;
             }
 
+            @Override
             public void nodeCreated(Object nodeId) {
                 newNodes[0]++;
             }
 
+            @Override
             public void nodeIdChanged(Object nodeId, Object newId) {
             }
 
-            public void nodePropertyChanged(Object nodeId, String property,
-                                            Object oldValue, Object newValue) {
+            @Override
+            public void nodePropertyChanged(Object nodeId, String property, Object oldValue, Object newValue) {
             }
 
+            @Override
             public void nodeRemoved(Object nodeId) {
                 newNodes[0]--;
             }
diff --git a/cayenne-client/src/test/java/org/apache/cayenne/remote/ClientChannelServerDiffsIT.java b/cayenne-client/src/test/java/org/apache/cayenne/remote/ClientChannelServerDiffsIT.java
index 1bfdb24..6f747ff 100644
--- a/cayenne-client/src/test/java/org/apache/cayenne/remote/ClientChannelServerDiffsIT.java
+++ b/cayenne-client/src/test/java/org/apache/cayenne/remote/ClientChannelServerDiffsIT.java
@@ -24,6 +24,7 @@ import org.apache.cayenne.ObjectId;
 import org.apache.cayenne.access.ClientServerChannel;
 import org.apache.cayenne.di.Inject;
 import org.apache.cayenne.event.MockEventManager;
+import org.apache.cayenne.graph.ArcId;
 import org.apache.cayenne.graph.GraphChangeHandler;
 import org.apache.cayenne.graph.GraphDiff;
 import org.apache.cayenne.map.LifecycleEvent;
@@ -227,6 +228,7 @@ public class ClientChannelServerDiffsIT extends ClientCase {
             size = 0;
         }
 
+        @Override
         public void nodePropertyChanged(
                 Object nodeId,
                 String property,
@@ -236,22 +238,27 @@ public class ClientChannelServerDiffsIT extends ClientCase {
             size++;
         }
 
-        public void arcCreated(Object nodeId, Object targetNodeId, Object arcId) {
+        @Override
+        public void arcCreated(Object nodeId, Object targetNodeId, ArcId arcId) {
             size++;
         }
 
-        public void arcDeleted(Object nodeId, Object targetNodeId, Object arcId) {
+        @Override
+        public void arcDeleted(Object nodeId, Object targetNodeId, ArcId arcId) {
             size++;
         }
 
+        @Override
         public void nodeCreated(Object nodeId) {
             size++;
         }
 
+        @Override
         public void nodeIdChanged(Object nodeId, Object newId) {
             size++;
         }
 
+        @Override
         public void nodeRemoved(Object nodeId) {
             size++;
         }
diff --git a/cayenne-commitlog/src/main/java/org/apache/cayenne/commitlog/DeletedDiffProcessor.java b/cayenne-commitlog/src/main/java/org/apache/cayenne/commitlog/DeletedDiffProcessor.java
index a2f706f..c631cad 100644
--- a/cayenne-commitlog/src/main/java/org/apache/cayenne/commitlog/DeletedDiffProcessor.java
+++ b/cayenne-commitlog/src/main/java/org/apache/cayenne/commitlog/DeletedDiffProcessor.java
@@ -24,6 +24,7 @@ import org.apache.cayenne.DataChannel;
 import org.apache.cayenne.DataRow;
 import org.apache.cayenne.ObjectId;
 import org.apache.cayenne.QueryResponse;
+import org.apache.cayenne.graph.ArcId;
 import org.apache.cayenne.graph.GraphChangeHandler;
 import org.apache.cayenne.commitlog.model.MutableChangeMap;
 import org.apache.cayenne.commitlog.model.MutableObjectChange;
@@ -129,12 +130,12 @@ class DeletedDiffProcessor implements GraphChangeHandler {
 	}
 
 	@Override
-	public void arcCreated(Object nodeId, Object targetNodeId, Object arcId) {
+	public void arcCreated(Object nodeId, Object targetNodeId, ArcId arcId) {
 		// do nothing
 	}
 
 	@Override
-	public void arcDeleted(Object nodeId, Object targetNodeId, Object arcId) {
+	public void arcDeleted(Object nodeId, Object targetNodeId, ArcId arcId) {
 		// do nothing
 	}
 }
diff --git a/cayenne-commitlog/src/main/java/org/apache/cayenne/commitlog/DiffFilter.java b/cayenne-commitlog/src/main/java/org/apache/cayenne/commitlog/DiffFilter.java
index d524387..9b64208 100644
--- a/cayenne-commitlog/src/main/java/org/apache/cayenne/commitlog/DiffFilter.java
+++ b/cayenne-commitlog/src/main/java/org/apache/cayenne/commitlog/DiffFilter.java
@@ -19,6 +19,7 @@
 package org.apache.cayenne.commitlog;
 
 import org.apache.cayenne.ObjectId;
+import org.apache.cayenne.graph.ArcId;
 import org.apache.cayenne.graph.GraphChangeHandler;
 import org.apache.cayenne.commitlog.meta.CommitLogEntity;
 import org.apache.cayenne.commitlog.meta.CommitLogEntityFactory;
@@ -73,14 +74,14 @@ class DiffFilter implements GraphChangeHandler {
 	}
 
 	@Override
-	public void arcCreated(Object nodeId, Object targetNodeId, Object arcId) {
+	public void arcCreated(Object nodeId, Object targetNodeId, ArcId arcId) {
 		if (entityFactory.getEntity((ObjectId) nodeId).isIncluded(arcId.toString())) {
 			delegate.arcCreated(nodeId, targetNodeId, arcId);
 		}
 	}
 
 	@Override
-	public void arcDeleted(Object nodeId, Object targetNodeId, Object arcId) {
+	public void arcDeleted(Object nodeId, Object targetNodeId, ArcId arcId) {
 		if (entityFactory.getEntity((ObjectId) nodeId).isIncluded(arcId.toString())) {
 			delegate.arcDeleted(nodeId, targetNodeId, arcId);
 		}
diff --git a/cayenne-commitlog/src/main/java/org/apache/cayenne/commitlog/DiffProcessor.java b/cayenne-commitlog/src/main/java/org/apache/cayenne/commitlog/DiffProcessor.java
index 849aa3a..f8518f4 100644
--- a/cayenne-commitlog/src/main/java/org/apache/cayenne/commitlog/DiffProcessor.java
+++ b/cayenne-commitlog/src/main/java/org/apache/cayenne/commitlog/DiffProcessor.java
@@ -19,6 +19,7 @@
 package org.apache.cayenne.commitlog;
 
 import org.apache.cayenne.ObjectId;
+import org.apache.cayenne.graph.ArcId;
 import org.apache.cayenne.graph.GraphChangeHandler;
 import org.apache.cayenne.commitlog.model.MutableChangeMap;
 import org.apache.cayenne.commitlog.model.MutableObjectChange;
@@ -64,7 +65,7 @@ class DiffProcessor implements GraphChangeHandler {
 	}
 
 	@Override
-	public void arcDeleted(Object nodeId, Object targetNodeId, Object arcId) {
+	public void arcDeleted(Object nodeId, Object targetNodeId, ArcId arcId) {
 		ObjectId id = (ObjectId) nodeId;
 		String relationshipName = arcId.toString();
 
@@ -83,7 +84,7 @@ class DiffProcessor implements GraphChangeHandler {
 	}
 
 	@Override
-	public void arcCreated(Object nodeId, Object targetNodeId, Object arcId) {
+	public void arcCreated(Object nodeId, Object targetNodeId, ArcId arcId) {
 
 		ObjectId id = (ObjectId) nodeId;
 		String relationshipName = arcId.toString();
diff --git a/cayenne-rop-server/src/main/java/org/apache/cayenne/CayenneContextChildDiffLoader.java b/cayenne-rop-server/src/main/java/org/apache/cayenne/CayenneContextChildDiffLoader.java
index 94d30f2..6402d72 100644
--- a/cayenne-rop-server/src/main/java/org/apache/cayenne/CayenneContextChildDiffLoader.java
+++ b/cayenne-rop-server/src/main/java/org/apache/cayenne/CayenneContextChildDiffLoader.java
@@ -18,6 +18,7 @@
  ****************************************************************/
 package org.apache.cayenne;
 
+import org.apache.cayenne.graph.ArcId;
 import org.apache.cayenne.graph.ChildDiffLoader;
 import org.apache.cayenne.reflect.ArcProperty;
 import org.apache.cayenne.reflect.AttributeProperty;
@@ -52,7 +53,7 @@ class CayenneContextChildDiffLoader extends ChildDiffLoader {
     }
 
     @Override
-    public void arcCreated(Object nodeId, Object targetNodeId, Object arcId) {
+    public void arcCreated(Object nodeId, Object targetNodeId, ArcId arcId) {
 
         final Persistent source = findObject(nodeId);
         final Persistent target = findObject(targetNodeId);
@@ -83,11 +84,11 @@ class CayenneContextChildDiffLoader extends ChildDiffLoader {
                 return false;
             }
         });
-        context.propertyChanged(source, (String) arcId, null, target);
+        context.propertyChanged(source, arcId.toString(), null, target);
     }
 
     @Override
-    public void arcDeleted(Object nodeId, final Object targetNodeId, Object arcId) {
+    public void arcDeleted(Object nodeId, final Object targetNodeId, ArcId arcId) {
         final Persistent source = findObject(nodeId);
 
         // needed as sometime temporary objects are evoked from the context before
@@ -133,7 +134,7 @@ class CayenneContextChildDiffLoader extends ChildDiffLoader {
             }
         });
 
-        context.propertyChanged(source, (String) arcId, target[0], null);
+        context.propertyChanged(source, arcId.toString(), target[0], null);
     }
 
 }
\ No newline at end of file
diff --git a/cayenne-rop-server/src/main/java/org/apache/cayenne/CayenneContextGraphManager.java b/cayenne-rop-server/src/main/java/org/apache/cayenne/CayenneContextGraphManager.java
index f4865c0..cfc0be6 100644
--- a/cayenne-rop-server/src/main/java/org/apache/cayenne/CayenneContextGraphManager.java
+++ b/cayenne-rop-server/src/main/java/org/apache/cayenne/CayenneContextGraphManager.java
@@ -23,6 +23,7 @@ import org.apache.cayenne.event.EventManager;
 import org.apache.cayenne.event.EventSubject;
 import org.apache.cayenne.graph.ArcCreateOperation;
 import org.apache.cayenne.graph.ArcDeleteOperation;
+import org.apache.cayenne.graph.ArcId;
 import org.apache.cayenne.graph.GraphChangeHandler;
 import org.apache.cayenne.graph.GraphDiff;
 import org.apache.cayenne.graph.GraphEvent;
@@ -274,13 +275,13 @@ final class CayenneContextGraphManager extends GraphMap {
     }
 
     @Override
-    public synchronized void arcCreated(Object nodeId, Object targetNodeId, Object arcId) {
+    public synchronized void arcCreated(Object nodeId, Object targetNodeId, ArcId arcId) {
         stateLog.arcCreated(nodeId, targetNodeId, arcId);
         processChange(new ArcCreateOperation(nodeId, targetNodeId, arcId));
     }
 
     @Override
-    public synchronized void arcDeleted(Object nodeId, Object targetNodeId, Object arcId) {
+    public synchronized void arcDeleted(Object nodeId, Object targetNodeId, ArcId arcId) {
         stateLog.arcDeleted(nodeId, targetNodeId, arcId);
         processChange(new ArcDeleteOperation(nodeId, targetNodeId, arcId));
     }
@@ -337,20 +338,24 @@ final class CayenneContextGraphManager extends GraphMap {
      */
     class RollbackChangeHandler implements GraphChangeHandler {
 
-        public void arcCreated(Object nodeId, Object targetNodeId, Object arcId) {
+        @Override
+        public void arcCreated(Object nodeId, Object targetNodeId, ArcId arcId) {
             context.mergeHandler.arcCreated(nodeId, targetNodeId, arcId);
             CayenneContextGraphManager.this.arcCreated(nodeId, targetNodeId, arcId);
         }
 
-        public void arcDeleted(Object nodeId, Object targetNodeId, Object arcId) {
+        @Override
+        public void arcDeleted(Object nodeId, Object targetNodeId, ArcId arcId) {
             context.mergeHandler.arcDeleted(nodeId, targetNodeId, arcId);
             CayenneContextGraphManager.this.arcDeleted(nodeId, targetNodeId, arcId);
         }
 
+        @Override
         public void nodeCreated(Object nodeId) {
             CayenneContextGraphManager.this.nodeCreated(nodeId);
         }
 
+        @Override
         public void nodeIdChanged(Object nodeId, Object newId) {
             CayenneContextGraphManager.this.nodeIdChanged(nodeId, newId);
         }
@@ -358,6 +363,7 @@ final class CayenneContextGraphManager extends GraphMap {
         /**
          * Need to write property directly to this context
          */
+        @Override
         public void nodePropertyChanged(
                 Object nodeId,
                 String property,
@@ -372,6 +378,7 @@ final class CayenneContextGraphManager extends GraphMap {
                     newValue);
         }
 
+        @Override
         public void nodeRemoved(Object nodeId) {
             CayenneContextGraphManager.this.nodeRemoved(nodeId);
         }
diff --git a/cayenne-rop-server/src/main/java/org/apache/cayenne/CayenneContextMergeHandler.java b/cayenne-rop-server/src/main/java/org/apache/cayenne/CayenneContextMergeHandler.java
index e3217a8..5ede370 100644
--- a/cayenne-rop-server/src/main/java/org/apache/cayenne/CayenneContextMergeHandler.java
+++ b/cayenne-rop-server/src/main/java/org/apache/cayenne/CayenneContextMergeHandler.java
@@ -19,6 +19,7 @@
 
 package org.apache.cayenne;
 
+import org.apache.cayenne.graph.ArcId;
 import org.apache.cayenne.graph.GraphChangeHandler;
 import org.apache.cayenne.graph.GraphDiff;
 import org.apache.cayenne.graph.GraphEvent;
@@ -47,6 +48,7 @@ class CayenneContextMergeHandler implements GraphChangeHandler, DataChannelListe
 
     // ******* DataChannelListener methods *******
 
+    @Override
     public void graphChanged(final GraphEvent e) {
         // process flush
         if (shouldProcessEvent(e) && e.getDiff() != null) {
@@ -63,6 +65,7 @@ class CayenneContextMergeHandler implements GraphChangeHandler, DataChannelListe
         }
     }
 
+    @Override
     public void graphFlushed(final GraphEvent e) {
         // TODO (Andrus, 10/17/2005) - there are a few problems with commit processing:
 
@@ -92,6 +95,7 @@ class CayenneContextMergeHandler implements GraphChangeHandler, DataChannelListe
         }
     }
 
+    @Override
     public void graphRolledback(final GraphEvent e) {
 
         // TODO: andrus, 3/29/2007: per CAY-771, if a LOCAL peer context posted the event,
@@ -122,6 +126,7 @@ class CayenneContextMergeHandler implements GraphChangeHandler, DataChannelListe
 
     // ******* GraphChangeHandler methods *********
 
+    @Override
     public void nodeIdChanged(Object nodeId, Object newId) {
         // do not unregister the node just yet... only put replaced id in deadIds to
         // remove it later. Otherwise stored operations will not work
@@ -138,14 +143,17 @@ class CayenneContextMergeHandler implements GraphChangeHandler, DataChannelListe
         }
     }
 
+    @Override
     public void nodeCreated(Object nodeId) {
         // ignore
     }
 
+    @Override
     public void nodeRemoved(Object nodeId) {
         context.getGraphManager().unregisterNode(nodeId);
     }
 
+    @Override
     public void nodePropertyChanged(
             Object nodeId,
             String property,
@@ -164,7 +172,8 @@ class CayenneContextMergeHandler implements GraphChangeHandler, DataChannelListe
         }
     }
 
-    public void arcCreated(Object nodeId, Object targetNodeId, Object arcId) {
+    @Override
+    public void arcCreated(Object nodeId, Object targetNodeId, ArcId arcId) {
         // null source or target likely means the object is not faulted yet... Faults
         // shouldn't get disturbed by adding/removing arcs
 
@@ -194,7 +203,8 @@ class CayenneContextMergeHandler implements GraphChangeHandler, DataChannelListe
         }
     }
 
-    public void arcDeleted(Object nodeId, Object targetNodeId, Object arcId) {
+    @Override
+    public void arcDeleted(Object nodeId, Object targetNodeId, ArcId arcId) {
 
         // null source or target likely means the object is not faulted yet... Faults
         // shouldn't get disturbed by adding/removing arcs
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/DataChannelSyncCallbackAction.java b/cayenne-server/src/main/java/org/apache/cayenne/DataChannelSyncCallbackAction.java
index 4e954f3..e4608ab 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/DataChannelSyncCallbackAction.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/DataChannelSyncCallbackAction.java
@@ -18,6 +18,7 @@
  ****************************************************************/
 package org.apache.cayenne;
 
+import org.apache.cayenne.graph.ArcId;
 import org.apache.cayenne.graph.GraphChangeHandler;
 import org.apache.cayenne.graph.GraphDiff;
 import org.apache.cayenne.graph.GraphManager;
@@ -87,6 +88,7 @@ public abstract class DataChannelSyncCallbackAction implements GraphChangeHandle
         }
     }
 
+    @Override
     public void nodeCreated(Object nodeId) {
         Op op = seenIds.put(nodeId, Op.INSERT);
         if (op == null) {
@@ -103,6 +105,7 @@ public abstract class DataChannelSyncCallbackAction implements GraphChangeHandle
         }
     }
 
+    @Override
     public void nodeRemoved(Object nodeId) {
         Op op = seenIds.put(nodeId, Op.DELETE);
         
@@ -128,19 +131,23 @@ public abstract class DataChannelSyncCallbackAction implements GraphChangeHandle
         }
     }
 
-    public void arcCreated(Object nodeId, Object targetNodeId, Object arcId) {
+    @Override
+    public void arcCreated(Object nodeId, Object targetNodeId, ArcId arcId) {
         // TODO: andrus, 9/21/2006 - should we register to-many relationship updates?
         nodeUpdated(nodeId);
     }
 
-    public void arcDeleted(Object nodeId, Object targetNodeId, Object arcId) {
+    @Override
+    public void arcDeleted(Object nodeId, Object targetNodeId, ArcId arcId) {
         // TODO: andrus, 9/21/2006 - should we register to-many relationship updates?
         nodeUpdated(nodeId);
     }
 
+    @Override
     public void nodeIdChanged(Object nodeId, Object newId) {
     }
 
+    @Override
     public void nodePropertyChanged(
             Object nodeId,
             String property,
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/ObjectContextDeleteAction.java b/cayenne-server/src/main/java/org/apache/cayenne/ObjectContextDeleteAction.java
index e38868c..4d0f431 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/ObjectContextDeleteAction.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/ObjectContextDeleteAction.java
@@ -24,6 +24,7 @@ import java.util.Collection;
 import java.util.Collections;
 import java.util.Map;
 
+import org.apache.cayenne.graph.ArcId;
 import org.apache.cayenne.map.DeleteRule;
 import org.apache.cayenne.map.LifecycleEvent;
 import org.apache.cayenne.map.ObjRelationship;
@@ -161,9 +162,10 @@ class ObjectContextDeleteAction {
             // joins must be removed even if they are non-existent or ignored in the
             // object graph
             if (processFlattened) {
+                ArcId arcId = new ArcId(property);
                 for (Persistent relatedObject : relatedObjects) {
-                    context.getGraphManager().arcDeleted(object.getObjectId()
-                            , relatedObject.getObjectId(), relationship.getName());
+                    context.getGraphManager()
+                            .arcDeleted(object.getObjectId(), relatedObject.getObjectId(), arcId);
                 }
             }
 
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/ObjectContextStateLog.java b/cayenne-server/src/main/java/org/apache/cayenne/ObjectContextStateLog.java
index 25a970b..7847d8b 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/ObjectContextStateLog.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/ObjectContextStateLog.java
@@ -19,6 +19,7 @@
 
 package org.apache.cayenne;
 
+import org.apache.cayenne.graph.ArcId;
 import org.apache.cayenne.graph.GraphChangeHandler;
 import org.apache.cayenne.graph.GraphManager;
 
@@ -28,7 +29,6 @@ import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
-import java.util.Vector;
 
 /**
  * Tracks dirty Persistent objects.
@@ -150,20 +150,24 @@ class ObjectContextStateLog implements GraphChangeHandler {
 
     // *** GraphChangeHandler methods
 
+    @Override
     public void nodeIdChanged(Object nodeId, Object newId) {
         if (dirtyIds.remove(nodeId)) {
             dirtyIds.add(newId);
         }
     }
 
+    @Override
     public void nodeCreated(Object nodeId) {
         dirtyIds.add(nodeId);
     }
 
+    @Override
     public void nodeRemoved(Object nodeId) {
         dirtyIds.add(nodeId);
     }
 
+    @Override
     public void nodePropertyChanged(
             Object nodeId,
             String property,
@@ -172,11 +176,13 @@ class ObjectContextStateLog implements GraphChangeHandler {
         dirtyIds.add(nodeId);
     }
 
-    public void arcCreated(Object nodeId, Object targetNodeId, Object arcId) {
+    @Override
+    public void arcCreated(Object nodeId, Object targetNodeId, ArcId arcId) {
         dirtyIds.add(nodeId);
     }
 
-    public void arcDeleted(Object nodeId, Object targetNodeId, Object arcId) {
+    @Override
+    public void arcDeleted(Object nodeId, Object targetNodeId, ArcId arcId) {
         dirtyIds.add(nodeId);
     }
 
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/ClientReturnDiffFilter.java b/cayenne-server/src/main/java/org/apache/cayenne/access/ClientReturnDiffFilter.java
index aae41da..c18e332 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/access/ClientReturnDiffFilter.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/access/ClientReturnDiffFilter.java
@@ -21,6 +21,7 @@ package org.apache.cayenne.access;
 import org.apache.cayenne.ObjectId;
 import org.apache.cayenne.graph.ArcCreateOperation;
 import org.apache.cayenne.graph.ArcDeleteOperation;
+import org.apache.cayenne.graph.ArcId;
 import org.apache.cayenne.graph.CompoundDiff;
 import org.apache.cayenne.graph.GraphChangeHandler;
 import org.apache.cayenne.graph.GraphDiff;
@@ -57,30 +58,35 @@ class ClientReturnDiffFilter implements GraphChangeHandler {
         return new CompoundDiff(diffs);
     }
 
-    public void arcCreated(Object nodeId, Object targetNodeId, Object arcId) {
+    @Override
+    public void arcCreated(Object nodeId, Object targetNodeId, ArcId arcId) {
         if (isClientArc(nodeId, targetNodeId, arcId)) {
             diffs.add(new ArcCreateOperation(nodeId, targetNodeId, arcId));
         }
     }
 
-    public void arcDeleted(Object nodeId, Object targetNodeId, Object arcId) {
+    @Override
+    public void arcDeleted(Object nodeId, Object targetNodeId, ArcId arcId) {
         if (isClientArc(nodeId, targetNodeId, arcId)) {
             diffs.add(new ArcDeleteOperation(nodeId, targetNodeId, arcId));
         }
     }
 
+    @Override
     public void nodeCreated(Object nodeId) {
         if (isClientNode(nodeId)) {
             diffs.add(new NodeCreateOperation(nodeId));
         }
     }
 
+    @Override
     public void nodeIdChanged(Object nodeId, Object newId) {
         if (isClientNode(nodeId)) {
             diffs.add(new NodeIdChangeOperation(nodeId, newId));
         }
     }
 
+    @Override
     public void nodePropertyChanged(
             Object nodeId,
             String property,
@@ -96,6 +102,7 @@ class ClientReturnDiffFilter implements GraphChangeHandler {
         }
     }
 
+    @Override
     public void nodeRemoved(Object nodeId) {
         if (isClientNode(nodeId)) {
             diffs.add(new NodeDeleteOperation(nodeId));
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/DataContext.java b/cayenne-server/src/main/java/org/apache/cayenne/access/DataContext.java
index 57993f4..fc2f585 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/access/DataContext.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/access/DataContext.java
@@ -44,6 +44,7 @@ import org.apache.cayenne.ResultIterator;
 import org.apache.cayenne.access.util.IteratedSelectObserver;
 import org.apache.cayenne.di.Injector;
 import org.apache.cayenne.event.EventManager;
+import org.apache.cayenne.graph.ArcId;
 import org.apache.cayenne.graph.ChildDiffLoader;
 import org.apache.cayenne.graph.CompoundDiff;
 import org.apache.cayenne.graph.GraphDiff;
@@ -566,20 +567,18 @@ public class DataContext extends BaseContext {
                 if (!property.isFault(persistent)) {
 
                     Object value = property.readProperty(persistent);
-                    Collection<Map.Entry> collection = (value instanceof Map) ? ((Map) value).entrySet()
+                    @SuppressWarnings("unchecked")
+                    Collection<Map.Entry> collection = (value instanceof Map)
+                            ? ((Map) value).entrySet()
                             : (Collection) value;
 
-                    Iterator<Map.Entry> it = collection.iterator();
-                    while (it.hasNext()) {
-                        Object target = it.next();
-
+                    for (Object target : collection) {
                         if (target instanceof Persistent) {
                             Persistent targetDO = (Persistent) target;
 
                             // make sure it is registered
                             registerNewObject(targetDO);
-                            getObjectStore().arcCreated(persistent.getObjectId(), targetDO.getObjectId(),
-                                    property.getName());
+                            getObjectStore().arcCreated(persistent.getObjectId(), targetDO.getObjectId(), new ArcId(property));
                         }
                     }
                 }
@@ -595,7 +594,7 @@ public class DataContext extends BaseContext {
 
                     // make sure it is registered
                     registerNewObject(targetDO);
-                    getObjectStore().arcCreated(persistent.getObjectId(), targetDO.getObjectId(), property.getName());
+                    getObjectStore().arcCreated(persistent.getObjectId(), targetDO.getObjectId(), new ArcId(property));
                 }
                 return true;
             }
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/DataContextMergeHandler.java b/cayenne-server/src/main/java/org/apache/cayenne/access/DataContextMergeHandler.java
index 6b422ea..3dcda4b 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/access/DataContextMergeHandler.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/access/DataContextMergeHandler.java
@@ -25,6 +25,7 @@ import org.apache.cayenne.PersistenceState;
 import org.apache.cayenne.Persistent;
 import org.apache.cayenne.access.ObjectStore.SnapshotEventDecorator;
 import org.apache.cayenne.access.event.SnapshotEvent;
+import org.apache.cayenne.graph.ArcId;
 import org.apache.cayenne.graph.GraphChangeHandler;
 import org.apache.cayenne.graph.GraphDiff;
 import org.apache.cayenne.graph.GraphEvent;
@@ -87,6 +88,7 @@ class DataContextMergeHandler implements GraphChangeHandler, DataChannelListener
 
     // *** GraphEventListener methods
 
+    @Override
     public void graphChanged(GraphEvent event) {
         // parent received external change
         if (shouldProcessEvent(event)) {
@@ -107,6 +109,7 @@ class DataContextMergeHandler implements GraphChangeHandler, DataChannelListener
         }
     }
 
+    @Override
     public void graphFlushed(GraphEvent event) {
 
         // peer is committed
@@ -130,6 +133,7 @@ class DataContextMergeHandler implements GraphChangeHandler, DataChannelListener
         }
     }
 
+    @Override
     public void graphRolledback(GraphEvent event) {
         // TODO: andrus, 3/26/2006 - enable this once all ObjectStore diffs implement
         // working undo operation
@@ -141,14 +145,17 @@ class DataContextMergeHandler implements GraphChangeHandler, DataChannelListener
 
     // *** GraphChangeHandler methods
 
+    @Override
     public void nodeIdChanged(Object nodeId, Object newId) {
         context.getObjectStore().processIdChange(nodeId, newId);
     }
 
+    @Override
     public void nodeCreated(Object nodeId) {
         // noop
     }
 
+    @Override
     public void nodeRemoved(Object nodeId) {
         ObjectStore os = context.getObjectStore();
         synchronized (os) {
@@ -156,6 +163,7 @@ class DataContextMergeHandler implements GraphChangeHandler, DataChannelListener
         }
     }
 
+    @Override
     public void nodePropertyChanged(
             Object nodeId,
             String property,
@@ -173,11 +181,13 @@ class DataContextMergeHandler implements GraphChangeHandler, DataChannelListener
         }
     }
 
-    public void arcCreated(Object nodeId, Object targetNodeId, Object arcId) {
+    @Override
+    public void arcCreated(Object nodeId, Object targetNodeId, ArcId arcId) {
         arcChanged(nodeId, targetNodeId, arcId);
     }
 
-    public void arcDeleted(Object nodeId, Object targetNodeId, Object arcId) {
+    @Override
+    public void arcDeleted(Object nodeId, Object targetNodeId, ArcId arcId) {
         arcChanged(nodeId, targetNodeId, arcId);
     }
 
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/DataDomainDBDiffBuilder.java b/cayenne-server/src/main/java/org/apache/cayenne/access/DataDomainDBDiffBuilder.java
index 80db9c5..ea6964d 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/access/DataDomainDBDiffBuilder.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/access/DataDomainDBDiffBuilder.java
@@ -22,6 +22,7 @@ package org.apache.cayenne.access;
 import org.apache.cayenne.ObjectId;
 import org.apache.cayenne.access.DataDomainSyncBucket.PropagatedValueFactory;
 import org.apache.cayenne.exp.parser.ASTDbPath;
+import org.apache.cayenne.graph.ArcId;
 import org.apache.cayenne.graph.GraphChangeHandler;
 import org.apache.cayenne.graph.GraphDiff;
 import org.apache.cayenne.map.DbAttribute;
@@ -150,6 +151,7 @@ class DataDomainDBDiffBuilder implements GraphChangeHandler {
     // GraphChangeHandler methods.
     // ==================================================
 
+    @Override
     public void nodePropertyChanged(Object nodeId, String property, Object oldValue, Object newValue) {
         // note - no checking for phantom mod... assuming there is no phantom
         // diffs
@@ -161,7 +163,8 @@ class DataDomainDBDiffBuilder implements GraphChangeHandler {
         currentPropertyDiff.put(property, newValue);
     }
 
-    public void arcCreated(Object nodeId, Object targetNodeId, Object arcId) {
+    @Override
+    public void arcCreated(Object nodeId, Object targetNodeId, ArcId arcId) {
         String arcIdString = arcId.toString();
         ObjRelationship relationship = objEntity.getRelationship(arcIdString);
 
@@ -184,14 +187,15 @@ class DataDomainDBDiffBuilder implements GraphChangeHandler {
         }
     }
 
-    private void doArcCreated(Object targetNodeId, Object arcId) {
+    private void doArcCreated(Object targetNodeId, ArcId arcId) {
         if (currentArcDiff == null) {
             currentArcDiff = new HashMap<>();
         }
         currentArcDiff.put(arcId, targetNodeId);
     }
 
-    public void arcDeleted(Object nodeId, Object targetNodeId, Object arcId) {
+    @Override
+    public void arcDeleted(Object nodeId, Object targetNodeId, ArcId arcId) {
 
         String arcIdString = arcId.toString();
         ObjRelationship relationship = objEntity.getRelationship(arcIdString);
@@ -214,7 +218,7 @@ class DataDomainDBDiffBuilder implements GraphChangeHandler {
         }
     }
 
-    private void doArcDeleted(Object targetNodeId, Object arcId) {
+    private void doArcDeleted(Object targetNodeId, ArcId arcId) {
         if (currentArcDiff == null) {
             currentArcDiff = new HashMap<>();
             currentArcDiff.put(arcId, null);
@@ -228,15 +232,18 @@ class DataDomainDBDiffBuilder implements GraphChangeHandler {
         }
     }
 
+    @Override
     public void nodeCreated(Object nodeId) {
         // need to append PK columns
         this.currentId = nodeId;
     }
 
+    @Override
     public void nodeRemoved(Object nodeId) {
         // noop
     }
 
+    @Override
     public void nodeIdChanged(Object nodeId, Object newId) {
         // noop
     }
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 c824d6e..b7aeaf2 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
@@ -24,6 +24,7 @@ import java.util.HashSet;
 
 import org.apache.cayenne.CayenneRuntimeException;
 import org.apache.cayenne.ObjectId;
+import org.apache.cayenne.graph.ArcId;
 import org.apache.cayenne.graph.GraphChangeHandler;
 import org.apache.cayenne.graph.GraphDiff;
 import org.apache.cayenne.map.DbEntity;
@@ -74,7 +75,7 @@ final class DataDomainIndirectDiffBuilder implements GraphChangeHandler {
     }
 
     @Override
-    public void arcCreated(Object nodeId, Object targetNodeId, Object arcId) {
+    public void arcCreated(Object nodeId, Object targetNodeId, ArcId arcId) {
         ObjEntity entity = resolver.getObjEntity(((ObjectId) nodeId).getEntityName());
         ObjRelationship relationship = entity.getRelationship(arcId.toString());
 
@@ -114,7 +115,7 @@ final class DataDomainIndirectDiffBuilder implements GraphChangeHandler {
     }
 
     @Override
-    public void arcDeleted(Object nodeId, Object targetNodeId, Object arcId) {
+    public void arcDeleted(Object nodeId, Object targetNodeId, ArcId arcId) {
 
         ObjEntity entity = resolver.getObjEntity(((ObjectId) nodeId).getEntityName());
         ObjRelationship relationship = entity.getRelationship(arcId.toString());
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/ObjectDiff.java b/cayenne-server/src/main/java/org/apache/cayenne/access/ObjectDiff.java
index d711bcf..97080eb 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/access/ObjectDiff.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/access/ObjectDiff.java
@@ -25,6 +25,7 @@ import org.apache.cayenne.ObjectId;
 import org.apache.cayenne.PersistenceState;
 import org.apache.cayenne.Persistent;
 import org.apache.cayenne.exp.parser.ASTDbPath;
+import org.apache.cayenne.graph.ArcId;
 import org.apache.cayenne.graph.GraphChangeHandler;
 import org.apache.cayenne.graph.GraphDiff;
 import org.apache.cayenne.graph.NodeDiff;
@@ -195,16 +196,16 @@ class ObjectDiff extends NodeDiff {
 
             ArcOperation arcDiff = (ArcOperation) diff;
             Object targetId = arcDiff.getTargetNodeId();
-            String arcId = arcDiff.getArcId().toString();
+            ArcId arcId = arcDiff.getArcId();
 
-            ArcProperty property = (ArcProperty) getClassDescriptor().getProperty(arcId);
+            ArcProperty property = (ArcProperty) getClassDescriptor().getProperty(arcId.getForwardArc());
 
             // note that some collection properties implement
             // 'SingleObjectArcProperty',
             // so we cant't do 'instanceof SingleObjectArcProperty'
             // TODO: andrus, 3.22.2006 - should we consider this a bug?
 
-            if (property == null && arcId.startsWith(ASTDbPath.DB_PREFIX)) {
+            if (property == null && arcId.getForwardArc().startsWith(ASTDbPath.DB_PREFIX)) {
                 addPhantomFkDiff(arcDiff);
                 addDiff = false;
             } else if (property instanceof ToManyProperty) {
@@ -232,10 +233,10 @@ class ObjectDiff extends NodeDiff {
                 } else if (property.getComplimentaryReverseArc() == null) {
 
                     // register complimentary arc diff
-                    String arc = ASTDbPath.DB_PREFIX + property.getComplimentaryReverseDbRelationshipPath();
-                    ArcOperation complimentartyOp = new ArcOperation(targetId, arcDiff.getNodeId(), arc,
-                            arcDiff.isDelete());
-                    parent.registerDiff(targetId, complimentartyOp);
+                    ArcId arc = arcId.getReverseId();
+                    //new ArcId(ASTDbPath.DB_PREFIX + property.getComplimentaryReverseDbRelationshipPath(), property.getName());
+                    ArcOperation complimentaryOp = new ArcOperation(targetId, arcDiff.getNodeId(), arc, arcDiff.isDelete());
+                    parent.registerDiff(targetId, complimentaryOp);
                 }
 
             } else if (property instanceof ToOneProperty) {
@@ -244,7 +245,7 @@ class ObjectDiff extends NodeDiff {
                     currentArcSnapshot = new HashMap<>();
                 }
 
-                currentArcSnapshot.put(arcId, targetId);
+                currentArcSnapshot.put(arcId.getForwardArc(), targetId);
             } else {
                 String message = (property == null) ? "No property for arcId " + arcId
                         : "Unrecognized property for arcId " + arcId + ": " + property;
@@ -443,10 +444,10 @@ class ObjectDiff extends NodeDiff {
     static final class ArcOperation extends NodeDiff {
 
         private Object targetNodeId;
-        private Object arcId;
+        private ArcId arcId;
         private boolean delete;
 
-        public ArcOperation(Object nodeId, Object targetNodeId, Object arcId, boolean delete) {
+        ArcOperation(Object nodeId, Object targetNodeId, ArcId arcId, boolean delete) {
 
             super(nodeId);
             this.targetNodeId = targetNodeId;
@@ -498,7 +499,7 @@ class ObjectDiff extends NodeDiff {
             throw new UnsupportedOperationException();
         }
 
-        public Object getArcId() {
+        public ArcId getArcId() {
             return arcId;
         }
 
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/ObjectStore.java b/cayenne-server/src/main/java/org/apache/cayenne/access/ObjectStore.java
index 4bb72c2..8a821c4 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/access/ObjectStore.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/access/ObjectStore.java
@@ -29,6 +29,7 @@ import org.apache.cayenne.Persistent;
 import org.apache.cayenne.access.ObjectDiff.ArcOperation;
 import org.apache.cayenne.access.event.SnapshotEvent;
 import org.apache.cayenne.access.event.SnapshotEventListener;
+import org.apache.cayenne.graph.ArcId;
 import org.apache.cayenne.graph.ChildDiffLoader;
 import org.apache.cayenne.graph.GraphChangeHandler;
 import org.apache.cayenne.graph.GraphDiff;
@@ -430,11 +431,11 @@ public class ObjectStore implements Serializable, SnapshotEventListener, GraphMa
             parentChanges.apply(new GraphChangeHandler() {
 
                 @Override
-                public void arcCreated(Object nodeId, Object targetNodeId, Object arcId) {
+                public void arcCreated(Object nodeId, Object targetNodeId, ArcId arcId) {
                 }
 
                 @Override
-                public void arcDeleted(Object nodeId, Object targetNodeId, Object arcId) {
+                public void arcDeleted(Object nodeId, Object targetNodeId, ArcId arcId) {
                 }
 
                 @Override
@@ -963,8 +964,8 @@ public class ObjectStore implements Serializable, SnapshotEventListener, GraphMa
      * @since 1.2
      */
     @Override
-    public void arcCreated(Object nodeId, Object targetNodeId, Object arcId) {
-        NodeDiff diff = new ArcOperation(nodeId, targetNodeId, arcId.toString(), false);
+    public void arcCreated(Object nodeId, Object targetNodeId, ArcId arcId) {
+        NodeDiff diff = new ArcOperation(nodeId, targetNodeId, arcId, false);
 
         if (lifecycleEventInducedChanges != null) {
             registerLifecycleEventInducedChange(diff);
@@ -977,8 +978,8 @@ public class ObjectStore implements Serializable, SnapshotEventListener, GraphMa
      * @since 1.2
      */
     @Override
-    public void arcDeleted(Object nodeId, Object targetNodeId, Object arcId) {
-        NodeDiff diff = new ArcOperation(nodeId, targetNodeId, arcId.toString(), true);
+    public void arcDeleted(Object nodeId, Object targetNodeId, ArcId arcId) {
+        NodeDiff diff = new ArcOperation(nodeId, targetNodeId, arcId, true);
 
         if (lifecycleEventInducedChanges != null) {
             registerLifecycleEventInducedChange(diff);
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/graph/ArcCreateOperation.java b/cayenne-server/src/main/java/org/apache/cayenne/graph/ArcCreateOperation.java
index 8182756..f36c89a 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/graph/ArcCreateOperation.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/graph/ArcCreateOperation.java
@@ -27,15 +27,15 @@ public class ArcCreateOperation extends NodeDiff {
 	private static final long serialVersionUID = 2497511574121718987L;
 	
 	protected Object targetNodeId;
-	protected Object arcId;
+	protected ArcId arcId;
 
-	public ArcCreateOperation(Object nodeId, Object targetNodeId, Object arcId) {
+	public ArcCreateOperation(Object nodeId, Object targetNodeId, ArcId arcId) {
 		super(nodeId);
 		this.targetNodeId = targetNodeId;
 		this.arcId = arcId;
 	}
 
-	public ArcCreateOperation(Object nodeId, Object targetNodeId, Object arcId, int diffId) {
+	public ArcCreateOperation(Object nodeId, Object targetNodeId, ArcId arcId, int diffId) {
 		super(nodeId, diffId);
 		this.targetNodeId = targetNodeId;
 		this.arcId = arcId;
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/graph/ArcDeleteOperation.java b/cayenne-server/src/main/java/org/apache/cayenne/graph/ArcDeleteOperation.java
index aaada17..e59507b 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/graph/ArcDeleteOperation.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/graph/ArcDeleteOperation.java
@@ -27,15 +27,15 @@ public class ArcDeleteOperation extends NodeDiff {
 	private static final long serialVersionUID = 3614253207710845385L;
 	
 	protected Object targetNodeId;
-	protected Object arcId;
+	protected ArcId arcId;
 
-	public ArcDeleteOperation(Object nodeId, Object targetNodeId, Object arcId) {
+	public ArcDeleteOperation(Object nodeId, Object targetNodeId, ArcId arcId) {
 		super(nodeId);
 		this.targetNodeId = targetNodeId;
 		this.arcId = arcId;
 	}
 
-	public ArcDeleteOperation(Object nodeId, Object targetNodeId, Object arcId, int diffId) {
+	public ArcDeleteOperation(Object nodeId, Object targetNodeId, ArcId arcId, int diffId) {
 		super(nodeId, diffId);
 		this.targetNodeId = targetNodeId;
 		this.arcId = arcId;
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/graph/ArcId.java b/cayenne-server/src/main/java/org/apache/cayenne/graph/ArcId.java
new file mode 100644
index 0000000..2a66a60
--- /dev/null
+++ b/cayenne-server/src/main/java/org/apache/cayenne/graph/ArcId.java
@@ -0,0 +1,87 @@
+/*****************************************************************
+ *   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.graph;
+
+import java.io.Serializable;
+import java.util.Objects;
+
+import org.apache.cayenne.exp.parser.ASTDbPath;
+import org.apache.cayenne.reflect.ArcProperty;
+
+/**
+ * Object that represents Arc identifier.
+ * Used in graph change operations.
+ *
+ * @since 4.2
+ */
+public class ArcId implements Serializable {
+
+    private static final long serialVersionUID = -3712846298213425259L;
+
+    private final String forwardArc;
+    private final String reverseArc;
+
+    public ArcId(ArcProperty property) {
+        this.forwardArc = property.getName();
+        this.reverseArc = property.getComplimentaryReverseArc() == null
+                ? ASTDbPath.DB_PREFIX + property.getComplimentaryReverseDbRelationshipPath()
+                : property.getComplimentaryReverseArc().getName();
+    }
+
+    public ArcId(String forwardArc, String reverseArc) {
+        this.forwardArc = Objects.requireNonNull(forwardArc);
+        this.reverseArc = Objects.requireNonNull(reverseArc);
+    }
+
+    public String getForwardArc() {
+        return forwardArc;
+    }
+
+    public String getReverseArc() {
+        return reverseArc;
+    }
+
+    public ArcId getReverseId() {
+        return new ArcId(reverseArc, forwardArc);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+
+        ArcId arcId = (ArcId) o;
+
+        if (!forwardArc.equals(arcId.forwardArc)) return false;
+        return reverseArc.equals(arcId.reverseArc);
+    }
+
+    @Override
+    public int hashCode() {
+        int result = forwardArc.hashCode();
+        result = 31 * result + reverseArc.hashCode();
+        return result;
+    }
+
+    @Override
+    public String toString() {
+        return forwardArc;
+    }
+}
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/graph/ChildDiffLoader.java b/cayenne-server/src/main/java/org/apache/cayenne/graph/ChildDiffLoader.java
index 03a9268..03d86fa 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/graph/ChildDiffLoader.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/graph/ChildDiffLoader.java
@@ -75,10 +75,12 @@ public class ChildDiffLoader implements GraphChangeHandler {
 		this.context = context;
 	}
 
+	@Override
 	public void nodeIdChanged(Object nodeId, Object newId) {
 		throw new CayenneRuntimeException("Not supported");
 	}
 
+	@Override
 	public void nodeCreated(Object nodeId) {
 
 		setExternalChange(Boolean.TRUE);
@@ -108,6 +110,7 @@ public class ChildDiffLoader implements GraphChangeHandler {
 		}
 	}
 
+	@Override
 	public void nodeRemoved(Object nodeId) {
 		setExternalChange(Boolean.TRUE);
 		Persistent object = findObject(nodeId);
@@ -122,6 +125,7 @@ public class ChildDiffLoader implements GraphChangeHandler {
 		}
 	}
 
+	@Override
 	public void nodePropertyChanged(Object nodeId, String property, Object oldValue, Object newValue) {
 
 		// this change is for simple property, so no need to convert targets to
@@ -141,7 +145,8 @@ public class ChildDiffLoader implements GraphChangeHandler {
 		}
 	}
 
-	public void arcCreated(Object nodeId, Object targetNodeId, Object arcId) {
+	@Override
+	public void arcCreated(Object nodeId, Object targetNodeId, ArcId arcId) {
 
 		final Persistent source = findObject(nodeId);
 		final Persistent target = findObject(targetNodeId);
@@ -185,7 +190,8 @@ public class ChildDiffLoader implements GraphChangeHandler {
 		}
 	}
 
-	public void arcDeleted(Object nodeId, final Object targetNodeId, Object arcId) {
+	@Override
+	public void arcDeleted(Object nodeId, final Object targetNodeId, ArcId arcId) {
 		final Persistent source = findObject(nodeId);
 
 		// needed as sometime temporary objects are evoked from the context
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/graph/GraphChangeHandler.java b/cayenne-server/src/main/java/org/apache/cayenne/graph/GraphChangeHandler.java
index 3c92520..3d40d23 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/graph/GraphChangeHandler.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/graph/GraphChangeHandler.java
@@ -56,10 +56,10 @@ public interface GraphChangeHandler {
     /**
      * Notifies implementing object that a new arc was created between two nodes.
      */
-    void arcCreated(Object nodeId, Object targetNodeId, Object arcId);
+    void arcCreated(Object nodeId, Object targetNodeId, ArcId arcId);
 
     /**
      * Notifies implementing object that an arc between two nodes was deleted.
      */
-    void arcDeleted(Object nodeId, Object targetNodeId, Object arcId);
+    void arcDeleted(Object nodeId, Object targetNodeId, ArcId arcId);
 }
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/graph/GraphDiffCompressor.java b/cayenne-server/src/main/java/org/apache/cayenne/graph/GraphDiffCompressor.java
index 9bf1dc4..31849a8 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/graph/GraphDiffCompressor.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/graph/GraphDiffCompressor.java
@@ -93,7 +93,8 @@ public class GraphDiffCompressor {
             return new CompoundDiff(compressed);
         }
 
-        public void arcCreated(Object nodeId, Object targetNodeId, Object arcId) {
+        @Override
+        public void arcCreated(Object nodeId, Object targetNodeId, ArcId arcId) {
 
             if (targetNodeId != null) {
                 List<NodeDiff> diffs = diffsByNode.get(nodeId);
@@ -116,7 +117,8 @@ public class GraphDiffCompressor {
             registerDiff(new ArcCreateOperation(nodeId, targetNodeId, arcId));
         }
 
-        public void arcDeleted(Object nodeId, Object targetNodeId, Object arcId) {
+        @Override
+        public void arcDeleted(Object nodeId, Object targetNodeId, ArcId arcId) {
 
             if (targetNodeId != null) {
                 List<NodeDiff> diffs = diffsByNode.get(nodeId);
@@ -139,31 +141,35 @@ public class GraphDiffCompressor {
             registerDiff(new ArcDeleteOperation(nodeId, targetNodeId, arcId));
         }
 
+        @Override
         public void nodeCreated(Object nodeId) {
             registerDiff(new NodeCreateOperation(nodeId));
 
             if (createdNodes == null) {
-                createdNodes = new HashSet<Object>();
+                createdNodes = new HashSet<>();
             }
 
             createdNodes.add(nodeId);
         }
 
+        @Override
         public void nodeIdChanged(Object nodeId, Object newId) {
             registerDiff(new NodeIdChangeOperation(nodeId, newId));
         }
 
+        @Override
         public void nodeRemoved(Object nodeId) {
 
             registerDiff(new NodeDeleteOperation(nodeId));
 
             if (deletedNodes == null) {
-                deletedNodes = new HashSet<Object>();
+                deletedNodes = new HashSet<>();
             }
 
             deletedNodes.add(nodeId);
         }
 
+        @Override
         public void nodePropertyChanged(
                 Object nodeId,
                 String property,
@@ -195,13 +201,9 @@ public class GraphDiffCompressor {
         private void registerDiff(NodeDiff diff) {
 
             compressed.add(diff);
-            List<NodeDiff> diffs = diffsByNode.get(diff.getNodeId());
-            if (diffs == null) {
-                diffs = new ArrayList<>();
-                diffsByNode.put(diff.getNodeId(), diffs);
-            }
-
-            diffs.add(diff);
+            diffsByNode
+                    .computeIfAbsent(diff.getNodeId(), k -> new ArrayList<>())
+                    .add(diff);
         }
     }
 }
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/graph/GraphMap.java b/cayenne-server/src/main/java/org/apache/cayenne/graph/GraphMap.java
index cd4c1f6..f65934e 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/graph/GraphMap.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/graph/GraphMap.java
@@ -51,44 +51,54 @@ public class GraphMap implements GraphManager {
     /**
      * Returns an immutable collection of registered nodes.
      */
+    @Override
     public Collection<Object> registeredNodes() {
         return Collections.unmodifiableCollection(nodes.values());
     }
 
+    @Override
     public synchronized Object getNode(Object nodeId) {
         return nodes.get(nodeId);
     }
 
+    @Override
     public synchronized void registerNode(Object nodeId, Object nodeObject) {
         nodes.put(nodeId, nodeObject);
     }
 
+    @Override
     public synchronized Object unregisterNode(Object nodeId) {
         return nodes.remove(nodeId);
     }
 
     // *** methods for tracking local changes declared in GraphChangeHandler interface
-    
-    public void arcCreated(Object nodeId, Object targetNodeId, Object arcId) {
+
+    @Override
+    public void arcCreated(Object nodeId, Object targetNodeId, ArcId arcId) {
         // noop
     }
 
-    public void arcDeleted(Object nodeId, Object targetNodeId, Object arcId) {
+    @Override
+    public void arcDeleted(Object nodeId, Object targetNodeId, ArcId arcId) {
         // noop
     }
 
+    @Override
     public void nodeCreated(Object nodeId) {
         // noop
     }
 
+    @Override
     public void nodeRemoved(Object nodeId) {
         // noop
     }
 
+    @Override
     public void nodeIdChanged(Object nodeId, Object newId) {
         // noop
     }
 
+    @Override
     public void nodePropertyChanged(
             Object nodeId,
             String property,
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/util/ObjectContextGraphAction.java b/cayenne-server/src/main/java/org/apache/cayenne/util/ObjectContextGraphAction.java
index 7944c1b..01868df 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/util/ObjectContextGraphAction.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/util/ObjectContextGraphAction.java
@@ -23,6 +23,7 @@ import java.io.Serializable;
 import org.apache.cayenne.ObjectContext;
 import org.apache.cayenne.PersistenceState;
 import org.apache.cayenne.Persistent;
+import org.apache.cayenne.graph.ArcId;
 import org.apache.cayenne.reflect.ArcProperty;
 import org.apache.cayenne.reflect.ClassDescriptor;
 import org.apache.cayenne.reflect.PropertyDescriptor;
@@ -81,14 +82,14 @@ public class ObjectContextGraphAction implements Serializable {
                 context.getGraphManager().arcDeleted(
                         object.getObjectId(),
                         ((Persistent) oldValue).getObjectId(),
-                        property.getName());
+                        new ArcId(property));
             }
 
             if (newValue instanceof Persistent) {
                 context.getGraphManager().arcCreated(
                         object.getObjectId(),
                         ((Persistent) newValue).getObjectId(),
-                        property.getName());
+                        new ArcId(property));
             }
         }
     }
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/util/Util.java b/cayenne-server/src/main/java/org/apache/cayenne/util/Util.java
index 08941b8..99c0c3e 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/util/Util.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/util/Util.java
@@ -26,6 +26,7 @@ import org.apache.cayenne.Persistent;
 import org.apache.cayenne.di.AdhocObjectFactory;
 import org.apache.cayenne.di.spi.DefaultAdhocObjectFactory;
 import org.apache.cayenne.di.spi.DefaultClassLoaderManager;
+import org.apache.cayenne.graph.ArcId;
 import org.apache.cayenne.reflect.ArcProperty;
 import org.apache.cayenne.reflect.AttributeProperty;
 import org.apache.cayenne.reflect.PropertyVisitor;
@@ -741,7 +742,7 @@ public class Util {
 			});
 
 			sourceObject.getObjectContext().getGraphManager()
-					.arcCreated(targetObject.getObjectId(), sourceObject.getObjectId(), reverseArc.getName());
+					.arcCreated(targetObject.getObjectId(), sourceObject.getObjectId(), new ArcId(reverseArc));
 
 			markAsDirty(targetObject);
 		}
@@ -771,7 +772,7 @@ public class Util {
 			});
 
 			sourceObject.getObjectContext().getGraphManager()
-					.arcDeleted(targetObject.getObjectId(), sourceObject.getObjectId(), reverseArc.getName());
+					.arcDeleted(targetObject.getObjectId(), sourceObject.getObjectId(), new ArcId(reverseArc));
 
 			markAsDirty(targetObject);
 		}
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/access/DeleteRulesIT.java b/cayenne-server/src/test/java/org/apache/cayenne/access/DeleteRulesIT.java
index 430c28d..6e7d821 100644
--- a/cayenne-server/src/test/java/org/apache/cayenne/access/DeleteRulesIT.java
+++ b/cayenne-server/src/test/java/org/apache/cayenne/access/DeleteRulesIT.java
@@ -338,16 +338,15 @@ public class DeleteRulesIT extends ServerCase {
         ObjectDiff changes = context.getObjectStore().changes.get(a.getObjectId());
 
         assertNotNull(changes);
-        Collection<NodeDiff> diffs = new ArrayList<NodeDiff>();
+        Collection<NodeDiff> diffs = new ArrayList<>();
         changes.appendDiffs(diffs);
-        Iterator<?> it = diffs.iterator();
-        while (it.hasNext()) {
-            Object diff = it.next();
+        for (Object diff : diffs) {
             if (diff instanceof ArcOperation) {
                 ArcOperation arcDelete = (ArcOperation) diff;
                 if (arcDelete.getNodeId().equals(a.getObjectId())
                         && arcDelete.getTargetNodeId().equals(b.getObjectId())
-                        && arcDelete.getArcId().equals(DeleteRuleFlatA.FLAT_B.getName()) && arcDelete.isDelete()) {
+                        && arcDelete.getArcId().getForwardArc().equals(DeleteRuleFlatA.FLAT_B.getName())
+                        && arcDelete.isDelete()) {
                     return;
                 }
             }
@@ -360,16 +359,15 @@ public class DeleteRulesIT extends ServerCase {
         ObjectDiff changes = context.getObjectStore().changes.get(a.getObjectId());
 
         if (changes != null) {
-            Collection<NodeDiff> diffs = new ArrayList<NodeDiff>();
+            Collection<NodeDiff> diffs = new ArrayList<>();
             changes.appendDiffs(diffs);
-            Iterator<?> it = diffs.iterator();
-            while (it.hasNext()) {
-                Object diff = it.next();
+            for (Object diff : diffs) {
                 if (diff instanceof ArcOperation) {
                     ArcOperation arcDelete = (ArcOperation) diff;
                     if (arcDelete.getNodeId().equals(a.getObjectId())
                             && arcDelete.getTargetNodeId().equals(b.getObjectId())
-                            && arcDelete.getArcId().equals(DeleteRuleFlatA.FLAT_B.getName()) && !arcDelete.isDelete()) {
+                            && arcDelete.getArcId().getForwardArc().equals(DeleteRuleFlatA.FLAT_B.getName())
+                            && !arcDelete.isDelete()) {
                         fail("Join was  deleted for flattened relationship");
                     }
                 }
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/unit/di/DataChannelSyncStats.java b/cayenne-server/src/test/java/org/apache/cayenne/unit/di/DataChannelSyncStats.java
index 4b2e49c..cbbf48b 100644
--- a/cayenne-server/src/test/java/org/apache/cayenne/unit/di/DataChannelSyncStats.java
+++ b/cayenne-server/src/test/java/org/apache/cayenne/unit/di/DataChannelSyncStats.java
@@ -18,6 +18,7 @@
  ****************************************************************/
 package org.apache.cayenne.unit.di;
 
+import org.apache.cayenne.graph.ArcId;
 import org.apache.cayenne.graph.GraphChangeHandler;
 
 public class DataChannelSyncStats implements GraphChangeHandler {
@@ -29,22 +30,27 @@ public class DataChannelSyncStats implements GraphChangeHandler {
     public int nodePropertiesChanged;
     public int nodesRemoved;
 
-    public void arcCreated(Object nodeId, Object targetNodeId, Object arcId) {
+    @Override
+    public void arcCreated(Object nodeId, Object targetNodeId, ArcId arcId) {
         arcsCreated++;
     }
 
-    public void arcDeleted(Object nodeId, Object targetNodeId, Object arcId) {
+    @Override
+    public void arcDeleted(Object nodeId, Object targetNodeId, ArcId arcId) {
         arcsDeleted++;
     }
 
+    @Override
     public void nodeCreated(Object nodeId) {
         nodesCreated++;
     }
 
+    @Override
     public void nodeIdChanged(Object nodeId, Object newId) {
         nodeIdsChanged++;
     }
 
+    @Override
     public void nodePropertyChanged(
             Object nodeId,
             String property,
@@ -53,6 +59,7 @@ public class DataChannelSyncStats implements GraphChangeHandler {
         nodePropertiesChanged++;
     }
 
+    @Override
     public void nodeRemoved(Object nodeId) {
         nodesRemoved++;
     }