CAY-2777 Reverse relationship is not set with single table inheritance
diff --git a/RELEASE-NOTES.txt b/RELEASE-NOTES.txt
index 806b26b..b3c3921 100644
--- a/RELEASE-NOTES.txt
+++ b/RELEASE-NOTES.txt
@@ -51,6 +51,7 @@
CAY-2764 Split expressions do not work with DB relationships
CAY-2765 dbimport: check excluded catalogs and schemas for the SQLServer
CAY-2774 Overriding service ordering in DI List causes DIRuntimeException
+CAY-2777 Reverse relationship is not set with single table inheritance
CAY-2782 Modeler: save button becomes active on DataMap comment field focus
CAY-2783 DbEntity to ObjEntity synchronization should check mandatory flag for primitive java types
CAY-2792 Fix Insertion Order For Reflexive DataObjects
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/flush/DefaultDataDomainFlushAction.java b/cayenne-server/src/main/java/org/apache/cayenne/access/flush/DefaultDataDomainFlushAction.java
index 5a673f2..3d99e79 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/access/flush/DefaultDataDomainFlushAction.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/access/flush/DefaultDataDomainFlushAction.java
@@ -41,6 +41,7 @@
import org.apache.cayenne.access.flush.operation.DbRowOpSorter;
import org.apache.cayenne.access.flush.operation.DbRowOp;
import org.apache.cayenne.access.flush.operation.DbRowOpVisitor;
+import org.apache.cayenne.access.flush.operation.OpIdFactory;
import org.apache.cayenne.access.flush.operation.UpdateDbRowOp;
import org.apache.cayenne.graph.CompoundDiff;
import org.apache.cayenne.graph.GraphDiff;
@@ -130,7 +131,7 @@
protected List<DbRowOp> mergeSameObjectIds(List<DbRowOp> dbRowOps) {
Map<ObjectId, DbRowOp> index = new HashMap<>(dbRowOps.size());
// new EffectiveOpId()
- dbRowOps.forEach(row -> index.merge(row.getChangeId(), row, new DbRowOpMerger()));
+ dbRowOps.forEach(row -> index.merge(OpIdFactory.idForOperation(row.getChangeId()), row, new DbRowOpMerger()));
// reuse list
dbRowOps.clear();
dbRowOps.addAll(index.values());
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/flush/EffectiveOpId.java b/cayenne-server/src/main/java/org/apache/cayenne/access/flush/EffectiveOpId.java
index 5b4d048..458125d 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/access/flush/EffectiveOpId.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/access/flush/EffectiveOpId.java
@@ -61,7 +61,7 @@
Object initial = value;
int safeguard = 0;
while (value instanceof Supplier && safeguard < MAX_NESTED_SUPPLIER_LEVEL) {
- value = ((Supplier) value).get();
+ value = ((Supplier<?>) value).get();
safeguard++;
}
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/flush/operation/OpIdFactory.java b/cayenne-server/src/main/java/org/apache/cayenne/access/flush/operation/OpIdFactory.java
new file mode 100644
index 0000000..b29d9e1
--- /dev/null
+++ b/cayenne-server/src/main/java/org/apache/cayenne/access/flush/operation/OpIdFactory.java
@@ -0,0 +1,120 @@
+/*****************************************************************
+ * 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
+ *
+ * https://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.flush.operation;
+
+import org.apache.cayenne.ObjectId;
+
+import java.util.Map;
+import java.util.Objects;
+
+/**
+ * Factory that wraps provided ID to be suitable for the better processing in the flush operation.
+ *
+ * @since 4.2
+ */
+public class OpIdFactory {
+
+ private static final String DB_PREFIX = "db:";
+
+ static public ObjectId idForOperation(ObjectId originalId) {
+ if(originalId.isReplacementIdAttached() && originalId.getEntityName().startsWith(DB_PREFIX)) {
+ return new ReplacementAwareObjectId(originalId);
+ } else {
+ return originalId;
+ }
+ }
+
+ /**
+ * Special wrapper for the ObjectId, that uses entity name + replacement map for hashCode() and equals()
+ */
+ static class ReplacementAwareObjectId implements ObjectId {
+
+ private final ObjectId originalId;
+
+ ReplacementAwareObjectId(ObjectId originalId) {
+ this.originalId = Objects.requireNonNull(originalId);
+ }
+
+ @Override
+ public boolean isTemporary() {
+ return originalId.isTemporary();
+ }
+
+ @Override
+ public String getEntityName() {
+ return originalId.getEntityName();
+ }
+
+ @Override
+ public byte[] getKey() {
+ return originalId.getKey();
+ }
+
+ @Override
+ public Map<String, Object> getIdSnapshot() {
+ return originalId.getIdSnapshot();
+ }
+
+ @Override
+ public Map<String, Object> getReplacementIdMap() {
+ return originalId.getReplacementIdMap();
+ }
+
+ @Override
+ public ObjectId createReplacementId() {
+ return originalId.createReplacementId();
+ }
+
+ @Override
+ public boolean isReplacementIdAttached() {
+ return originalId.isReplacementIdAttached();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if(this == obj) {
+ return true;
+ }
+ if(!(obj instanceof ObjectId)) {
+ return false;
+ }
+
+ ObjectId other = (ObjectId) obj;
+ if(!other.isReplacementIdAttached()) {
+ return false;
+ }
+ if(!Objects.equals(originalId.getEntityName(), other.getEntityName())) {
+ return false;
+ }
+ return originalId.getReplacementIdMap().equals(other.getReplacementIdMap());
+ }
+
+ @Override
+ public int hashCode() {
+ return 31 * getEntityName().hashCode() + originalId.getReplacementIdMap().hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return "OpId: " + originalId;
+ }
+ }
+
+}
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/map/ObjRelationship.java b/cayenne-server/src/main/java/org/apache/cayenne/map/ObjRelationship.java
index ee36fbf..82f0abf 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/map/ObjRelationship.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/map/ObjRelationship.java
@@ -196,8 +196,8 @@
ObjEntity source = getSourceEntity();
for (ObjRelationship relationship : target.getRelationships()) {
-
- if (relationship.getTargetEntity() != source) {
+ ObjEntity maybeSameSource = relationship.getTargetEntity();
+ if (maybeSameSource != source && !source.isSubentityOf(maybeSameSource)) {
continue;
}
@@ -306,9 +306,8 @@
return true;
}
- // entities with qualifiers may result in filtering even existing target
- // rows, so
- // such relationships are optional
+ // entities with qualifiers may result in filtering even existing target rows,
+ // so such relationships are optional
if (isQualifiedEntity(getTargetEntity())) {
return true;
}
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..efa5813 100644
--- a/cayenne-server/src/test/java/org/apache/cayenne/ManyToManyJoinIT.java
+++ b/cayenne-server/src/test/java/org/apache/cayenne/ManyToManyJoinIT.java
@@ -22,6 +22,8 @@
import org.apache.cayenne.di.Inject;
import org.apache.cayenne.testdo.relationships_many_to_many_join.Author;
+import org.apache.cayenne.testdo.relationships_many_to_many_join.SelfRelationship;
+import org.apache.cayenne.testdo.relationships_many_to_many_join.SelfRelationshipSub;
import org.apache.cayenne.testdo.relationships_many_to_many_join.Song;
import org.apache.cayenne.unit.di.server.CayenneProjects;
import org.apache.cayenne.unit.di.server.ServerCase;
@@ -48,4 +50,29 @@
assertEquals(author, song.getAuthors().iterator().next());
}
+ @Test
+ public void testManyToManySelfRelationship() {
+ SelfRelationship parent1 = context.newObject(SelfRelationship.class);
+ parent1.setName("parent1");
+
+ SelfRelationshipSub child1 = context.newObject(SelfRelationshipSub.class);
+ child1.setName("child1");
+
+ SelfRelationshipSub child2 = context.newObject(SelfRelationshipSub.class);
+ child2.setName("child2");
+
+ // this sets both forward and reverse relationships
+ child2.addToSelfParents(parent1);
+
+ // this still couldn't set reverse relationship, as it present in the Subclass only
+ parent1.addToSelfChildren(child1);
+
+ context.commitChanges();
+
+ assertEquals(2, parent1.getSelfChildren().size());
+
+ assertEquals(1, child2.getSelfParents().size());
+ assertEquals(1, child1.getSelfParents().size());
+ }
+
}
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/access/flush/DefaultDataDomainFlushActionTest.java b/cayenne-server/src/test/java/org/apache/cayenne/access/flush/DefaultDataDomainFlushActionTest.java
index 8f08fc0..29b89a4 100644
--- a/cayenne-server/src/test/java/org/apache/cayenne/access/flush/DefaultDataDomainFlushActionTest.java
+++ b/cayenne-server/src/test/java/org/apache/cayenne/access/flush/DefaultDataDomainFlushActionTest.java
@@ -92,6 +92,35 @@
}
@Test
+ public void mergeSameObjectsId_ReplacementId() {
+ ObjectId id1 = ObjectId.of("db:test2");
+ id1.getReplacementIdMap().put("id", 1);
+ ObjectId id2 = ObjectId.of("db:test");
+ id2.getReplacementIdMap().put("id", 1);
+ ObjectId id3 = ObjectId.of("db:test");
+ id3.getReplacementIdMap().put("id", 1);
+ ObjectId id4 = ObjectId.of("db:test");
+ id4.getReplacementIdMap().put("id", 2);
+
+ DbEntity test = mockEntity("test");
+ DbEntity test2 = mockEntity("test2");
+ BaseDbRowOp[] op = new BaseDbRowOp[4];
+ op[0] = new InsertDbRowOp(mockObject(id1), test2, id1); // +
+ op[1] = new InsertDbRowOp(mockObject(id2), test, id2); // -
+ op[2] = new DeleteDbRowOp(mockObject(id3), test, id3); // -
+ op[3] = new UpdateDbRowOp(mockObject(id4), test, id4); // +
+
+ DefaultDataDomainFlushAction action = mock(DefaultDataDomainFlushAction.class);
+ when(action.mergeSameObjectIds((List<DbRowOp>) any(List.class))).thenCallRealMethod();
+
+ Collection<DbRowOp> merged = action.mergeSameObjectIds(new ArrayList<>(Arrays.asList(op)));
+ assertEquals(3, merged.size());
+
+ assertThat(merged, hasItems(op[0], op[2], op[3]));
+ assertThat(merged, not(hasItem(sameInstance(op[1]))));
+ }
+
+ @Test
public void createQueries() {
ObjectId id1 = ObjectId.of("test", "id", 1);
ObjectId id2 = ObjectId.of("test", "id", 2);
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/access/flush/operation/OpIdFactoryTest.java b/cayenne-server/src/test/java/org/apache/cayenne/access/flush/operation/OpIdFactoryTest.java
new file mode 100644
index 0000000..a921f2e
--- /dev/null
+++ b/cayenne-server/src/test/java/org/apache/cayenne/access/flush/operation/OpIdFactoryTest.java
@@ -0,0 +1,67 @@
+package org.apache.cayenne.access.flush.operation;
+
+import org.apache.cayenne.ObjectId;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+public class OpIdFactoryTest {
+
+ @Test
+ public void testEqualsAndHashCode() {
+ ObjectId idSource1 = ObjectId.of("db:test");
+ idSource1.getReplacementIdMap().put("id", 1);
+
+ ObjectId idSource2 = ObjectId.of("db:test");
+ idSource2.getReplacementIdMap().put("id", 1);
+
+ ObjectId idSource3 = ObjectId.of("db:test");
+ idSource3.getReplacementIdMap().put("id2", 1);
+
+ ObjectId idSource4 = ObjectId.of("db:test2");
+ idSource4.getReplacementIdMap().put("id", 1);
+
+ ObjectId idSource5 = ObjectId.of("db:test2");
+ idSource5.getReplacementIdMap().put("id", 1);
+
+ ObjectId idSource6 = ObjectId.of("db:test", "id", 1);
+
+ ObjectId id1 = OpIdFactory.idForOperation(idSource1);
+ ObjectId id2 = OpIdFactory.idForOperation(idSource2);
+ ObjectId id3 = OpIdFactory.idForOperation(idSource3);
+ ObjectId id4 = OpIdFactory.idForOperation(idSource4);
+ ObjectId id5 = OpIdFactory.idForOperation(idSource5);
+ ObjectId id6 = OpIdFactory.idForOperation(idSource6);
+
+ assertEquals(id1, id1);
+ assertEquals(id2, id2);
+ assertEquals(id1, id2);
+ assertEquals(id1.hashCode(), id2.hashCode());
+
+ assertEquals(id4, id4);
+ assertEquals(id5, id5);
+ assertEquals(id4, id5);
+ assertEquals(id4.hashCode(), id5.hashCode());
+
+ assertNotEquals(id1, id3);
+ assertNotEquals(id1.hashCode(), id3.hashCode());
+ assertNotEquals(id1, id4);
+ assertNotEquals(id1.hashCode(), id4.hashCode());
+ assertNotEquals(id2, id5);
+ assertNotEquals(id2.hashCode(), id5.hashCode());
+ assertNotEquals(id3, id4);
+ assertNotEquals(id3.hashCode(), id4.hashCode());
+
+ assertNotEquals(id1, id6);
+ assertNotEquals(id1.hashCode(), id6.hashCode());
+
+ assertNotSame(idSource1, id1);
+ assertNotSame(idSource2, id2);
+ assertNotSame(idSource3, id3);
+ assertNotSame(idSource4, id4);
+ assertNotSame(idSource5, id5);
+ assertSame(idSource6, id6);
+ }
+
+
+}
\ No newline at end of file
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/testdo/relationships_many_to_many_join/SelfRelationship.java b/cayenne-server/src/test/java/org/apache/cayenne/testdo/relationships_many_to_many_join/SelfRelationship.java
new file mode 100644
index 0000000..91a30e2
--- /dev/null
+++ b/cayenne-server/src/test/java/org/apache/cayenne/testdo/relationships_many_to_many_join/SelfRelationship.java
@@ -0,0 +1,28 @@
+/*****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * https://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._SelfRelationship;
+
+public class SelfRelationship extends _SelfRelationship {
+
+ private static final long serialVersionUID = 1L;
+
+}
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/testdo/relationships_many_to_many_join/SelfRelationshipSub.java b/cayenne-server/src/test/java/org/apache/cayenne/testdo/relationships_many_to_many_join/SelfRelationshipSub.java
new file mode 100644
index 0000000..457a745
--- /dev/null
+++ b/cayenne-server/src/test/java/org/apache/cayenne/testdo/relationships_many_to_many_join/SelfRelationshipSub.java
@@ -0,0 +1,28 @@
+/*****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * https://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._SelfRelationshipSub;
+
+public class SelfRelationshipSub extends _SelfRelationshipSub {
+
+ private static final long serialVersionUID = 1L;
+
+}
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 77a0f08..809495d 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
@@ -16,7 +16,7 @@
*/
public abstract class _Author extends BaseDataObject {
- private static final long serialVersionUID = 1L;
+ private static final long serialVersionUID = 1L;
public static final String AUTHOR_ID_PK_COLUMN = "AUTHOR_ID";
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/testdo/relationships_many_to_many_join/auto/_SelfRelationship.java b/cayenne-server/src/test/java/org/apache/cayenne/testdo/relationships_many_to_many_join/auto/_SelfRelationship.java
new file mode 100644
index 0000000..35929fd
--- /dev/null
+++ b/cayenne-server/src/test/java/org/apache/cayenne/testdo/relationships_many_to_many_join/auto/_SelfRelationship.java
@@ -0,0 +1,132 @@
+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.List;
+
+import org.apache.cayenne.BaseDataObject;
+import org.apache.cayenne.exp.property.ListProperty;
+import org.apache.cayenne.exp.property.NumericProperty;
+import org.apache.cayenne.exp.property.PropertyFactory;
+import org.apache.cayenne.exp.property.StringProperty;
+import org.apache.cayenne.testdo.relationships_many_to_many_join.SelfRelationshipSub;
+
+/**
+ * Class _SelfRelationship was generated by Cayenne.
+ * It is probably a good idea to avoid changing this class manually,
+ * since it may be overwritten next time code is regenerated.
+ * If you need to make any customizations, please use subclass.
+ */
+public abstract class _SelfRelationship extends BaseDataObject {
+
+ private static final long serialVersionUID = 1L;
+
+ public static final String SELF_ID_PK_COLUMN = "SELF_ID";
+
+ public static final StringProperty<String> NAME = PropertyFactory.createString("name", String.class);
+ public static final NumericProperty<Integer> TYPE = PropertyFactory.createNumeric("type", Integer.class);
+ public static final ListProperty<SelfRelationshipSub> SELF_CHILDREN = PropertyFactory.createList("selfChildren", SelfRelationshipSub.class);
+
+ protected String name;
+ protected int type;
+
+ protected Object selfChildren;
+
+ public void setName(String name) {
+ beforePropertyWrite("name", this.name, name);
+ this.name = name;
+ }
+
+ public String getName() {
+ beforePropertyRead("name");
+ return this.name;
+ }
+
+ public void setType(int type) {
+ beforePropertyWrite("type", this.type, type);
+ this.type = type;
+ }
+
+ public int getType() {
+ beforePropertyRead("type");
+ return this.type;
+ }
+
+ public void addToSelfChildren(SelfRelationshipSub obj) {
+ addToManyTarget("selfChildren", obj, true);
+ }
+
+ public void removeFromSelfChildren(SelfRelationshipSub obj) {
+ removeToManyTarget("selfChildren", obj, true);
+ }
+
+ @SuppressWarnings("unchecked")
+ public List<SelfRelationshipSub> getSelfChildren() {
+ return (List<SelfRelationshipSub>)readProperty("selfChildren");
+ }
+
+ @Override
+ public Object readPropertyDirectly(String propName) {
+ if(propName == null) {
+ throw new IllegalArgumentException();
+ }
+
+ switch(propName) {
+ case "name":
+ return this.name;
+ case "type":
+ return this.type;
+ case "selfChildren":
+ return this.selfChildren;
+ 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 "type":
+ this.type = val == null ? 0 : (int)val;
+ break;
+ case "selfChildren":
+ this.selfChildren = 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.writeInt(this.type);
+ out.writeObject(this.selfChildren);
+ }
+
+ @Override
+ protected void readState(ObjectInputStream in) throws IOException, ClassNotFoundException {
+ super.readState(in);
+ this.name = (String)in.readObject();
+ this.type = in.readInt();
+ this.selfChildren = in.readObject();
+ }
+
+}
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/testdo/relationships_many_to_many_join/auto/_SelfRelationshipSub.java b/cayenne-server/src/test/java/org/apache/cayenne/testdo/relationships_many_to_many_join/auto/_SelfRelationshipSub.java
new file mode 100644
index 0000000..e35e562
--- /dev/null
+++ b/cayenne-server/src/test/java/org/apache/cayenne/testdo/relationships_many_to_many_join/auto/_SelfRelationshipSub.java
@@ -0,0 +1,91 @@
+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.List;
+
+import org.apache.cayenne.exp.property.ListProperty;
+import org.apache.cayenne.exp.property.PropertyFactory;
+import org.apache.cayenne.testdo.relationships_many_to_many_join.SelfRelationship;
+
+/**
+ * Class _SelfRelationshipSub was generated by Cayenne.
+ * It is probably a good idea to avoid changing this class manually,
+ * since it may be overwritten next time code is regenerated.
+ * If you need to make any customizations, please use subclass.
+ */
+public abstract class _SelfRelationshipSub extends SelfRelationship {
+
+ private static final long serialVersionUID = 1L;
+
+ public static final String SELF_ID_PK_COLUMN = "SELF_ID";
+
+ public static final ListProperty<SelfRelationship> SELF_PARENTS = PropertyFactory.createList("selfParents", SelfRelationship.class);
+
+
+ protected Object selfParents;
+
+ public void addToSelfParents(SelfRelationship obj) {
+ addToManyTarget("selfParents", obj, true);
+ }
+
+ public void removeFromSelfParents(SelfRelationship obj) {
+ removeToManyTarget("selfParents", obj, true);
+ }
+
+ @SuppressWarnings("unchecked")
+ public List<SelfRelationship> getSelfParents() {
+ return (List<SelfRelationship>)readProperty("selfParents");
+ }
+
+ @Override
+ public Object readPropertyDirectly(String propName) {
+ if(propName == null) {
+ throw new IllegalArgumentException();
+ }
+
+ switch(propName) {
+ case "selfParents":
+ return this.selfParents;
+ default:
+ return super.readPropertyDirectly(propName);
+ }
+ }
+
+ @Override
+ public void writePropertyDirectly(String propName, Object val) {
+ if(propName == null) {
+ throw new IllegalArgumentException();
+ }
+
+ switch (propName) {
+ case "selfParents":
+ this.selfParents = 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.selfParents);
+ }
+
+ @Override
+ protected void readState(ObjectInputStream in) throws IOException, ClassNotFoundException {
+ super.readState(in);
+ this.selfParents = in.readObject();
+ }
+
+}
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 b2d18d9..fa99551 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
@@ -19,7 +19,7 @@
*/
public abstract class _Song extends BaseDataObject {
- private static final long serialVersionUID = 1L;
+ private static final long serialVersionUID = 1L;
public static final String SONG_ID_PK_COLUMN = "SONG_ID";
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 4da95b3..899ebfc 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
@@ -8,6 +8,15 @@
<db-attribute name="AUTHOR_ID" type="INTEGER" isPrimaryKey="true" isMandatory="true"/>
<db-attribute name="AUTHOR_NAME" type="VARCHAR" isMandatory="true" length="50"/>
</db-entity>
+ <db-entity name="X_SELF">
+ <db-attribute name="NAME" type="VARCHAR" isMandatory="true" length="255"/>
+ <db-attribute name="SELF_ID" type="INTEGER" isPrimaryKey="true" isMandatory="true"/>
+ <db-attribute name="TYPE" type="INTEGER" isMandatory="true"/>
+ </db-entity>
+ <db-entity name="X_SELF_JOIN">
+ <db-attribute name="SELF_ID_FROM" type="INTEGER" isPrimaryKey="true" isMandatory="true"/>
+ <db-attribute name="SELF_ID_TO" type="INTEGER" isPrimaryKey="true" isMandatory="true"/>
+ </db-entity>
<db-entity name="X_SONG">
<db-attribute name="SONG_ID" type="INTEGER" isPrimaryKey="true" isMandatory="true"/>
<db-attribute name="SONG_NAME" type="VARCHAR" isMandatory="true" length="50"/>
@@ -19,12 +28,32 @@
<obj-entity name="Author" className="org.apache.cayenne.testdo.relationships_many_to_many_join.Author" dbEntityName="X_AUTHOR">
<obj-attribute name="name" type="java.lang.String" db-attribute-path="AUTHOR_NAME"/>
</obj-entity>
+ <obj-entity name="SelfRelationship" className="org.apache.cayenne.testdo.relationships_many_to_many_join.SelfRelationship" dbEntityName="X_SELF">
+ <qualifier><![CDATA[type = 1]]></qualifier>
+ <obj-attribute name="name" type="java.lang.String" db-attribute-path="NAME"/>
+ <obj-attribute name="type" type="int" db-attribute-path="TYPE"/>
+ </obj-entity>
+ <obj-entity name="SelfRelationshipSub" superEntityName="SelfRelationship" className="org.apache.cayenne.testdo.relationships_many_to_many_join.SelfRelationshipSub">
+ <qualifier><![CDATA[type = 2]]></qualifier>
+ </obj-entity>
<obj-entity name="Song" className="org.apache.cayenne.testdo.relationships_many_to_many_join.Song" dbEntityName="X_SONG">
<obj-attribute name="name" type="java.lang.String" db-attribute-path="SONG_NAME"/>
</obj-entity>
<db-relationship name="songAuthor" source="X_AUTHOR" target="X_SONGAUTHOR" toDependentPK="true" toMany="true">
<db-attribute-pair source="AUTHOR_ID" target="AUTHOR_ID"/>
</db-relationship>
+ <db-relationship name="selfJoinFrom" source="X_SELF" target="X_SELF_JOIN" toDependentPK="true" toMany="true">
+ <db-attribute-pair source="SELF_ID" target="SELF_ID_FROM"/>
+ </db-relationship>
+ <db-relationship name="selfJoinTo" source="X_SELF" target="X_SELF_JOIN" toDependentPK="true" toMany="true">
+ <db-attribute-pair source="SELF_ID" target="SELF_ID_TO"/>
+ </db-relationship>
+ <db-relationship name="selfFrom" source="X_SELF_JOIN" target="X_SELF">
+ <db-attribute-pair source="SELF_ID_FROM" target="SELF_ID"/>
+ </db-relationship>
+ <db-relationship name="selfTo" source="X_SELF_JOIN" target="X_SELF">
+ <db-attribute-pair source="SELF_ID_TO" target="SELF_ID"/>
+ </db-relationship>
<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>
@@ -34,5 +63,7 @@
<db-relationship name="song" source="X_SONGAUTHOR" target="X_SONG">
<db-attribute-pair source="SONG_ID" target="SONG_ID"/>
</db-relationship>
+ <obj-relationship name="selfChildren" source="SelfRelationship" target="SelfRelationshipSub" deleteRule="Deny" db-relationship-path="selfJoinFrom.selfTo"/>
+ <obj-relationship name="selfParents" source="SelfRelationshipSub" target="SelfRelationship" deleteRule="Nullify" db-relationship-path="selfJoinTo.selfFrom"/>
<obj-relationship name="authors" source="Song" target="Author" collection-type="java.util.Set" deleteRule="Cascade" db-relationship-path="songAuthor.author"/>
</data-map>