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/21 14:42:19 UTC

[cayenne] 01/02: CAY-2582 Double insert of manyToMany relationship mapped to Set

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 21167714f4400ac6d4b3a905c595e4dd0f1c4450
Author: Nikita Timofeev <st...@gmail.com>
AuthorDate: Tue May 21 17:08:06 2019 +0300

    CAY-2582 Double insert of manyToMany relationship mapped to Set
---
 .../org/apache/cayenne/access/ToManyListFault.java |   2 +-
 .../java/org/apache/cayenne/access/ToManySet.java  |  72 +++++++++++++
 .../org/apache/cayenne/access/ToManySetFault.java  |   3 +-
 .../apache/cayenne/util/PersistentObjectList.java  |   6 +-
 .../apache/cayenne/util/PersistentObjectMap.java   |   2 +-
 .../apache/cayenne/util/PersistentObjectSet.java   | 115 +++++++++++++--------
 6 files changed, 150 insertions(+), 50 deletions(-)

diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/ToManyListFault.java b/cayenne-server/src/main/java/org/apache/cayenne/access/ToManyListFault.java
index ab8c4c5..6aee149 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/access/ToManyListFault.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/access/ToManyListFault.java
@@ -31,6 +31,6 @@ public class ToManyListFault extends Fault {
      */
     @Override
     public Object resolveFault(Persistent sourceObject, String relationshipName) {
-        return new ToManyList(sourceObject, relationshipName);
+        return new ToManyList<>(sourceObject, relationshipName);
     }
 }
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/ToManySet.java b/cayenne-server/src/main/java/org/apache/cayenne/access/ToManySet.java
new file mode 100644
index 0000000..42c71ca
--- /dev/null
+++ b/cayenne-server/src/main/java/org/apache/cayenne/access/ToManySet.java
@@ -0,0 +1,72 @@
+/*****************************************************************
+ *   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.access;
+
+import java.io.Serializable;
+import java.util.Collection;
+import java.util.List;
+
+import org.apache.cayenne.PersistenceState;
+import org.apache.cayenne.Persistent;
+import org.apache.cayenne.util.PersistentObjectSet;
+
+public class ToManySet<E> extends PersistentObjectSet<E> implements Serializable {
+
+    protected ToManySet(Persistent relationshipOwner, String relationshipName) {
+        super(relationshipOwner, relationshipName);
+    }
+
+    @Override
+    protected boolean shouldAddToRemovedFromUnresolvedSet(E object) {
+        // No point in adding a new or transient object -- these will never be fetched
+        // from the database.
+        if (object instanceof Persistent) {
+            Persistent dataObject = (Persistent) object;
+            return (dataObject.getPersistenceState() != PersistenceState.TRANSIENT)
+                    && (dataObject.getPersistenceState() != PersistenceState.NEW);
+        }
+        return true;
+    }
+
+    @Override
+    protected void postprocessAdd(Collection<? extends E> collection) {
+        // no need for this operation for DataObjects...
+    }
+
+    @Override
+    protected void postprocessRemove(Collection<? extends E> collection) {
+        // no need for this operation for DataObjects...
+    }
+
+    @Override
+    protected void postprocessAdd(E addedObject) {
+        // no need for this operation for DataObjects...
+    }
+
+    @Override
+    protected void postprocessRemove(E removedObject) {
+        // no need for this operation for DataObjects...
+    }
+
+    @Override
+    protected void updateReverse(List<E> resolved) {
+        // no need for this operation for DataObjects...
+    }
+}
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/ToManySetFault.java b/cayenne-server/src/main/java/org/apache/cayenne/access/ToManySetFault.java
index 31e198b..889e98f 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/access/ToManySetFault.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/access/ToManySetFault.java
@@ -20,7 +20,6 @@ package org.apache.cayenne.access;
 
 import org.apache.cayenne.Fault;
 import org.apache.cayenne.Persistent;
-import org.apache.cayenne.util.PersistentObjectSet;
 
 /**
  * @since 3.0
@@ -29,7 +28,7 @@ public class ToManySetFault extends Fault {
 
     @Override
     public Object resolveFault(Persistent sourceObject, String relationshipName) {
-        return new PersistentObjectSet(sourceObject, relationshipName);
+        return new ToManySet<>(sourceObject, relationshipName);
     }
 
 }
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/util/PersistentObjectList.java b/cayenne-server/src/main/java/org/apache/cayenne/util/PersistentObjectList.java
index bf6d461..610859a 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/util/PersistentObjectList.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/util/PersistentObjectList.java
@@ -261,7 +261,7 @@ public class PersistentObjectList<E> extends RelationshipFault<E> implements Lis
             // TODO: here we assume that all objects were removed,
             // while removeAll may technically return true and remove only some objects...
             // need a smarter approach
-            postprocessRemove((E)c);
+            postprocessRemove((Collection<? extends E>)c);
             return true;
         }
 
@@ -269,7 +269,7 @@ public class PersistentObjectList<E> extends RelationshipFault<E> implements Lis
     }
 
     @Override
-    public boolean retainAll(Collection c) {
+    public boolean retainAll(Collection<?> c) {
         // TODO: handle object graoh change notifications on object removals...
         return resolvedObjectList().retainAll(c);
     }
@@ -408,7 +408,7 @@ public class PersistentObjectList<E> extends RelationshipFault<E> implements Lis
      * @return whether object should be added to {@link #removedFromUnresolved} during
      *         removal
      */
