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

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


The following commit(s) were added to refs/heads/master by this push:
     new e83fd7a24 CAY-2777 Reverse relationship is not set with single table 
inheritance
e83fd7a24 is described below

commit e83fd7a24d2eecac56da0ebfe6c321a49ee941a9
Author: Nikita Timofeev <[email protected]>
AuthorDate: Wed Apr 12 14:43:45 2023 +0300

    CAY-2777 Reverse relationship is not set with single table inheritance
---
 RELEASE-NOTES.txt                                  |   1 +
 .../access/flush/DefaultDataDomainFlushAction.java |   3 +-
 .../apache/cayenne/access/flush/EffectiveOpId.java |   2 +-
 .../access/flush/operation/OpIdFactory.java        | 120 +++++++++++++++++++++
 .../org/apache/cayenne/map/ObjRelationship.java    |   9 +-
 .../java/org/apache/cayenne/ManyToManyJoinIT.java  |  27 +++++
 .../flush/DefaultDataDomainFlushActionTest.java    |  29 +++++
 .../access/flush/operation/OpIdFactoryTest.java    |  67 ++++++++++++
 .../SelfRelationship.java}                         |  31 +-----
 .../SelfRelationshipSub.java}                      |  31 +-----
 .../auto/_Author.java                              |   2 +-
 .../auto/{_Song.java => _SelfRelationship.java}    |  62 +++++++----
 .../{_Author.java => _SelfRelationshipSub.java}    |  44 ++++----
 .../auto/_Song.java                                |   2 +-
 .../relationships-many-to-many-join.map.xml        |  31 ++++++
 15 files changed, 357 insertions(+), 104 deletions(-)

diff --git a/RELEASE-NOTES.txt b/RELEASE-NOTES.txt
index 806b26b69..b3c3921ef 100644
--- a/RELEASE-NOTES.txt
+++ b/RELEASE-NOTES.txt
@@ -51,6 +51,7 @@ CAY-2763 Split expressions do not work with flattened 
relationships
 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 5a673f2f9..3d99e797f 100644
--- 
a/cayenne-server/src/main/java/org/apache/cayenne/access/flush/DefaultDataDomainFlushAction.java
+++ 
b/cayenne-server/src/main/java/org/apache/cayenne/access/flush/DefaultDataDomainFlushAction.java
@@ -41,6 +41,7 @@ import 
org.apache.cayenne.access.flush.operation.DbRowOpMerger;
 import org.apache.cayenne.access.flush.operation.DbRowOpSorter;
 import org.apache.cayenne.access.flush.operation.DbRowOp;
 import org.apache.cayenne.access.flush.operation.DbRowOpVisitor;
