You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cayenne.apache.org by ab...@apache.org on 2019/06/07 14:50:17 UTC
[cayenne] 02/02: CAY-2582 Double insert of manyToMany relationship
mapped to Set
This is an automated email from the ASF dual-hosted git repository.
abulatski pushed a commit to branch STABLE-4.0
in repository https://gitbox.apache.org/repos/asf/cayenne.git
commit 60b16868953607ad9e4eb67f5843ac93aec44376
Author: Arseni Bulatski <an...@gmail.com>
AuthorDate: Fri Jun 7 17:48:36 2019 +0300
CAY-2582 Double insert of manyToMany relationship mapped to Set
---
RELEASE-NOTES.txt | 1 +
.../java/org/apache/cayenne/access/ToManySet.java | 72 +++++++++++++++++++++
.../org/apache/cayenne/access/ToManySetFault.java | 3 +-
.../apache/cayenne/util/PersistentObjectSet.java | 12 ++--
.../java/org/apache/cayenne/ManyToManyJoinIT.java | 66 +++++++++++++++++--
.../relationships_many_to_many_join/Author.java | 18 ++++++
.../relationships_many_to_many_join/Song.java | 18 ++++++
.../auto/_Author.java | 67 ++-----------------
.../auto/_Song.java | 75 ++--------------------
.../cayenne-relationships-many-to-many-join.xml | 5 +-
.../relationships-many-to-many-join.map.xml | 10 +--
11 files changed, 194 insertions(+), 153 deletions(-)
diff --git a/RELEASE-NOTES.txt b/RELEASE-NOTES.txt
index ece7e32..40100e7 100644
--- a/RELEASE-NOTES.txt
+++ b/RELEASE-NOTES.txt
@@ -22,6 +22,7 @@ Bug Fixes:
CAY-2550 Modeler: ObjAttribute inspector modifies wrong columns in attribute table
CAY-2559 Modeler: Warning dialog shows wrong information after changing target entity in dbRelationship
CAY-2573 DI field injection is triggered when creating sql Driver
+CAY-2582 Double insert of manyToMany relationship mapped to Set
CAY-2584 Crypto: can't use ColumnSelect with encrypted columns
----------------------------------
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..6dcfbcc
--- /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 extends PersistentObjectSet implements Serializable {
+
+ protected ToManySet(Persistent relationshipOwner, String relationshipName) {
+ super(relationshipOwner, relationshipName);
+ }
+
+ @Override
+ protected boolean shouldAddToRemovedFromUnresolvedSet(Object 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<?> collection) {
+ // no need for this operation for DataObjects...
+ }
+
+ @Override
+ protected void postprocessRemove(Collection<?> collection) {
+ // no need for this operation for DataObjects...
+ }
+
+ @Override
+ protected void postprocessAdd(Object addedObject) {
+ // no need for this operation for DataObjects...
+ }
+
+ @Override
+ protected void postprocessRemove(Object removedObject) {
+ // no need for this operation for DataObjects...
+ }
+
+ @Override
+ protected void updateReverse(List 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 44e2e10..4114b0d 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/PersistentObjectSet.java b/cayenne-server/src/main/java/org/apache/cayenne/util/PersistentObjectSet.java
index 0275e5a..c3e4242 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
@@ -110,6 +110,10 @@ public class PersistentObjectSet extends RelationshipFault
this.objectSet = objectSet;
}
+ protected boolean shouldAddToRemovedFromUnresolvedSet(Object object) {
+ return true;
+ }
+
// ====================================================
// Standard Set Methods.
// ====================================================
@@ -323,19 +327,19 @@ public class PersistentObjectSet extends RelationshipFault
return true;
}
- void postprocessAdd(Collection<?> collection) {
+ protected void postprocessAdd(Collection<?> collection) {
for (Object next : collection) {
postprocessAdd(next);
}
}
- void postprocessRemove(Collection<?> collection) {
+ protected void postprocessRemove(Collection<?> collection) {
for (Object next : collection) {
postprocessRemove(next);
}
}
- void postprocessAdd(Object addedObject) {
+ protected void postprocessAdd(Object addedObject) {
// notify ObjectContext
if (relationshipOwner.getObjectContext() != null) {
@@ -351,7 +355,7 @@ public class PersistentObjectSet extends RelationshipFault
}
}
- void postprocessRemove(Object removedObject) {
+ protected void postprocessRemove(Object removedObject) {
// notify ObjectContext
if (relationshipOwner.getObjectContext() != null) {
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/ManyToManyJoinIT.java b/cayenne-server/src/test/java/org/apache/cayenne/ManyToManyJoinIT.java
index 0949b5e..b8b25e8 100644
--- a/cayenne-server/src/test/java/org/apache/cayenne/ManyToManyJoinIT.java
+++ b/cayenne-server/src/test/java/org/apache/cayenne/ManyToManyJoinIT.java
@@ -18,9 +18,13 @@
****************************************************************/
package org.apache.cayenne;
-import static org.junit.Assert.*;
+import java.util.concurrent.atomic.AtomicInteger;
+import org.apache.cayenne.access.DataDomain;
+import org.apache.cayenne.configuration.server.ServerRuntime;
import org.apache.cayenne.di.Inject;
+import org.apache.cayenne.graph.GraphChangeHandler;
+import org.apache.cayenne.graph.GraphDiff;
import org.apache.cayenne.testdo.relationships_many_to_many_join.Author;
import org.apache.cayenne.testdo.relationships_many_to_many_join.Song;
import org.apache.cayenne.unit.di.server.CayenneProjects;
@@ -28,24 +32,78 @@ import org.apache.cayenne.unit.di.server.ServerCase;
import org.apache.cayenne.unit.di.server.UseServerRuntime;
import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
@UseServerRuntime(CayenneProjects.RELATIONSHIPS_MANY_TO_MANY_JOIN_PROJECT)
public class ManyToManyJoinIT extends ServerCase {
@Inject
+ private ServerRuntime serverRuntime;
+
+ @Inject
private ObjectContext context;
@Test
- public void testManyToManyJoinWithFlattenedRelationship() throws Exception {
+ public void testManyToManyJoinWithFlattenedRelationship() {
Author author = context.newObject(Author.class);
author.setName("Bob Dylan");
-
+
Song song = context.newObject(Song.class);
song.setName("House of the Rising Sun");
song.addToAuthors(author);
-
+
context.commitChanges();
assertEquals(author, song.getAuthors().iterator().next());
}
+ @Test
+ public void testManyToManyJoinWithFlattenedRelationshipEvents() {
+ DataDomain domain = serverRuntime.getDataDomain();
+ DataChannelFilter f1 = new MockDataChannelFilter() {
+ @Override
+ public GraphDiff onSync(
+ ObjectContext originatingContext,
+ GraphDiff changes,
+ int syncType,
+ DataChannelFilterChain filterChain) {
+
+ GraphDiff response = filterChain.onSync(
+ originatingContext,
+ changes,
+ syncType);
+ final AtomicInteger count = new AtomicInteger(0);
+ changes.apply(new GraphChangeHandler() {
+ @Override
+ public void nodeIdChanged(Object nodeId, Object newId) {}
+
+ @Override
+ public void nodeCreated(Object nodeId) {}
+
+ @Override
+ public void nodeRemoved(Object nodeId) {}
+
+ @Override
+ public void nodePropertyChanged(Object nodeId, String property, Object oldValue, Object newValue) {}
+
+ @Override
+ public void arcCreated(Object nodeId, Object targetNodeId, Object arcId) {
+ count.incrementAndGet();
+ }
+
+ @Override
+ public void arcDeleted(Object nodeId, Object targetNodeId, Object arcId) {}
+ });
+ assertEquals(1, count.get());
+ return response;
+ }
+ };
+ domain.addFilter(f1);
+ Author author = context.newObject(Author.class);
+ author.setName("Bob Dylan");
+ Song song = context.newObject(Song.class);
+ song.setName("House of the Rising Sun");
+ song.addToAuthors(author);
+ context.commitChanges();
+ }
}
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/testdo/relationships_many_to_many_join/Author.java b/cayenne-server/src/test/java/org/apache/cayenne/testdo/relationships_many_to_many_join/Author.java
index a50741e..e516b70 100644
--- a/cayenne-server/src/test/java/org/apache/cayenne/testdo/relationships_many_to_many_join/Author.java
+++ b/cayenne-server/src/test/java/org/apache/cayenne/testdo/relationships_many_to_many_join/Author.java
@@ -1,3 +1,21 @@
+/*****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ ****************************************************************/
package org.apache.cayenne.testdo.relationships_many_to_many_join;
import org.apache.cayenne.testdo.relationships_many_to_many_join.auto._Author;
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/testdo/relationships_many_to_many_join/Song.java b/cayenne-server/src/test/java/org/apache/cayenne/testdo/relationships_many_to_many_join/Song.java
index 8df7056..abf7b8a 100644
--- a/cayenne-server/src/test/java/org/apache/cayenne/testdo/relationships_many_to_many_join/Song.java
+++ b/cayenne-server/src/test/java/org/apache/cayenne/testdo/relationships_many_to_many_join/Song.java
@@ -1,3 +1,21 @@
+/*****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ ****************************************************************/
package org.apache.cayenne.testdo.relationships_many_to_many_join;
import org.apache.cayenne.testdo.relationships_many_to_many_join.auto._Song;
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/testdo/relationships_many_to_many_join/auto/_Author.java b/cayenne-server/src/test/java/org/apache/cayenne/testdo/relationships_many_to_many_join/auto/_Author.java
index 11554d9..999a0a6 100644
--- a/cayenne-server/src/test/java/org/apache/cayenne/testdo/relationships_many_to_many_join/auto/_Author.java
+++ b/cayenne-server/src/test/java/org/apache/cayenne/testdo/relationships_many_to_many_join/auto/_Author.java
@@ -1,10 +1,6 @@
package org.apache.cayenne.testdo.relationships_many_to_many_join.auto;
-import java.io.IOException;
-import java.io.ObjectInputStream;
-import java.io.ObjectOutputStream;
-
-import org.apache.cayenne.BaseDataObject;
+import org.apache.cayenne.CayenneDataObject;
import org.apache.cayenne.exp.Property;
/**
@@ -13,7 +9,7 @@ import org.apache.cayenne.exp.Property;
* since it may be overwritten next time code is regenerated.
* If you need to make any customizations, please use subclass.
*/
-public abstract class _Author extends BaseDataObject {
+public abstract class _Author extends CayenneDataObject {
private static final long serialVersionUID = 1L;
@@ -21,66 +17,11 @@ public abstract class _Author extends BaseDataObject {
public static final Property<String> NAME = Property.create("name", String.class);
- protected String name;
-
-
public void setName(String name) {
- beforePropertyWrite("name", this.name, name);
- this.name = name;
+ writeProperty("name", name);
}
-
public String getName() {
- beforePropertyRead("name");
- return this.name;
- }
-
- @Override
- public Object readPropertyDirectly(String propName) {
- if(propName == null) {
- throw new IllegalArgumentException();
- }
-
- switch(propName) {
- case "name":
- return this.name;
- default:
- return super.readPropertyDirectly(propName);
- }
- }
-
- @Override
- public void writePropertyDirectly(String propName, Object val) {
- if(propName == null) {
- throw new IllegalArgumentException();
- }
-
- switch (propName) {
- case "name":
- this.name = (String)val;
- break;
- default:
- super.writePropertyDirectly(propName, val);
- }
- }
-
- private void writeObject(ObjectOutputStream out) throws IOException {
- writeSerialized(out);
- }
-
- private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
- readSerialized(in);
- }
-
- @Override
- protected void writeState(ObjectOutputStream out) throws IOException {
- super.writeState(out);
- out.writeObject(this.name);
- }
-
- @Override
- protected void readState(ObjectInputStream in) throws IOException, ClassNotFoundException {
- super.readState(in);
- this.name = (String)in.readObject();
+ return (String)readProperty("name");
}
}
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/testdo/relationships_many_to_many_join/auto/_Song.java b/cayenne-server/src/test/java/org/apache/cayenne/testdo/relationships_many_to_many_join/auto/_Song.java
index 15160ca..c639c9f 100644
--- a/cayenne-server/src/test/java/org/apache/cayenne/testdo/relationships_many_to_many_join/auto/_Song.java
+++ b/cayenne-server/src/test/java/org/apache/cayenne/testdo/relationships_many_to_many_join/auto/_Song.java
@@ -1,11 +1,8 @@
package org.apache.cayenne.testdo.relationships_many_to_many_join.auto;
-import java.io.IOException;
-import java.io.ObjectInputStream;
-import java.io.ObjectOutputStream;
import java.util.Set;
-import org.apache.cayenne.BaseDataObject;
+import org.apache.cayenne.CayenneDataObject;
import org.apache.cayenne.exp.Property;
import org.apache.cayenne.testdo.relationships_many_to_many_join.Author;
@@ -15,7 +12,7 @@ import org.apache.cayenne.testdo.relationships_many_to_many_join.Author;
* since it may be overwritten next time code is regenerated.
* If you need to make any customizations, please use subclass.
*/
-public abstract class _Song extends BaseDataObject {
+public abstract class _Song extends CayenneDataObject {
private static final long serialVersionUID = 1L;
@@ -24,87 +21,23 @@ public abstract class _Song extends BaseDataObject {
public static final Property<String> NAME = Property.create("name", String.class);
public static final Property<Set<Author>> AUTHORS = Property.create("authors", Set.class);
- protected String name;
-
- protected Object authors;
-
public void setName(String name) {
- beforePropertyWrite("name", this.name, name);
- this.name = name;
+ writeProperty("name", name);
}
-
public String getName() {
- beforePropertyRead("name");
- return this.name;
+ return (String)readProperty("name");
}
public void addToAuthors(Author obj) {
addToManyTarget("authors", obj, true);
}
-
public void removeFromAuthors(Author obj) {
removeToManyTarget("authors", obj, true);
}
-
@SuppressWarnings("unchecked")
public Set<Author> getAuthors() {
return (Set<Author>)readProperty("authors");
}
- @Override
- public Object readPropertyDirectly(String propName) {
- if(propName == null) {
- throw new IllegalArgumentException();
- }
-
- switch(propName) {
- case "name":
- return this.name;
- case "authors":
- return this.authors;
- default:
- return super.readPropertyDirectly(propName);
- }
- }
-
- @Override
- public void writePropertyDirectly(String propName, Object val) {
- if(propName == null) {
- throw new IllegalArgumentException();
- }
-
- switch (propName) {
- case "name":
- this.name = (String)val;
- break;
- case "authors":
- this.authors = val;
- break;
- default:
- super.writePropertyDirectly(propName, val);
- }
- }
-
- private void writeObject(ObjectOutputStream out) throws IOException {
- writeSerialized(out);
- }
-
- private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
- readSerialized(in);
- }
-
- @Override
- protected void writeState(ObjectOutputStream out) throws IOException {
- super.writeState(out);
- out.writeObject(this.name);
- out.writeObject(this.authors);
- }
-
- @Override
- protected void readState(ObjectInputStream in) throws IOException, ClassNotFoundException {
- super.readState(in);
- this.name = (String)in.readObject();
- this.authors = in.readObject();
- }
}
diff --git a/cayenne-server/src/test/resources/cayenne-relationships-many-to-many-join.xml b/cayenne-server/src/test/resources/cayenne-relationships-many-to-many-join.xml
index 0121356..d93fe1c 100644
--- a/cayenne-server/src/test/resources/cayenne-relationships-many-to-many-join.xml
+++ b/cayenne-server/src/test/resources/cayenne-relationships-many-to-many-join.xml
@@ -1,7 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
-<domain xmlns="http://cayenne.apache.org/schema/10/domain"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://cayenne.apache.org/schema/10/domain https://cayenne.apache.org/schema/10/domain.xsd"
- project-version="10">
+<domain project-version="9">
<map name="relationships-many-to-many-join"/>
</domain>
diff --git a/cayenne-server/src/test/resources/relationships-many-to-many-join.map.xml b/cayenne-server/src/test/resources/relationships-many-to-many-join.map.xml
index 38ef803..07da692 100644
--- a/cayenne-server/src/test/resources/relationships-many-to-many-join.map.xml
+++ b/cayenne-server/src/test/resources/relationships-many-to-many-join.map.xml
@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
-<data-map xmlns="http://cayenne.apache.org/schema/10/modelMap"
+<data-map xmlns="http://cayenne.apache.org/schema/9/modelMap"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://cayenne.apache.org/schema/10/modelMap https://cayenne.apache.org/schema/10/modelMap.xsd"
- project-version="10">
+ xsi:schemaLocation="http://cayenne.apache.org/schema/9/modelMap https://cayenne.apache.org/schema/9/modelMap.xsd"
+ project-version="9">
<property name="defaultPackage" value="org.apache.cayenne.testdo.relationships_many_to_many_join"/>
<db-entity name="X_AUTHOR">
<db-attribute name="AUTHOR_ID" type="INTEGER" isPrimaryKey="true" isMandatory="true"/>
@@ -28,10 +28,10 @@
<db-relationship name="songAuthor" source="X_SONG" target="X_SONGAUTHOR" toDependentPK="true" toMany="true">
<db-attribute-pair source="SONG_ID" target="SONG_ID"/>
</db-relationship>
- <db-relationship name="song" source="X_SONGAUTHOR" target="X_SONG">
+ <db-relationship name="song" source="X_SONGAUTHOR" target="X_SONG" toMany="false">
<db-attribute-pair source="SONG_ID" target="SONG_ID"/>
</db-relationship>
- <db-relationship name="author" source="X_SONGAUTHOR" target="X_AUTHOR">
+ <db-relationship name="author" source="X_SONGAUTHOR" target="X_AUTHOR" toMany="false">
<db-attribute-pair source="AUTHOR_ID" target="AUTHOR_ID"/>
</db-relationship>
<obj-relationship name="authors" source="Song" target="Author" collection-type="java.util.Set" deleteRule="Cascade" db-relationship-path="songAuthor.author"/>