-    protected boolean shouldAddToRemovedFromUnresolvedList(Object object) {
+    protected boolean shouldAddToRemovedFromUnresolvedList(E object) {
         return true;
     }
 
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/util/PersistentObjectMap.java b/cayenne-server/src/main/java/org/apache/cayenne/util/PersistentObjectMap.java
index 9d56f42..ed7ffcf 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/util/PersistentObjectMap.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/util/PersistentObjectMap.java
@@ -93,7 +93,7 @@ public class PersistentObjectMap extends RelationshipFault implements Map, Value
 
     public Object setValue(Object value) throws CayenneRuntimeException {
         resolvedObjectMap();
-        return setValueDirectly(objectMap);
+        return setValueDirectly(value);
     }
 
     public Object setValueDirectly(Object value) throws CayenneRuntimeException {
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/util/PersistentObjectSet.java b/cayenne-server/src/main/java/org/apache/cayenne/util/PersistentObjectSet.java
index 9d31eb8..28d6093 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/util/PersistentObjectSet.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/util/PersistentObjectSet.java
@@ -32,15 +32,15 @@ import org.apache.cayenne.ValueHolder;
 /**
  * @since 3.0
  */
-public class PersistentObjectSet extends RelationshipFault 
-    implements Set, ValueHolder, PersistentObjectCollection {
+public class PersistentObjectSet<E> extends RelationshipFault<E>
+    implements Set<E>, ValueHolder<Object>, PersistentObjectCollection<E> {
 
     // wrapped objects set
-    protected Set objectSet;
+    protected Set<E> objectSet;
 
     // track additions/removals in unresolved...
-    protected Set addedToUnresolved;
-    protected Set removedFromUnresolved;
+    protected Set<E> addedToUnresolved;
+    protected Set<E> removedFromUnresolved;
 
     // exists for the benefit of manual serialization schemes such as the one in Hessian.
     private PersistentObjectSet() {
@@ -53,6 +53,7 @@ public class PersistentObjectSet extends RelationshipFault
     /**
      * Returns whether this list is not yet resolved and requires a fetch.
      */
+    @Override
     public boolean isFault() {
 
         if (objectSet != null) {
@@ -62,7 +63,7 @@ public class PersistentObjectSet extends RelationshipFault
         // object may be in an inconsistent state during construction time
         // synchronize??
         else if (isTransientParent()) {
-            objectSet = new HashSet();
+            objectSet = new HashSet<>();
             return false;
         }
         else {
@@ -73,19 +74,21 @@ public class PersistentObjectSet extends RelationshipFault
     /**
      * Turns itself into a fault, thus forcing a refresh on the next access.
      */
+    @Override
     public void invalidate() {
         setObjectSet(null);
     }
 
+    @Override
     public Object setValueDirectly(Object value) throws CayenneRuntimeException {
-        Object old = this.objectSet;
+        Set<E> old = this.objectSet;
 
         if (value == null || value instanceof Set) {
-            setObjectSet((Set) value);
+            setObjectSet((Set<E>) value);
         } else if (value instanceof Collection) {
             // we can wrap non-set collections on the fly - this is needed for prefetch handling...
             // although it seems to be breaking the contract for 'setValueDirectly' ???
-            setObjectSet(new HashSet((Collection) value));
+            setObjectSet(new HashSet<>((Collection<E>) value));
         } else {
             throw new CayenneRuntimeException("Value must be a list, got: %s", value.getClass().getName());
         }
@@ -93,20 +96,22 @@ public class PersistentObjectSet extends RelationshipFault
         return old;
     }
 
-    public Object getValue() throws CayenneRuntimeException {
+    @Override
+    public Set<E> getValue() throws CayenneRuntimeException {
         return resolvedObjectSet();
     }
 
-    public Object getValueDirectly() throws CayenneRuntimeException {
+    @Override
+    public Set<E> getValueDirectly() throws CayenneRuntimeException {
         return objectSet;
     }
 
     public Object setValue(Object value) throws CayenneRuntimeException {
         resolvedObjectSet();
-        return setValueDirectly(objectSet);
+        return setValueDirectly(value);
     }
 
-    public void setObjectSet(Set objectSet) {
+    public void setObjectSet(Set<E> objectSet) {
         this.objectSet = objectSet;
     }
 
@@ -114,7 +119,8 @@ public class PersistentObjectSet extends RelationshipFault
     // Standard Set Methods.
     // ====================================================
 
-    public boolean add(Object o) {
+    @Override
+    public boolean add(E o) {
         if ((isFault()) ? addLocal(o) : objectSet.add(o)) {
             postprocessAdd(o);
             return true;
@@ -123,7 +129,8 @@ public class PersistentObjectSet extends RelationshipFault
         return false;
     }
 
-    public boolean addAll(Collection c) {
+    @Override
+    public boolean addAll(Collection<? extends E> c) {
         if (resolvedObjectSet().addAll(c)) {
             // TODO: here we assume that all objects were added, while addAll may
             // technically return true and add only some objects... need a smarter
@@ -136,16 +143,19 @@ public class PersistentObjectSet extends RelationshipFault
         return false;
     }
 
+    @Override
     public void clear() {
-        Set resolved = resolvedObjectSet();
+        Set<E> resolved = resolvedObjectSet();
         postprocessRemove(resolved);
         resolved.clear();
     }
 
+    @Override
     public boolean contains(Object o) {
         return resolvedObjectSet().contains(o);
     }
 
+    @Override
     public boolean containsAll(Collection c) {
         return resolvedObjectSet().containsAll(c);
     }
@@ -168,38 +178,46 @@ public class PersistentObjectSet extends RelationshipFault
         return 53 + resolvedObjectSet().hashCode();
     }
 
+    @Override
     public boolean isEmpty() {
         return resolvedObjectSet().isEmpty();
     }
 
-    public Iterator iterator() {
+    @Override
+    public Iterator<E> iterator() {
         return resolvedObjectSet().iterator();
     }
 
+    @SuppressWarnings("unchecked")
+    @Override
     public boolean remove(Object o) {
-        if ((isFault()) ? removeLocal(o) : objectSet.remove(o)) {
-            postprocessRemove(o);
+        E object = (E) o;
+        if ((isFault()) ? removeLocal(object) : objectSet.remove(o)) {
+            postprocessRemove(object);
             return true;
         }
 
         return false;
     }
 
-    public boolean removeAll(Collection c) {
+    @SuppressWarnings("unchecked")
+    @Override
+    public boolean removeAll(Collection<?> c) {
         if (resolvedObjectSet().removeAll(c)) {
             // TODO: here we assume that all objects were removed, while removeAll may
             // technically return true and remove only some objects... need a smarter
             // approach
-            postprocessRemove(c);
+            postprocessRemove((Collection<? extends E>) c);
             return true;
         }
 
         return false;
     }
 
-    public boolean retainAll(Collection c) {
-    	Collection toRemove = new HashSet(resolvedObjectSet().size());
-    	for (Object object : resolvedObjectSet()) {
+    @Override
+    public boolean retainAll(Collection<?> c) {
+    	Collection<E> toRemove = new HashSet<>(resolvedObjectSet().size());
+    	for (E object : resolvedObjectSet()) {
 			if (!c.contains(object)) {
 				toRemove.add(object);
 			}
@@ -212,15 +230,18 @@ public class PersistentObjectSet extends RelationshipFault
         return result;
     }
 
+    @Override
     public int size() {
         return resolvedObjectSet().size();
     }
 
+    @Override
     public Object[] toArray() {
         return resolvedObjectSet().toArray();
     }
 
-    public Object[] toArray(Object[] a) {
+    @Override
+    public <T> T[] toArray(T[] a) {
         return resolvedObjectSet().toArray(a);
     }
 
@@ -232,7 +253,7 @@ public class PersistentObjectSet extends RelationshipFault
     /**
      * Returns internal objects list resolving it if needed.
      */
-    protected Set resolvedObjectSet() {
+    protected Set<E> resolvedObjectSet() {
         if (isFault()) {
 
             synchronized (this) {
@@ -240,8 +261,8 @@ public class PersistentObjectSet extends RelationshipFault
                 // now that we obtained the lock, check
                 // if another thread just resolved the list
                 if (isFault()) {
-                    List localList = resolveFromDB();
-                    this.objectSet = new HashSet(localList);
+                    List<E> localList = resolveFromDB();
+                    this.objectSet = new HashSet<>(localList);
                 }
             }
         }
@@ -255,7 +276,7 @@ public class PersistentObjectSet extends RelationshipFault
     }
     
     @Override
-    protected void mergeLocalChanges(List resolved) {
+    protected void mergeLocalChanges(List<E> resolved) {
 
         // only merge if an object is in an uncommitted state
         // any other state means that our local tracking
@@ -270,7 +291,7 @@ public class PersistentObjectSet extends RelationshipFault
             // do not include transient objects...
             if (addedToUnresolved != null) {
 
-                for (Object next : addedToUnresolved) {
+                for (E next : addedToUnresolved) {
 
                     if (next instanceof Persistent) {
                         Persistent dataObject = (Persistent) next;
@@ -290,14 +311,14 @@ public class PersistentObjectSet extends RelationshipFault
         clearLocalChanges();
     }
 
-    boolean addLocal(Object object) {
+    boolean addLocal(E object) {
 
         if (removedFromUnresolved != null) {
             removedFromUnresolved.remove(object);
         }
 
         if (addedToUnresolved == null) {
-            addedToUnresolved = new HashSet();
+            addedToUnresolved = new HashSet<>();
         }
 
         addedToUnresolved.add(object);
@@ -307,35 +328,37 @@ public class PersistentObjectSet extends RelationshipFault
         return true;
     }
 
-    boolean removeLocal(Object object) {
+    boolean removeLocal(E object) {
         if (addedToUnresolved != null) {
             addedToUnresolved.remove(object);
         }
 
         if (removedFromUnresolved == null) {
-            removedFromUnresolved = new HashSet();
+            removedFromUnresolved = new HashSet<>();
         }
 
-        removedFromUnresolved.add(object);
+        if (shouldAddToRemovedFromUnresolvedSet(object)) {
+            removedFromUnresolved.add(object);
+        }
 
         // this is really meaningless, since we don't know
         // if an object was present in the list
         return true;
     }
 
-    void postprocessAdd(Collection<?> collection) {
-        for (Object next : collection) {
+    protected void postprocessAdd(Collection<? extends E> collection) {
+        for (E next : collection) {
             postprocessAdd(next);
         }
     }
 
-    void postprocessRemove(Collection<?> collection) {
-        for (Object next : collection) {
+    protected void postprocessRemove(Collection<? extends E> collection) {
+        for (E next : collection) {
             postprocessRemove(next);
         }
     }
 
-    void postprocessAdd(Object addedObject) {
+    protected void postprocessAdd(E addedObject) {
 
         // notify ObjectContext
         if (relationshipOwner.getObjectContext() != null) {
@@ -351,7 +374,7 @@ public class PersistentObjectSet extends RelationshipFault
         }
     }
 
-    void postprocessRemove(Object removedObject) {
+    protected void postprocessRemove(E removedObject) {
 
         // notify ObjectContext
         if (relationshipOwner.getObjectContext() != null) {
@@ -367,12 +390,17 @@ public class PersistentObjectSet extends RelationshipFault
         }
     }
 
+    protected boolean shouldAddToRemovedFromUnresolvedSet(E object) {
+        return true;
+    }
+
     @Override
     public String toString() {
         return (objectSet != null) ? objectSet.toString() : "[<unresolved>]";
     }
 
-    public void addDirectly(Object target) {
+    @Override
+    public void addDirectly(E target) {
         if (isFault()) {
             addLocal(target);
         }
@@ -381,7 +409,8 @@ public class PersistentObjectSet extends RelationshipFault
         }
     }
 
-    public void removeDirectly(Object target) {
+    @Override
+    public void removeDirectly(E target) {
         if (isFault()) {
             removeLocal(target);
         }