+import org.apache.cayenne.access.flush.operation.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 @@ public class DefaultDataDomainFlushAction implements 
DataDomainFlushAction {
     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 5b4d048a1..458125d3b 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 @@ public class EffectiveOpId {
                 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 000000000..b29d9e1f6
--- /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 ee36fbf68..82f0abfb4 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 @@ public class ObjRelationship extends 
Relationship<ObjEntity, ObjAttribute, ObjRe
         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 @@ public class ObjRelationship extends 
Relationship<ObjEntity, ObjAttribute, ObjRe
             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 0949b5e60..efa5813f5 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 static org.junit.Assert.*;
 
 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 @@ public class ManyToManyJoinIT extends ServerCase {
         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 8f08fc02d..29b89a496 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
@@ -91,6 +91,35 @@ public class DefaultDataDomainFlushActionTest {
         assertThat(merged, not(hasItem(sameInstance(op[9]))));
     }
 
+    @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);
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 000000000..a921f2ecd
--- /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/ManyToManyJoinIT.java 
b/cayenne-server/src/test/java/org/apache/cayenne/testdo/relationships_many_to_many_join/SelfRelationship.java
similarity index 50%
copy from cayenne-server/src/test/java/org/apache/cayenne/ManyToManyJoinIT.java
copy to 
cayenne-server/src/test/java/org/apache/cayenne/testdo/relationships_many_to_many_join/SelfRelationship.java
index 0949b5e60..91a30e2bd 100644
--- a/cayenne-server/src/test/java/org/apache/cayenne/ManyToManyJoinIT.java
+++ 
b/cayenne-server/src/test/java/org/apache/cayenne/testdo/relationships_many_to_many_join/SelfRelationship.java
@@ -16,36 +16,13 @@
  *  specific language governing permissions and limitations
  *  under the License.
  ****************************************************************/
-package org.apache.cayenne;
 
-import static org.junit.Assert.*;
+package org.apache.cayenne.testdo.relationships_many_to_many_join;
 
-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.Song;
-import org.apache.cayenne.unit.di.server.CayenneProjects;
-import org.apache.cayenne.unit.di.server.ServerCase;
-import org.apache.cayenne.unit.di.server.UseServerRuntime;
-import org.junit.Test;
+import 
org.apache.cayenne.testdo.relationships_many_to_many_join.auto._SelfRelationship;
 
-@UseServerRuntime(CayenneProjects.RELATIONSHIPS_MANY_TO_MANY_JOIN_PROJECT)
-public class ManyToManyJoinIT extends ServerCase {
+public class SelfRelationship extends _SelfRelationship {
 
-    @Inject
-    private ObjectContext context;
-
-    @Test
-    public void testManyToManyJoinWithFlattenedRelationship() throws Exception 
{
-       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());
-    }
+    private static final long serialVersionUID = 1L;
 
 }
diff --git 
a/cayenne-server/src/test/java/org/apache/cayenne/ManyToManyJoinIT.java 
b/cayenne-server/src/test/java/org/apache/cayenne/testdo/relationships_many_to_many_join/SelfRelationshipSub.java
similarity index 50%
copy from cayenne-server/src/test/java/org/apache/cayenne/ManyToManyJoinIT.java
copy to 
cayenne-server/src/test/java/org/apache/cayenne/testdo/relationships_many_to_many_join/SelfRelationshipSub.java
index 0949b5e60..457a7453b 100644
--- a/cayenne-server/src/test/java/org/apache/cayenne/ManyToManyJoinIT.java
+++ 
b/cayenne-server/src/test/java/org/apache/cayenne/testdo/relationships_many_to_many_join/SelfRelationshipSub.java
@@ -16,36 +16,13 @@
  *  specific language governing permissions and limitations
  *  under the License.
  ****************************************************************/
-package org.apache.cayenne;
 
-import static org.junit.Assert.*;
+package org.apache.cayenne.testdo.relationships_many_to_many_join;
 
-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.Song;
-import org.apache.cayenne.unit.di.server.CayenneProjects;
-import org.apache.cayenne.unit.di.server.ServerCase;
-import org.apache.cayenne.unit.di.server.UseServerRuntime;
-import org.junit.Test;
+import 
org.apache.cayenne.testdo.relationships_many_to_many_join.auto._SelfRelationshipSub;
 
-@UseServerRuntime(CayenneProjects.RELATIONSHIPS_MANY_TO_MANY_JOIN_PROJECT)
-public class ManyToManyJoinIT extends ServerCase {
+public class SelfRelationshipSub extends _SelfRelationshipSub {
 
-    @Inject
-    private ObjectContext context;
-
-    @Test
-    public void testManyToManyJoinWithFlattenedRelationship() throws Exception 
{
-       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());
-    }
+    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 77a0f08e7..809495d76 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 @@ import org.apache.cayenne.exp.property.StringProperty;
  */
 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/_Song.java
 
b/cayenne-server/src/test/java/org/apache/cayenne/testdo/relationships_many_to_many_join/auto/_SelfRelationship.java
similarity index 57%
copy from 
cayenne-server/src/test/java/org/apache/cayenne/testdo/relationships_many_to_many_join/auto/_Song.java
copy to 
cayenne-server/src/test/java/org/apache/cayenne/testdo/relationships_many_to_many_join/auto/_SelfRelationship.java
index b2d18d900..35929fdc0 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/_SelfRelationship.java
@@ -3,32 +3,35 @@ 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 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.SetProperty;
 import org.apache.cayenne.exp.property.StringProperty;
-import org.apache.cayenne.testdo.relationships_many_to_many_join.Author;
+import 
org.apache.cayenne.testdo.relationships_many_to_many_join.SelfRelationshipSub;
 
 /**
- * Class _Song was generated by Cayenne.
+ * 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 _Song extends BaseDataObject {
+public abstract class _SelfRelationship extends BaseDataObject {
 
-    private static final long serialVersionUID = 1L; 
+    private static final long serialVersionUID = 1L;
 
-    public static final String SONG_ID_PK_COLUMN = "SONG_ID";
+    public static final String SELF_ID_PK_COLUMN = "SELF_ID";
 
     public static final StringProperty<String> NAME = 
PropertyFactory.createString("name", String.class);
-    public static final SetProperty<Author> AUTHORS = 
PropertyFactory.createSet("authors", Author.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 authors;
+    protected Object selfChildren;
 
     public void setName(String name) {
         beforePropertyWrite("name", this.name, name);
@@ -40,17 +43,27 @@ public abstract class _Song extends BaseDataObject {
         return this.name;
     }
 
-    public void addToAuthors(Author obj) {
-        addToManyTarget("authors", obj, true);
+    public void setType(int type) {
+        beforePropertyWrite("type", this.type, type);
+        this.type = type;
     }
 
-    public void removeFromAuthors(Author obj) {
-        removeToManyTarget("authors", obj, true);
+    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 Set<Author> getAuthors() {
-        return (Set<Author>)readProperty("authors");
+    public List<SelfRelationshipSub> getSelfChildren() {
+        return (List<SelfRelationshipSub>)readProperty("selfChildren");
     }
 
     @Override
@@ -62,8 +75,10 @@ public abstract class _Song extends BaseDataObject {
         switch(propName) {
             case "name":
                 return this.name;
-            case "authors":
-                return this.authors;
+            case "type":
+                return this.type;
+            case "selfChildren":
+                return this.selfChildren;
             default:
                 return super.readPropertyDirectly(propName);
         }
@@ -79,8 +94,11 @@ public abstract class _Song extends BaseDataObject {
             case "name":
                 this.name = (String)val;
                 break;
-            case "authors":
-                this.authors = val;
+            case "type":
+                this.type = val == null ? 0 : (int)val;
+                break;
+            case "selfChildren":
+                this.selfChildren = val;
                 break;
             default:
                 super.writePropertyDirectly(propName, val);
@@ -99,14 +117,16 @@ public abstract class _Song extends BaseDataObject {
     protected void writeState(ObjectOutputStream out) throws IOException {
         super.writeState(out);
         out.writeObject(this.name);
-        out.writeObject(this.authors);
+        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.authors = 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/_Author.java
 
b/cayenne-server/src/test/java/org/apache/cayenne/testdo/relationships_many_to_many_join/auto/_SelfRelationshipSub.java
similarity index 57%
copy from 
cayenne-server/src/test/java/org/apache/cayenne/testdo/relationships_many_to_many_join/auto/_Author.java
copy to 
cayenne-server/src/test/java/org/apache/cayenne/testdo/relationships_many_to_many_join/auto/_SelfRelationshipSub.java
index 77a0f08e7..e35e56274 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/_SelfRelationshipSub.java
@@ -3,36 +3,40 @@ 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.PropertyFactory;
-import org.apache.cayenne.exp.property.StringProperty;
+import 
org.apache.cayenne.testdo.relationships_many_to_many_join.SelfRelationship;
 
 /**
- * Class _Author was generated by Cayenne.
+ * 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 _Author extends BaseDataObject {
+public abstract class _SelfRelationshipSub extends SelfRelationship {
 
-    private static final long serialVersionUID = 1L; 
+    private static final long serialVersionUID = 1L;
 
-    public static final String AUTHOR_ID_PK_COLUMN = "AUTHOR_ID";
+    public static final String SELF_ID_PK_COLUMN = "SELF_ID";
 
-    public static final StringProperty<String> NAME = 
PropertyFactory.createString("name", String.class);
+    public static final ListProperty<SelfRelationship> SELF_PARENTS = 
PropertyFactory.createList("selfParents", SelfRelationship.class);
 
-    protected String name;
 
+    protected Object selfParents;
 
-    public void setName(String name) {
-        beforePropertyWrite("name", this.name, name);
-        this.name = name;
+    public void addToSelfParents(SelfRelationship obj) {
+        addToManyTarget("selfParents", obj, true);
     }
 
-    public String getName() {
-        beforePropertyRead("name");
-        return this.name;
+    public void removeFromSelfParents(SelfRelationship obj) {
+        removeToManyTarget("selfParents", obj, true);
+    }
+
+    @SuppressWarnings("unchecked")
+    public List<SelfRelationship> getSelfParents() {
+        return (List<SelfRelationship>)readProperty("selfParents");
     }
 
     @Override
@@ -42,8 +46,8 @@ public abstract class _Author extends BaseDataObject {
         }
 
         switch(propName) {
-            case "name":
-                return this.name;
+            case "selfParents":
+                return this.selfParents;
             default:
                 return super.readPropertyDirectly(propName);
         }
@@ -56,8 +60,8 @@ public abstract class _Author extends BaseDataObject {
         }
 
         switch (propName) {
-            case "name":
-                this.name = (String)val;
+            case "selfParents":
+                this.selfParents = val;
                 break;
             default:
                 super.writePropertyDirectly(propName, val);
@@ -75,13 +79,13 @@ public abstract class _Author extends BaseDataObject {
     @Override
     protected void writeState(ObjectOutputStream out) throws IOException {
         super.writeState(out);
-        out.writeObject(this.name);
+        out.writeObject(this.selfParents);
     }
 
     @Override
     protected void readState(ObjectInputStream in) throws IOException, 
ClassNotFoundException {
         super.readState(in);
-        this.name = (String)in.readObject();
+        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 b2d18d900..fa99551ee 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 @@ import 
org.apache.cayenne.testdo.relationships_many_to_many_join.Author;
  */
 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 4da95b334..899ebfcac 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>


Reply via email to