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/08/19 13:49:05 UTC

[cayenne] branch STABLE-4.1 updated: CAY-2606 Can't resolve obj path with embeddable component

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

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


The following commit(s) were added to refs/heads/STABLE-4.1 by this push:
     new 4c0bd42  CAY-2606 Can't resolve obj path with embeddable component
4c0bd42 is described below

commit 4c0bd42379f6850ccee3d3f3a1726a979757e4e2
Author: Nikita Timofeev <st...@gmail.com>
AuthorDate: Mon Aug 19 16:48:37 2019 +0300

    CAY-2606 Can't resolve obj path with embeddable component
---
 RELEASE-NOTES.txt                                  |  1 +
 .../apache/cayenne/map/PathComponentIterator.java  | 15 ++++-
 .../org/apache/cayenne/access/EmbeddingIT.java     | 63 +++++++++++++++++-
 .../cayenne/testdo/embeddable/EmbedEntity2.java    |  9 +++
 .../testdo/embeddable/auto/_EmbedEntity1.java      | 24 +++++++
 .../{_EmbedEntity1.java => _EmbedEntity2.java}     | 75 +++++++++++-----------
 .../src/test/resources/cayenne-embeddable.xml      |  2 +
 .../src/test/resources/embeddable.map.xml          | 25 +++++++-
 8 files changed, 169 insertions(+), 45 deletions(-)

diff --git a/RELEASE-NOTES.txt b/RELEASE-NOTES.txt
index 953bc62..dce7b52 100644
--- a/RELEASE-NOTES.txt
+++ b/RELEASE-NOTES.txt
@@ -34,6 +34,7 @@ CAY-2600 Modeler DbImport: Can't retrieve schema for databases with no catalog s
 CAY-2601 Modeler DbImport: result dialog issues
 CAY-2603 NPE reloading project in the model
 CAY-2605 Modeler: Unable to save - java.nio.file.InvalidPathException
+CAY-2606 Can't resolve obj path with embeddable component
 
 ----------------------------------
 Release: 4.1.B2
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/map/PathComponentIterator.java b/cayenne-server/src/main/java/org/apache/cayenne/map/PathComponentIterator.java
index a23a281..210540f 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/map/PathComponentIterator.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/map/PathComponentIterator.java
@@ -37,6 +37,7 @@ class PathComponentIterator implements Iterator<PathComponent<Attribute, Relatio
     private final String path;
     private final Map<String, String> aliasMap;
 
+    private EmbeddedAttribute embeddedAttribute;
     private Entity currentEntity;
 
     PathComponentIterator(Entity root, String path, Map<String, String> aliasMap) {
@@ -44,6 +45,7 @@ class PathComponentIterator implements Iterator<PathComponent<Attribute, Relatio
         this.path = Objects.requireNonNull(path);
         this.aliasMap = Objects.requireNonNull(aliasMap);
         this.toks = new StringTokenizer(path, Entity.PATH_SEPARATOR);
+        this.embeddedAttribute = null;
     }
 
     public boolean hasNext() {
@@ -62,10 +64,19 @@ class PathComponentIterator implements Iterator<PathComponent<Attribute, Relatio
         }
 
         // see if this is an attribute
-        Attribute attr = currentEntity.getAttribute(pathComp);
+        Attribute attr;
+        if(embeddedAttribute != null) {
+            attr = embeddedAttribute.getAttribute(pathComp);
+            embeddedAttribute = null;
+        } else {
+            attr = currentEntity.getAttribute(pathComp);
+        }
+
         if (attr != null) {
             // do a sanity check...
-            if (toks.hasMoreTokens()) {
+            if(attr instanceof EmbeddedAttribute) {
+                embeddedAttribute = (EmbeddedAttribute)attr;
+            } else if (toks.hasMoreTokens()) {
                 throw new ExpressionException(
                         "Attribute must be the last component of the path: '" + pathComp + "'.", path, null);
             }
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/access/EmbeddingIT.java b/cayenne-server/src/test/java/org/apache/cayenne/access/EmbeddingIT.java
index 9675a0c..0c38d9e 100644
--- a/cayenne-server/src/test/java/org/apache/cayenne/access/EmbeddingIT.java
+++ b/cayenne-server/src/test/java/org/apache/cayenne/access/EmbeddingIT.java
@@ -24,9 +24,11 @@ import org.apache.cayenne.ObjectContext;
 import org.apache.cayenne.PersistenceState;
 import org.apache.cayenne.di.Inject;
 import org.apache.cayenne.query.SelectQuery;
+import org.apache.cayenne.query.ObjectSelect;
 import org.apache.cayenne.test.jdbc.DBHelper;
 import org.apache.cayenne.test.jdbc.TableHelper;
 import org.apache.cayenne.testdo.embeddable.EmbedEntity1;
+import org.apache.cayenne.testdo.embeddable.EmbedEntity2;
 import org.apache.cayenne.testdo.embeddable.Embeddable1;
 import org.apache.cayenne.unit.di.server.CayenneProjects;
 import org.apache.cayenne.unit.di.server.ServerCase;
@@ -49,21 +51,29 @@ public class EmbeddingIT extends ServerCase {
     protected DBHelper dbHelper;
     
     protected TableHelper tEmbedEntity1;
+    protected TableHelper tEmbedEntity2;
 
     @Before
     public void setUp() throws Exception {
         tEmbedEntity1 = new TableHelper(dbHelper, "EMBED_ENTITY1");
         tEmbedEntity1.setColumns("ID", "NAME", "EMBEDDED10", "EMBEDDED20", "EMBEDDED30", "EMBEDDED40");
+
+        tEmbedEntity2 = new TableHelper(dbHelper, "EMBED_ENTITY2");
+        tEmbedEntity2.setColumns("ID", "NAME", "ENTITY1_ID", "EMBEDDED10", "EMBEDDED20");
     }
     
     protected void createSelectDataSet() throws Exception {
-        tEmbedEntity1.delete().execute();
         tEmbedEntity1.insert(1, "n1", "e1", "e2", "e3", "e4");
         tEmbedEntity1.insert(2, "n2", "ex1", "ex2", "ex3", "ex4");
     }
+
+    protected void createSelectDataSet2() throws Exception {
+        createSelectDataSet();
+        tEmbedEntity2.insert(1, "n2-1", 1, "e1", "e2");
+        tEmbedEntity2.insert(2, "n2-1", 2, "e1", "e2");
+    }
     
     protected void createUpdateDataSet() throws Exception {
-        tEmbedEntity1.delete().execute();
         tEmbedEntity1.insert(1, "n1", "e1", "e2", "e3", "e4");
     }
 
@@ -180,4 +190,53 @@ public class EmbeddingIT extends ServerCase {
         assertNotNull(row);
         assertEquals("x1", row.get("EMBEDDED10"));
     }
+
+    @Test
+    public void testPropertyExpression() throws Exception {
+        createSelectDataSet();
+
+        List<EmbedEntity1> result = ObjectSelect.query(EmbedEntity1.class)
+                .where(EmbedEntity1.EMBEDDED1.dot(Embeddable1.EMBEDDED10).eq("e1"))
+                .orderBy(EmbedEntity1.EMBEDDED2.dot(Embeddable1.EMBEDDED20).desc())
+                .select(context);
+
+        assertEquals(1, result.size());
+    }
+
+    @Test
+    public void testRelatedEmbedded() throws Exception {
+        createSelectDataSet2();
+
+        List<EmbedEntity2> result = ObjectSelect.query(EmbedEntity2.class)
+                .where(EmbedEntity2.ENTITY1.dot(EmbedEntity1.EMBEDDED1).dot(Embeddable1.EMBEDDED10).eq("e1"))
+                .orderBy(EmbedEntity2.ENTITY1.dot(EmbedEntity1.EMBEDDED2).dot(Embeddable1.EMBEDDED20).desc())
+                .select(context);
+
+        assertEquals(1, result.size());
+    }
+
+    @Test
+    public void testPrefetchWithEmbedded() throws Exception {
+        createSelectDataSet2();
+
+        List<EmbedEntity2> result = ObjectSelect.query(EmbedEntity2.class)
+                .prefetch(EmbedEntity2.ENTITY1.joint())
+                .select(context);
+
+        assertEquals(2, result.size());
+        assertNotNull(result.get(0).getEntity1().getEmbedded1());
+        assertNotNull(result.get(1).getEntity1().getEmbedded1());
+    }
+
+    @Test
+    public void testInMemoryFilteringByEmbeddable() throws Exception {
+        createSelectDataSet();
+
+        List<EmbedEntity1> result = ObjectSelect.query(EmbedEntity1.class).select(context);
+        assertEquals(2, result.size());
+
+        List<EmbedEntity1> filtered = EmbedEntity1.EMBEDDED1.dot(Embeddable1.EMBEDDED10).eq("e1").filterObjects(result);
+        assertEquals(1, filtered.size());
+        assertEquals("n1", filtered.get(0).getName());
+    }
 }
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/testdo/embeddable/EmbedEntity2.java b/cayenne-server/src/test/java/org/apache/cayenne/testdo/embeddable/EmbedEntity2.java
new file mode 100644
index 0000000..128e79b
--- /dev/null
+++ b/cayenne-server/src/test/java/org/apache/cayenne/testdo/embeddable/EmbedEntity2.java
@@ -0,0 +1,9 @@
+package org.apache.cayenne.testdo.embeddable;
+
+import org.apache.cayenne.testdo.embeddable.auto._EmbedEntity2;
+
+public class EmbedEntity2 extends _EmbedEntity2 {
+
+    private static final long serialVersionUID = 1L; 
+
+}
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/testdo/embeddable/auto/_EmbedEntity1.java b/cayenne-server/src/test/java/org/apache/cayenne/testdo/embeddable/auto/_EmbedEntity1.java
index 2634b64..d1e34d0 100644
--- a/cayenne-server/src/test/java/org/apache/cayenne/testdo/embeddable/auto/_EmbedEntity1.java
+++ b/cayenne-server/src/test/java/org/apache/cayenne/testdo/embeddable/auto/_EmbedEntity1.java
@@ -3,9 +3,11 @@ package org.apache.cayenne.testdo.embeddable.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;
+import org.apache.cayenne.testdo.embeddable.EmbedEntity2;
 import org.apache.cayenne.testdo.embeddable.Embeddable1;
 
 /**
@@ -23,11 +25,13 @@ public abstract class _EmbedEntity1 extends BaseDataObject {
     public static final Property<Embeddable1> EMBEDDED1 = Property.create("embedded1", Embeddable1.class);
     public static final Property<Embeddable1> EMBEDDED2 = Property.create("embedded2", Embeddable1.class);
     public static final Property<String> NAME = Property.create("name", String.class);
+    public static final Property<List<EmbedEntity2>> EMBED_ENTITY2S = Property.create("embedEntity2s", List.class);
 
     protected Embeddable1 embedded1;
     protected Embeddable1 embedded2;
     protected String name;
 
+    protected Object embedEntity2s;
 
     public void setEmbedded1(Embeddable1 embedded1) {
         beforePropertyWrite("embedded1", this.embedded1, embedded1);
@@ -59,6 +63,19 @@ public abstract class _EmbedEntity1 extends BaseDataObject {
         return this.name;
     }
 
+    public void addToEmbedEntity2s(EmbedEntity2 obj) {
+        addToManyTarget("embedEntity2s", obj, true);
+    }
+
+    public void removeFromEmbedEntity2s(EmbedEntity2 obj) {
+        removeToManyTarget("embedEntity2s", obj, true);
+    }
+
+    @SuppressWarnings("unchecked")
+    public List<EmbedEntity2> getEmbedEntity2s() {
+        return (List<EmbedEntity2>)readProperty("embedEntity2s");
+    }
+
     @Override
     public Object readPropertyDirectly(String propName) {
         if(propName == null) {
@@ -72,6 +89,8 @@ public abstract class _EmbedEntity1 extends BaseDataObject {
                 return this.embedded2;
             case "name":
                 return this.name;
+            case "embedEntity2s":
+                return this.embedEntity2s;
             default:
                 return super.readPropertyDirectly(propName);
         }
@@ -93,6 +112,9 @@ public abstract class _EmbedEntity1 extends BaseDataObject {
             case "name":
                 this.name = (String)val;
                 break;
+            case "embedEntity2s":
+                this.embedEntity2s = val;
+                break;
             default:
                 super.writePropertyDirectly(propName, val);
         }
@@ -112,6 +134,7 @@ public abstract class _EmbedEntity1 extends BaseDataObject {
         out.writeObject(this.embedded1);
         out.writeObject(this.embedded2);
         out.writeObject(this.name);
+        out.writeObject(this.embedEntity2s);
     }
 
     @Override
@@ -120,6 +143,7 @@ public abstract class _EmbedEntity1 extends BaseDataObject {
         this.embedded1 = (Embeddable1)in.readObject();
         this.embedded2 = (Embeddable1)in.readObject();
         this.name = (String)in.readObject();
+        this.embedEntity2s = in.readObject();
     }
 
 }
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/testdo/embeddable/auto/_EmbedEntity1.java b/cayenne-server/src/test/java/org/apache/cayenne/testdo/embeddable/auto/_EmbedEntity2.java
similarity index 62%
copy from cayenne-server/src/test/java/org/apache/cayenne/testdo/embeddable/auto/_EmbedEntity1.java
copy to cayenne-server/src/test/java/org/apache/cayenne/testdo/embeddable/auto/_EmbedEntity2.java
index 2634b64..de53c02 100644
--- a/cayenne-server/src/test/java/org/apache/cayenne/testdo/embeddable/auto/_EmbedEntity1.java
+++ b/cayenne-server/src/test/java/org/apache/cayenne/testdo/embeddable/auto/_EmbedEntity2.java
@@ -6,57 +6,56 @@ import java.io.ObjectOutputStream;
 
 import org.apache.cayenne.BaseDataObject;
 import org.apache.cayenne.exp.Property;
+import org.apache.cayenne.testdo.embeddable.EmbedEntity1;
 import org.apache.cayenne.testdo.embeddable.Embeddable1;
 
 /**
- * Class _EmbedEntity1 was generated by Cayenne.
+ * Class _EmbedEntity2 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 _EmbedEntity1 extends BaseDataObject {
+public abstract class _EmbedEntity2 extends BaseDataObject {
 
     private static final long serialVersionUID = 1L; 
 
     public static final String ID_PK_COLUMN = "ID";
 
-    public static final Property<Embeddable1> EMBEDDED1 = Property.create("embedded1", Embeddable1.class);
-    public static final Property<Embeddable1> EMBEDDED2 = Property.create("embedded2", Embeddable1.class);
     public static final Property<String> NAME = Property.create("name", String.class);
+    public static final Property<Embeddable1> EMBEDDED = Property.create("embedded", Embeddable1.class);
+    public static final Property<EmbedEntity1> ENTITY1 = Property.create("entity1", EmbedEntity1.class);
 
-    protected Embeddable1 embedded1;
-    protected Embeddable1 embedded2;
     protected String name;
+    protected Embeddable1 embedded;
 
+    protected Object entity1;
 
-    public void setEmbedded1(Embeddable1 embedded1) {
-        beforePropertyWrite("embedded1", this.embedded1, embedded1);
-        this.embedded1 = embedded1;
+    public void setName(String name) {
+        beforePropertyWrite("name", this.name, name);
+        this.name = name;
     }
 
-    public Embeddable1 getEmbedded1() {
-        beforePropertyRead("embedded1");
-        return this.embedded1;
+    public String getName() {
+        beforePropertyRead("name");
+        return this.name;
     }
 
-    public void setEmbedded2(Embeddable1 embedded2) {
-        beforePropertyWrite("embedded2", this.embedded2, embedded2);
-        this.embedded2 = embedded2;
+    public void setEmbedded(Embeddable1 embedded) {
+        beforePropertyWrite("embedded", this.embedded, embedded);
+        this.embedded = embedded;
     }
 
-    public Embeddable1 getEmbedded2() {
-        beforePropertyRead("embedded2");
-        return this.embedded2;
+    public Embeddable1 getEmbedded() {
+        beforePropertyRead("embedded");
+        return this.embedded;
     }
 
-    public void setName(String name) {
-        beforePropertyWrite("name", this.name, name);
-        this.name = name;
+    public void setEntity1(EmbedEntity1 entity1) {
+        setToOneTarget("entity1", entity1, true);
     }
 
-    public String getName() {
-        beforePropertyRead("name");
-        return this.name;
+    public EmbedEntity1 getEntity1() {
+        return (EmbedEntity1)readProperty("entity1");
     }
 
     @Override
@@ -66,12 +65,12 @@ public abstract class _EmbedEntity1 extends BaseDataObject {
         }
 
         switch(propName) {
-            case "embedded1":
-                return this.embedded1;
-            case "embedded2":
-                return this.embedded2;
             case "name":
                 return this.name;
+            case "embedded":
+                return this.embedded;
+            case "entity1":
+                return this.entity1;
             default:
                 return super.readPropertyDirectly(propName);
         }
@@ -84,15 +83,15 @@ public abstract class _EmbedEntity1 extends BaseDataObject {
         }
 
         switch (propName) {
-            case "embedded1":
-                this.embedded1 = (Embeddable1)val;
-                break;
-            case "embedded2":
-                this.embedded2 = (Embeddable1)val;
-                break;
             case "name":
                 this.name = (String)val;
                 break;
+            case "embedded":
+                this.embedded = (Embeddable1)val;
+                break;
+            case "entity1":
+                this.entity1 = val;
+                break;
             default:
                 super.writePropertyDirectly(propName, val);
         }
@@ -109,17 +108,17 @@ public abstract class _EmbedEntity1 extends BaseDataObject {
     @Override
     protected void writeState(ObjectOutputStream out) throws IOException {
         super.writeState(out);
-        out.writeObject(this.embedded1);
-        out.writeObject(this.embedded2);
         out.writeObject(this.name);
+        out.writeObject(this.embedded);
+        out.writeObject(this.entity1);
     }
 
     @Override
     protected void readState(ObjectInputStream in) throws IOException, ClassNotFoundException {
         super.readState(in);
-        this.embedded1 = (Embeddable1)in.readObject();
-        this.embedded2 = (Embeddable1)in.readObject();
         this.name = (String)in.readObject();
+        this.embedded = (Embeddable1)in.readObject();
+        this.entity1 = in.readObject();
     }
 
 }
diff --git a/cayenne-server/src/test/resources/cayenne-embeddable.xml b/cayenne-server/src/test/resources/cayenne-embeddable.xml
index aedacf3..eabee1b 100644
--- a/cayenne-server/src/test/resources/cayenne-embeddable.xml
+++ b/cayenne-server/src/test/resources/cayenne-embeddable.xml
@@ -1,5 +1,7 @@
 <?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">
 	<map name="embeddable"/>
 </domain>
diff --git a/cayenne-server/src/test/resources/embeddable.map.xml b/cayenne-server/src/test/resources/embeddable.map.xml
index 10c4db6..dbcae05 100644
--- a/cayenne-server/src/test/resources/embeddable.map.xml
+++ b/cayenne-server/src/test/resources/embeddable.map.xml
@@ -1,12 +1,12 @@
 <?xml version="1.0" encoding="utf-8"?>
 <data-map xmlns="http://cayenne.apache.org/schema/10/modelMap"
 	 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-	 xsi:schemaLocation="http://cayenne.apache.org/schema/10/modelMap http://cayenne.apache.org/schema/10/modelMap.xsd"
+	 xsi:schemaLocation="http://cayenne.apache.org/schema/10/modelMap https://cayenne.apache.org/schema/10/modelMap.xsd"
 	 project-version="10">
 	<property name="defaultPackage" value="org.apache.cayenne.testdo.embeddable"/>
 	<embeddable className="org.apache.cayenne.testdo.embeddable.Embeddable1">
-		<embeddable-attribute name="embedded10" type="java.lang.String" db-attribute-name="EMBEDDED10"/>
 		<embeddable-attribute name="embedded20" type="java.lang.String" db-attribute-name="EMBEDDED20"/>
+		<embeddable-attribute name="embedded10" type="java.lang.String" db-attribute-name="EMBEDDED10"/>
 	</embeddable>
 	<db-entity name="EMBED_ENTITY1">
 		<db-attribute name="EMBEDDED10" type="VARCHAR" length="100"/>
@@ -16,12 +16,31 @@
 		<db-attribute name="ID" type="INTEGER" isPrimaryKey="true" isMandatory="true"/>
 		<db-attribute name="NAME" type="VARCHAR" length="100"/>
 	</db-entity>
+	<db-entity name="EMBED_ENTITY2">
+		<db-attribute name="EMBEDDED10" type="VARCHAR" length="100"/>
+		<db-attribute name="EMBEDDED20" type="VARCHAR" length="100"/>
+		<db-attribute name="ENTITY1_ID" type="INTEGER"/>
+		<db-attribute name="ID" type="INTEGER" isPrimaryKey="true" isMandatory="true"/>
+		<db-attribute name="NAME" type="VARCHAR" length="100"/>
+	</db-entity>
 	<obj-entity name="EmbedEntity1" className="org.apache.cayenne.testdo.embeddable.EmbedEntity1" dbEntityName="EMBED_ENTITY1">
 		<embedded-attribute name="embedded1" type="org.apache.cayenne.testdo.embeddable.Embeddable1"/>
 		<embedded-attribute name="embedded2" type="org.apache.cayenne.testdo.embeddable.Embeddable1">
-			<embeddable-attribute-override name="embedded10" db-attribute-path="EMBEDDED30"/>
 			<embeddable-attribute-override name="embedded20" db-attribute-path="EMBEDDED40"/>
+			<embeddable-attribute-override name="embedded10" db-attribute-path="EMBEDDED30"/>
 		</embedded-attribute>
 		<obj-attribute name="name" type="java.lang.String" db-attribute-path="NAME"/>
 	</obj-entity>
+	<obj-entity name="EmbedEntity2" className="org.apache.cayenne.testdo.embeddable.EmbedEntity2" dbEntityName="EMBED_ENTITY2">
+		<embedded-attribute name="embedded" type="org.apache.cayenne.testdo.embeddable.Embeddable1"/>
+		<obj-attribute name="name" type="java.lang.String" db-attribute-path="NAME"/>
+	</obj-entity>
+	<db-relationship name="untitledRel" source="EMBED_ENTITY1" target="EMBED_ENTITY2" toMany="true">
+		<db-attribute-pair source="ID" target="ENTITY1_ID"/>
+	</db-relationship>
+	<db-relationship name="untitledRel" source="EMBED_ENTITY2" target="EMBED_ENTITY1">
+		<db-attribute-pair source="ENTITY1_ID" target="ID"/>
+	</db-relationship>
+	<obj-relationship name="embedEntity2s" source="EmbedEntity1" target="EmbedEntity2" deleteRule="Deny" db-relationship-path="untitledRel"/>
+	<obj-relationship name="entity1" source="EmbedEntity2" target="EmbedEntity1" deleteRule="Nullify" db-relationship-path="untitledRel"/>
 </data-map>