From e996e8001658698ecd4ccc56f41aafd4e04b0924 Mon Sep 17 00:00:00 2001 From: Arseni Bulatski Date: Sat, 29 Dec 2018 16:58:14 +0300 Subject: [PATCH 1/2] CAY-2507 Property api to add aliases --- RELEASE-NOTES.txt | 8 + .../apache/cayenne/exp/ExpressionFactory.java | 2 +- .../apache/cayenne/exp/parser/ASTDbPath.java | 13 +- .../apache/cayenne/exp/parser/ASTObjPath.java | 7 +- .../apache/cayenne/exp/parser/ASTPath.java | 6 +- .../cayenne/exp/property/BaseProperty.java | 10 +- .../cayenne/exp/property/EntityProperty.java | 6 +- .../cayenne/exp/property/ListProperty.java | 10 +- .../cayenne/exp/property/MapProperty.java | 13 +- .../cayenne/exp/property/PropertyFactory.java | 4 +- .../cayenne/exp/property/PropertyUtils.java | 70 +++++++ .../exp/property/RelationshipProperty.java | 49 ++++- .../cayenne/exp/property/SetProperty.java | 10 +- .../org/apache/cayenne/map/ObjEntity.java | 51 +++-- .../cayenne/query/SelectQueryMetadata.java | 74 +++++-- .../exp/property/EntityPropertyTest.java | 15 +- .../exp/property/ListPropertyTest.java | 22 +- .../cayenne/exp/property/MapPropertyTest.java | 19 +- .../exp/property/PropertyAliasesIT.java | 189 ++++++++++++++++++ .../cayenne/exp/property/SetPropertyTest.java | 13 +- .../apache/cayenne/query/ColumnSelectIT.java | 27 +-- .../cayenne/query/ObjectSelectTest.java | 20 +- .../query/ObjectSelect_AggregateIT.java | 18 +- .../cayenne/query/ObjectSelect_RunIT.java | 5 +- .../apache/cayenne/query/SelectQueryIT.java | 23 +-- 25 files changed, 537 insertions(+), 147 deletions(-) create mode 100644 cayenne-server/src/main/java/org/apache/cayenne/exp/property/PropertyUtils.java create mode 100644 cayenne-server/src/test/java/org/apache/cayenne/exp/property/PropertyAliasesIT.java diff --git a/RELEASE-NOTES.txt b/RELEASE-NOTES.txt index f2248cda39..aaa4e39ff6 100644 --- a/RELEASE-NOTES.txt +++ b/RELEASE-NOTES.txt @@ -7,6 +7,14 @@ http://cayenne.apache.org/ To browse individual bug reports check out project issue tracker: https://issues.apache.org/jira/browse/CAY +---------------------------------- +Release: 4.2.M1 +Date: +---------------------------------- +Changes/New Features: + +CAY-2507 Property api to add aliases + ---------------------------------- Release: 4.1.M3 Date: diff --git a/cayenne-server/src/main/java/org/apache/cayenne/exp/ExpressionFactory.java b/cayenne-server/src/main/java/org/apache/cayenne/exp/ExpressionFactory.java index 92f45cc21d..3ad5e77f1f 100644 --- a/cayenne-server/src/main/java/org/apache/cayenne/exp/ExpressionFactory.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/exp/ExpressionFactory.java @@ -329,7 +329,7 @@ public static Expression matchAllExp(String path, Object... values) { int splitEnd = path.indexOf(Entity.PATH_SEPARATOR, split + 1); - String beforeSplit = split > 0 ? path.substring(0, split) + "." : ""; + String beforeSplit = split > 0 ? path.substring(0, split) : ""; String afterSplit = splitEnd > 0 ? "." + path.substring(splitEnd + 1) : ""; String aliasBase = "split" + autoAliasId++ + "_"; String splitChunk = splitEnd > 0 ? path.substring(split + 1, splitEnd) : path.substring(split + 1); diff --git a/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTDbPath.java b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTDbPath.java index f3f2f7b04e..870b84d160 100644 --- a/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTDbPath.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTDbPath.java @@ -19,12 +19,6 @@ package org.apache.cayenne.exp.parser; -import java.io.IOException; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; - import org.apache.cayenne.Cayenne; import org.apache.cayenne.ObjectContext; import org.apache.cayenne.ObjectId; @@ -39,6 +33,12 @@ import org.apache.cayenne.query.SelectById; import org.apache.cayenne.util.CayenneMapEntry; +import java.io.IOException; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + /** * Path expression traversing DB relationships and attributes. * @@ -166,6 +166,7 @@ protected Object evaluateNode(Object o) throws Exception { public Expression shallowCopy() { ASTDbPath copy = new ASTDbPath(id); copy.path = path; + copy.setPathAliases(pathAliases); return copy; } diff --git a/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTObjPath.java b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTObjPath.java index 6c1604e765..c536fc664e 100644 --- a/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTObjPath.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTObjPath.java @@ -19,9 +19,6 @@ package org.apache.cayenne.exp.parser; -import java.io.IOException; -import java.util.List; - import org.apache.cayenne.CayenneRuntimeException; import org.apache.cayenne.DataObject; import org.apache.cayenne.exp.Expression; @@ -31,6 +28,9 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.io.IOException; +import java.util.List; + public class ASTObjPath extends ASTPath { private static final long serialVersionUID = -3574281576491705706L; @@ -68,6 +68,7 @@ protected Object evaluateNode(Object o) throws Exception { public Expression shallowCopy() { ASTObjPath copy = new ASTObjPath(id); copy.path = path; + copy.setPathAliases(pathAliases); return copy; } diff --git a/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTPath.java b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTPath.java index 8ab1a395ae..f88b5703e7 100644 --- a/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTPath.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTPath.java @@ -19,12 +19,12 @@ package org.apache.cayenne.exp.parser; -import java.util.Iterator; -import java.util.Map; - import org.apache.cayenne.map.Entity; import org.apache.cayenne.util.CayenneMapEntry; +import java.util.Iterator; +import java.util.Map; + /** * Generic path expression. * diff --git a/cayenne-server/src/main/java/org/apache/cayenne/exp/property/BaseProperty.java b/cayenne-server/src/main/java/org/apache/cayenne/exp/property/BaseProperty.java index 1ce3836bfb..28bc26408c 100644 --- a/cayenne-server/src/main/java/org/apache/cayenne/exp/property/BaseProperty.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/exp/property/BaseProperty.java @@ -19,11 +19,6 @@ package org.apache.cayenne.exp.property; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.function.Supplier; - import org.apache.cayenne.exp.Expression; import org.apache.cayenne.exp.ExpressionFactory; import org.apache.cayenne.exp.FunctionExpressionFactory; @@ -33,6 +28,11 @@ import org.apache.cayenne.query.SortOrder; import org.apache.cayenne.reflect.PropertyUtils; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.function.Supplier; + /** * Property that represents generic attribute. *

diff --git a/cayenne-server/src/main/java/org/apache/cayenne/exp/property/EntityProperty.java b/cayenne-server/src/main/java/org/apache/cayenne/exp/property/EntityProperty.java index 6398a2225e..1f087d60db 100644 --- a/cayenne-server/src/main/java/org/apache/cayenne/exp/property/EntityProperty.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/exp/property/EntityProperty.java @@ -56,7 +56,11 @@ protected EntityProperty(String name, Expression expression, Class type) { */ @Override public EntityProperty alias(String alias) { - return PropertyFactory.createEntity(alias, getExpression(), getType()); + String substrPath = PropertyUtils.substringPath(this.getName()); + String aliasedPath = substrPath + alias; + return PropertyFactory.createEntity(aliasedPath, + PropertyUtils.createPathExp(aliasedPath, this.getName().substring(substrPath.length()), alias, getExpression().getPathAliases()), + this.getType()); } /** diff --git a/cayenne-server/src/main/java/org/apache/cayenne/exp/property/ListProperty.java b/cayenne-server/src/main/java/org/apache/cayenne/exp/property/ListProperty.java index 410d6dbf1b..d8021dc78e 100644 --- a/cayenne-server/src/main/java/org/apache/cayenne/exp/property/ListProperty.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/exp/property/ListProperty.java @@ -19,11 +19,11 @@ package org.apache.cayenne.exp.property; -import java.util.List; - import org.apache.cayenne.Persistent; import org.apache.cayenne.exp.Expression; +import java.util.List; + /** * Property that represents to-many relationship mapped on {@link List}. *

{@code
@@ -52,7 +52,11 @@ protected ListProperty(String name, Expression expression, Class entityType)
      */
     @Override
     public ListProperty alias(String alias) {
-        return PropertyFactory.createList(alias, this.getExpression(), this.getEntityType());
+        String substrPath = PropertyUtils.substringPath(this.getName());
+        String aliasedPath = substrPath + alias;
+        return PropertyFactory.createList(aliasedPath,
+                PropertyUtils.createPathExp(aliasedPath, this.getName().substring(substrPath.length()), alias, getExpression().getPathAliases()),
+                this.getEntityType());
     }
 
     /**
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/exp/property/MapProperty.java b/cayenne-server/src/main/java/org/apache/cayenne/exp/property/MapProperty.java
index 0137659468..1974de471a 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/exp/property/MapProperty.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/exp/property/MapProperty.java
@@ -19,13 +19,13 @@
 
 package org.apache.cayenne.exp.property;
 
-import java.util.Collection;
-import java.util.Map;
-
 import org.apache.cayenne.Persistent;
 import org.apache.cayenne.exp.Expression;
 import org.apache.cayenne.exp.ExpressionFactory;
 
+import java.util.Collection;
+import java.util.Map;
+
 /**
  * Property that represents to-many relationship mapped on {@link Map}.
  *
@@ -204,7 +204,12 @@ public Expression notContainsId(Collection ids) {
      */
     @Override
     public MapProperty alias(String alias) {
-        return PropertyFactory.createMap(alias, this.getExpression(), getKeyType(), getEntityType());
+        String substrPath = PropertyUtils.substringPath(this.getName());
+        String aliasedPath = substrPath + alias;
+        return PropertyFactory.createMap(aliasedPath,
+                PropertyUtils.createPathExp(aliasedPath, this.getName().substring(substrPath.length()), alias, getExpression().getPathAliases()),
+                this.getKeyType(),
+                this.getEntityType());
     }
 
     /**
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/exp/property/PropertyFactory.java b/cayenne-server/src/main/java/org/apache/cayenne/exp/property/PropertyFactory.java
index c355d734ea..e16d936869 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/exp/property/PropertyFactory.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/exp/property/PropertyFactory.java
@@ -19,14 +19,14 @@
 
 package org.apache.cayenne.exp.property;
 
-import java.time.LocalDateTime;
-
 import org.apache.cayenne.ObjectContext;
 import org.apache.cayenne.Persistent;
 import org.apache.cayenne.exp.Expression;
 import org.apache.cayenne.exp.ExpressionFactory;
 import org.apache.cayenne.exp.FunctionExpressionFactory;
 
+import java.time.LocalDateTime;
+
 /**
  *
  * Factory class that produces all property types.
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/exp/property/PropertyUtils.java b/cayenne-server/src/main/java/org/apache/cayenne/exp/property/PropertyUtils.java
new file mode 100644
index 0000000000..43b41fc88b
--- /dev/null
+++ b/cayenne-server/src/main/java/org/apache/cayenne/exp/property/PropertyUtils.java
@@ -0,0 +1,70 @@
+/*****************************************************************
+ *   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.exp.property;
+
+import org.apache.cayenne.CayenneRuntimeException;
+import org.apache.cayenne.exp.Expression;
+import org.apache.cayenne.exp.parser.ASTObjPath;
+import org.apache.cayenne.exp.parser.ASTPath;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @since 4.2
+ */
+class PropertyUtils {
+
+    static ASTPath createPathExp(String aliasedPath, String segment, String alias, Map aliasMap) {
+        ASTPath pathExp = new ASTObjPath(aliasedPath);
+        Map aliases = new HashMap<>(aliasMap);
+        aliases.put(alias, segment);
+        pathExp.setPathAliases(aliases);
+
+        return pathExp;
+    }
+
+    static ASTPath createExpressionWithCopiedAliases(String name, Expression expression) {
+        if(expression instanceof ASTPath) {
+            ASTPath pathExp = new ASTObjPath(name);
+            pathExp.setPathAliases(expression.getPathAliases());
+            return pathExp;
+        }
+
+        throw new CayenneRuntimeException("Dot is used only with path expressions.");
+    }
+
+    static String substringPath(String propertyName){
+        for(int i = propertyName.length() - 1; i >= 0; i--) {
+            if(propertyName.charAt(i) == '.') {
+                return propertyName.substring(0, i + 1);
+            }
+        }
+
+        return "";
+    }
+
+    static void checkAliases(Expression expression) {
+        if(!expression.getPathAliases().isEmpty()) {
+            throw new CayenneRuntimeException("Can't use aliases with prefetch");
+        }
+    }
+
+}
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/exp/property/RelationshipProperty.java b/cayenne-server/src/main/java/org/apache/cayenne/exp/property/RelationshipProperty.java
index 13b9fdccc0..76e9d5a10c 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/exp/property/RelationshipProperty.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/exp/property/RelationshipProperty.java
@@ -38,7 +38,10 @@ public interface RelationshipProperty extends Property {
      * @return a newly created Property object.
      */
     default BaseProperty dot(String property) {
-        return PropertyFactory.createBase(getName() + "." + property, null);
+        String path = getName() + "." + property;
+        return PropertyFactory.createBase(path,
+                PropertyUtils.createExpressionWithCopiedAliases(path, getExpression()),
+                null);
     }
 
     /**
@@ -48,7 +51,10 @@ default BaseProperty dot(String property) {
      * @return a newly created Property object.
      */
     default  BaseProperty dot(BaseProperty property) {
-        return PropertyFactory.createBase(getName() + "." + property.getName(), property.getType());
+        String path = getName() + "." + property.getName();
+        return PropertyFactory.createBase(path,
+                PropertyUtils.createExpressionWithCopiedAliases(path, getExpression()),
+                property.getType());
     }
 
     /**
@@ -58,7 +64,10 @@ default  BaseProperty dot(BaseProperty property) {
      * @return a newly created Property object.
      */
     default  NumericProperty dot(NumericProperty property) {
-        return PropertyFactory.createNumeric(getName() + "." + property.getName(), property.getType());
+        String path = getName() + "." + property.getName();
+        return PropertyFactory.createNumeric(path,
+                PropertyUtils.createExpressionWithCopiedAliases(path, getExpression()),
+                property.getType());
     }
 
     /**
@@ -68,7 +77,10 @@ default  NumericProperty dot(NumericProperty property) {
      * @return a newly created Property object.
      */
     default  StringProperty dot(StringProperty property) {
-        return PropertyFactory.createString(getName() + "." + property.getName(), property.getType());
+        String path = getName() + "." + property.getName();
+        return PropertyFactory.createString(path,
+                PropertyUtils.createExpressionWithCopiedAliases(path, getExpression()),
+                property.getType());
     }
 
     /**
@@ -78,7 +90,10 @@ default  StringProperty dot(StringProperty propert
      * @return a newly created Property object.
      */
     default  DateProperty dot(DateProperty property) {
-        return PropertyFactory.createDate(getName() + "." + property.getName(), property.getType());
+        String path = getName() + "." + property.getName();
+        return PropertyFactory.createDate(path,
+                PropertyUtils.createExpressionWithCopiedAliases(path, getExpression()),
+                property.getType());
     }
 
     /**
@@ -88,7 +103,10 @@ default  DateProperty dot(DateProperty property) {
      * @return a newly created Property object.
      */
     default  EntityProperty dot(EntityProperty property) {
-        return PropertyFactory.createEntity(getName() + "." + property.getName(), property.getType());
+        String path = getName() + "." + property.getName();
+        return PropertyFactory.createEntity(path,
+                PropertyUtils.createExpressionWithCopiedAliases(path, getExpression()),
+                property.getType());
     }
 
     /**
@@ -98,7 +116,10 @@ default  EntityProperty dot(EntityProperty property)
      * @return a newly created Property object.
      */
     default  ListProperty dot(ListProperty property) {
-        return PropertyFactory.createList(getName() + "." + property.getName(), property.getEntityType());
+        String path = getName() + "." + property.getName();
+        return PropertyFactory.createList(path,
+                PropertyUtils.createExpressionWithCopiedAliases(path, getExpression()),
+                property.getEntityType());
     }
 
     /**
@@ -108,7 +129,10 @@ default  ListProperty dot(ListProperty property) {
      * @return a newly created Property object.
      */
     default  SetProperty dot(SetProperty property) {
-        return PropertyFactory.createSet(getName() + "." + property.getName(), property.getEntityType());
+        String path = getName() + "." + property.getName();
+        return PropertyFactory.createSet(path,
+                PropertyUtils.createExpressionWithCopiedAliases(path, getExpression()),
+                property.getEntityType());
     }
 
     /**
@@ -118,7 +142,11 @@ default  SetProperty dot(SetProperty property) {
      * @return a newly created Property object.
      */
     default  MapProperty dot(MapProperty property) {
-        return PropertyFactory.createMap(getName() + "." + property.getName(), property.getKeyType(), property.getEntityType());
+        String path = getName() + "." + property.getName();
+        return PropertyFactory.createMap(path,
+                PropertyUtils.createExpressionWithCopiedAliases(path, getExpression()),
+                property.getKeyType(),
+                property.getEntityType());
     }
 
     /**
@@ -134,6 +162,7 @@ default  MapProperty dot(MapProperty proper
      * prefetch semantics.
      */
     default PrefetchTreeNode joint() {
+        PropertyUtils.checkAliases(getExpression());
         return PrefetchTreeNode.withPath(getName(), PrefetchTreeNode.JOINT_PREFETCH_SEMANTICS);
     }
 
@@ -143,6 +172,7 @@ default PrefetchTreeNode joint() {
      * "disjoint" prefetch semantics.
      */
     default PrefetchTreeNode disjoint() {
+        PropertyUtils.checkAliases(getExpression());
         return PrefetchTreeNode.withPath(getName(), PrefetchTreeNode.DISJOINT_PREFETCH_SEMANTICS);
     }
 
@@ -152,6 +182,7 @@ default PrefetchTreeNode disjoint() {
      * "disjoint by id" prefetch semantics.
      */
     default PrefetchTreeNode disjointById() {
+        PropertyUtils.checkAliases(getExpression());
         return PrefetchTreeNode.withPath(getName(), PrefetchTreeNode.DISJOINT_BY_ID_PREFETCH_SEMANTICS);
     }
 
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/exp/property/SetProperty.java b/cayenne-server/src/main/java/org/apache/cayenne/exp/property/SetProperty.java
index f7ded0b062..908fab7ce8 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/exp/property/SetProperty.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/exp/property/SetProperty.java
@@ -19,11 +19,11 @@
 
 package org.apache.cayenne.exp.property;
 
-import java.util.Set;
-
 import org.apache.cayenne.Persistent;
 import org.apache.cayenne.exp.Expression;
 
+import java.util.Set;
+
 /**
  * Property that represents to-many relationship mapped on {@link Set}.
  *
@@ -48,7 +48,11 @@ protected SetProperty(String name, Expression expression, Class entityType) {
      */
     @Override
     public SetProperty alias(String alias) {
-        return PropertyFactory.createSet(alias, this.getExpression(), this.getEntityType());
+        String substrPath = PropertyUtils.substringPath(this.getName());
+        String aliasedPath = substrPath + alias;
+        return PropertyFactory.createSet(aliasedPath,
+                PropertyUtils.createPathExp(aliasedPath, this.getName().substring(substrPath.length()), alias, getExpression().getPathAliases()),
+                this.getEntityType());
     }
 
     /**
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/map/ObjEntity.java b/cayenne-server/src/main/java/org/apache/cayenne/map/ObjEntity.java
index 74e793ddaf..d9ddca381c 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/map/ObjEntity.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/map/ObjEntity.java
@@ -1003,15 +1003,14 @@ public Expression translateToRelatedEntity(Expression expression, String relatio
 
         DBPathConverter transformer = new DBPathConverter();
 
-        String dbPath = transformer.toDbPath(createPathIterator(relationshipPath));
+        String dbPath = transformer.toDbPath(createPathIterator(relationshipPath, expression.getPathAliases()));
         Expression dbClone = expression.transform(transformer);
 
         return getDbEntity().translateToRelatedEntity(dbClone, dbPath);
     }
 
-    private PathComponentIterator createPathIterator(String path) {
-        return new PathComponentIterator(ObjEntity.this, path, Collections.emptyMap());
-        // TODO: do we need aliases here?
+    private PathComponentIterator createPathIterator(String path, Map aliasMap) {
+        return new PathComponentIterator(ObjEntity.this, path, aliasMap);
     }
 
     /**
@@ -1037,31 +1036,41 @@ String toDbPath(PathComponentIterator objectPathComponents) {
                 PathComponent component = objectPathComponents.next();
 
                 Iterator dbSubpath;
-
-                if (component.getAttribute() != null) {
+                if(component.getAttribute() != null) {
                     dbSubpath = ((ObjAttribute) component.getAttribute()).getDbPathIterator();
-                } else if (component.getRelationship() != null) {
+                    buildPath(dbSubpath, component, buf);
+                } else if(component.getRelationship() != null) {
                     dbSubpath = ((ObjRelationship) component.getRelationship()).getDbRelationships().iterator();
+                    buildPath(dbSubpath, component, buf);
+                } else if(component.getAliasedPath() != null) {
+                    for(PathComponent pathComponent : component.getAliasedPath()) {
+                       if(pathComponent.getRelationship() != null) {
+                           dbSubpath = ((ObjRelationship) pathComponent.getRelationship()).getDbRelationships().iterator();
+                           buildPath(dbSubpath, pathComponent, buf);
+                       }
+                    }
                 } else {
                     throw new CayenneRuntimeException("Unknown path component: %s", component);
                 }
+            }
 
-                while (dbSubpath.hasNext()) {
-                    CayenneMapEntry subComponent = (CayenneMapEntry) dbSubpath.next();
-                    if (buf.length() > 0) {
-                        buf.append(Entity.PATH_SEPARATOR);
-                    }
+            return buf.toString();
+        }
 
-                    buf.append(subComponent.getName());
-                    
-                    // use OUTER join for all components of the path is Obj path is OUTER
-                    if (component.getJoinType() == JoinType.LEFT_OUTER) {
-                        buf.append(OUTER_JOIN_INDICATOR);
-                    }
+        private void buildPath(Iterator dbSubpath, PathComponent component, StringBuilder buf) {
+            while (dbSubpath.hasNext()) {
+                CayenneMapEntry subComponent = (CayenneMapEntry) dbSubpath.next();
+                if (buf.length() > 0) {
+                    buf.append(Entity.PATH_SEPARATOR);
                 }
-            }
 
-            return buf.toString();
+                buf.append(subComponent.getName());
+
+                // use OUTER join for all components of the path is Obj path is OUTER
+                if (component.getJoinType() == JoinType.LEFT_OUTER) {
+                    buf.append(OUTER_JOIN_INDICATOR);
+                }
+            }
         }
 
         public Object apply(Object input) {
@@ -1077,7 +1086,7 @@ public Object apply(Object input) {
             }
 
             // convert obj_path to db_path
-            String converted = toDbPath(createPathIterator((String) expression.getOperand(0)));
+            String converted = toDbPath(createPathIterator((String) expression.getOperand(0), expression.getPathAliases()));
             return ExpressionFactory.dbPathExp(converted);
         }
     }
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/query/SelectQueryMetadata.java b/cayenne-server/src/main/java/org/apache/cayenne/query/SelectQueryMetadata.java
index 13c6761ce8..63e72f83ad 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/query/SelectQueryMetadata.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/query/SelectQueryMetadata.java
@@ -18,26 +18,18 @@
  ****************************************************************/
 package org.apache.cayenne.query;
 
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
 import org.apache.cayenne.CayenneRuntimeException;
 import org.apache.cayenne.ObjectId;
 import org.apache.cayenne.Persistent;
 import org.apache.cayenne.access.types.ValueObjectType;
 import org.apache.cayenne.access.types.ValueObjectTypeRegistry;
-import org.apache.cayenne.exp.property.BaseProperty;
 import org.apache.cayenne.exp.Expression;
 import org.apache.cayenne.exp.ExpressionFactory;
 import org.apache.cayenne.exp.TraversalHandler;
 import org.apache.cayenne.exp.parser.ASTDbPath;
 import org.apache.cayenne.exp.parser.ASTFunctionCall;
 import org.apache.cayenne.exp.parser.ASTScalar;
+import org.apache.cayenne.exp.property.BaseProperty;
 import org.apache.cayenne.map.DbAttribute;
 import org.apache.cayenne.map.DbEntity;
 import org.apache.cayenne.map.DbJoin;
@@ -56,6 +48,15 @@
 import org.apache.cayenne.reflect.ToOneProperty;
 import org.apache.cayenne.util.CayenneMapEntry;
 
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
 /**
  * @since 3.0
  */
@@ -63,9 +64,9 @@ class SelectQueryMetadata extends BaseQueryMetadata {
 
 	private static final long serialVersionUID = 7465922769303943945L;
 	
-	Map pathSplitAliases;
-	boolean isSingleResultSetMapping;
-	boolean suppressingDistinct;
+	private Map pathSplitAliases;
+	private boolean isSingleResultSetMapping;
+	private boolean suppressingDistinct;
 
 	@Override
 	void copyFromInfo(QueryMetadata info) {
@@ -154,21 +155,64 @@ private String makeCacheKey(SelectQuery query, EntityResolver resolver) {
 	}
 
 	private void resolveAutoAliases(SelectQuery query) {
+		resolveQualifierAliases(query);
+		resolveColumnsAliases(query);
+        resolveOrderingAliases(query);
+		resolveHavingQualifierAliases(query);
+		// TODO: include aliases in prefetches? flattened attributes?
+	}
+
+	private void resolveQualifierAliases(SelectQuery query) {
 		Expression qualifier = query.getQualifier();
 		if (qualifier != null) {
 			resolveAutoAliases(qualifier);
 		}
-
-		// TODO: include aliases in prefetches? flattened attributes?
 	}
 
+	private void resolveColumnsAliases(SelectQuery query) {
+        Collection> columns = query.getColumns();
+        if(columns != null) {
+            for(BaseProperty property : columns) {
+                Expression propertyExpression = property.getExpression();
+                if(propertyExpression != null) {
+                    resolveAutoAliases(propertyExpression);
+                }
+            }
+        }
+    }
+
+    private void resolveOrderingAliases(SelectQuery query) {
+        List orderings = query.getOrderings();
+        if(orderings != null) {
+            for(Ordering ordering : orderings) {
+                Expression sortSpec = ordering.getSortSpec();
+                if(sortSpec != null) {
+                    resolveAutoAliases(sortSpec);
+                }
+            }
+        }
+    }
+
+    private void resolveHavingQualifierAliases(SelectQuery query) {
+        Expression havingQualifier = query.getHavingQualifier();
+        if(havingQualifier != null) {
+            resolveAutoAliases(havingQualifier);
+        }
+    }
+
 	private void resolveAutoAliases(Expression expression) {
 		Map aliases = expression.getPathAliases();
 		if (!aliases.isEmpty()) {
 			if (pathSplitAliases == null) {
 				pathSplitAliases = new HashMap<>();
 			}
-
+			for(String key : aliases.keySet()) {
+				if(pathSplitAliases.containsKey(key)) {
+					if(!pathSplitAliases.get(key).equals(aliases.get(key))) {
+						throw new CayenneRuntimeException("Can't add the same alias to different path segments.");
+					}
+				}
+			}
 			pathSplitAliases.putAll(aliases);
 		}
 
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/exp/property/EntityPropertyTest.java b/cayenne-server/src/test/java/org/apache/cayenne/exp/property/EntityPropertyTest.java
index 8b9ba0e52e..046e37a4ea 100644
--- a/cayenne-server/src/test/java/org/apache/cayenne/exp/property/EntityPropertyTest.java
+++ b/cayenne-server/src/test/java/org/apache/cayenne/exp/property/EntityPropertyTest.java
@@ -24,7 +24,7 @@
 import org.junit.Before;
 import org.junit.Test;
 
-import static org.junit.Assert.*;
+import static org.junit.Assert.assertEquals;
 
 /**
  * @since 4.2
@@ -32,21 +32,26 @@
 public class EntityPropertyTest {
 
     private EntityProperty property;
+    private EntityProperty property1;
 
     @Before
     public void createProperty() {
         property = new EntityProperty<>("path", null, Artist.class);
+        property1 = new EntityProperty<>("path.artist", null, Artist.class);
     }
 
     @Test
     public void alias() {
         assertEquals("path", property.getName());
-        assertNull(property.getAlias());
-
         property = property.alias("alias");
-
         assertEquals("alias", property.getName());
-        assertEquals("alias", property.getAlias());
+        assertEquals(1, property.getExpression().getPathAliases().size());
+
+        assertEquals("path.artist", property1.getName());
+        property1 = property1.alias("a");
+        assertEquals("path.a", property1.getName());
+        assertEquals(1, property1.getExpression().getPathAliases().size());
+        assertEquals("artist", property1.getExpression().getPathAliases().get("a"));
     }
 
     @Test
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/exp/property/ListPropertyTest.java b/cayenne-server/src/test/java/org/apache/cayenne/exp/property/ListPropertyTest.java
index fb775afb3b..df875393d9 100644
--- a/cayenne-server/src/test/java/org/apache/cayenne/exp/property/ListPropertyTest.java
+++ b/cayenne-server/src/test/java/org/apache/cayenne/exp/property/ListPropertyTest.java
@@ -19,17 +19,16 @@
 
 package org.apache.cayenne.exp.property;
 
-import java.util.Arrays;
-import java.util.Collection;
-
-import org.apache.cayenne.ObjectId;
 import org.apache.cayenne.exp.Expression;
 import org.apache.cayenne.exp.ExpressionFactory;
 import org.apache.cayenne.testdo.testmap.Artist;
 import org.junit.Before;
 import org.junit.Test;
 
-import static org.junit.Assert.*;
+import java.util.Arrays;
+import java.util.Collection;
+
+import static org.junit.Assert.assertEquals;
 
 /**
  * @since 4.2
@@ -37,22 +36,27 @@
 public class ListPropertyTest {
 
     private ListProperty property;
+    private ListProperty property1;
 
     @Before
     public void createProperty() {
         property = new ListProperty<>("path", null, Artist.class);
+        property1 = new ListProperty<>("path.artist", null, Artist.class);
     }
 
 
     @Test
     public void alias() {
         assertEquals("path", property.getName());
-        assertNull(property.getAlias());
-
         property = property.alias("alias");
-
         assertEquals("alias", property.getName());
-        assertEquals("alias", property.getAlias());
+        assertEquals(1, property.getExpression().getPathAliases().size());
+
+        assertEquals("path.artist", property1.getName());
+        property1 = property1.alias("a");
+        assertEquals("path.a", property1.getName());
+        assertEquals(1, property1.getExpression().getPathAliases().size());
+        assertEquals("artist", property1.getExpression().getPathAliases().get("a"));
     }
 
     @Test
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/exp/property/MapPropertyTest.java b/cayenne-server/src/test/java/org/apache/cayenne/exp/property/MapPropertyTest.java
index bc4ee9e0a9..5545942633 100644
--- a/cayenne-server/src/test/java/org/apache/cayenne/exp/property/MapPropertyTest.java
+++ b/cayenne-server/src/test/java/org/apache/cayenne/exp/property/MapPropertyTest.java
@@ -19,15 +19,15 @@
 
 package org.apache.cayenne.exp.property;
 
-import java.util.Arrays;
-
 import org.apache.cayenne.exp.Expression;
 import org.apache.cayenne.exp.ExpressionFactory;
 import org.apache.cayenne.testdo.testmap.Artist;
 import org.junit.Before;
 import org.junit.Test;
 
-import static org.junit.Assert.*;
+import java.util.Arrays;
+
+import static org.junit.Assert.assertEquals;
 
 /**
  * @since 4.2
@@ -35,10 +35,12 @@
 public class MapPropertyTest {
 
     private MapProperty property;
+    private MapProperty property1;
 
     @Before
     public void createProperty() {
         property = new MapProperty<>("path", null, Integer.class, Artist.class);
+        property1 = new MapProperty<>("path.artist", null, Integer.class, Artist.class);
     }
 
     @Test
@@ -132,12 +134,15 @@ public void notContainsManyIdCollection() {
     @Test
     public void alias() {
         assertEquals("path", property.getName());
-        assertNull(property.getAlias());
-
         property = property.alias("alias");
-
         assertEquals("alias", property.getName());
-        assertEquals("alias", property.getAlias());
+        assertEquals(1, property.getExpression().getPathAliases().size());
+
+        assertEquals("path.artist", property1.getName());
+        property1 = property1.alias("a");
+        assertEquals("path.a", property1.getName());
+        assertEquals(1, property1.getExpression().getPathAliases().size());
+        assertEquals("artist", property1.getExpression().getPathAliases().get("a"));
     }
 
     @Test
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/exp/property/PropertyAliasesIT.java b/cayenne-server/src/test/java/org/apache/cayenne/exp/property/PropertyAliasesIT.java
new file mode 100644
index 0000000000..5dbfe1423d
--- /dev/null
+++ b/cayenne-server/src/test/java/org/apache/cayenne/exp/property/PropertyAliasesIT.java
@@ -0,0 +1,189 @@
+/*****************************************************************
+ *   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.exp.property;
+
+import org.apache.cayenne.Cayenne;
+import org.apache.cayenne.CayenneRuntimeException;
+import org.apache.cayenne.access.DataContext;
+import org.apache.cayenne.di.Inject;
+import org.apache.cayenne.exp.Expression;
+import org.apache.cayenne.exp.ExpressionFactory;
+import org.apache.cayenne.exp.parser.ASTEqual;
+import org.apache.cayenne.exp.parser.ASTObjPath;
+import org.apache.cayenne.exp.parser.ASTPath;
+import org.apache.cayenne.query.ObjectSelect;
+import org.apache.cayenne.query.SelectQuery;
+import org.apache.cayenne.test.jdbc.DBHelper;
+import org.apache.cayenne.test.jdbc.TableHelper;
+import org.apache.cayenne.testdo.testmap.Artist;
+import org.apache.cayenne.testdo.testmap.Gallery;
+import org.apache.cayenne.testdo.testmap.Painting;
+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.Before;
+import org.junit.Test;
+
+import java.util.Collections;
+import java.util.List;
+
+import static junit.framework.TestCase.assertTrue;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+/**
+ * @since 4.2
+ */
+@UseServerRuntime(CayenneProjects.TESTMAP_PROJECT)
+public class PropertyAliasesIT extends ServerCase {
+
+    @Inject
+    private DataContext context;
+
+    @Inject
+    private DBHelper dbHelper;
+
+    @Before
+    public void createArtistsDataSet() throws Exception {
+        TableHelper tArtist = new TableHelper(dbHelper, "ARTIST");
+        tArtist.setColumns("ARTIST_ID", "ARTIST_NAME", "DATE_OF_BIRTH");
+
+        long dateBase = System.currentTimeMillis();
+        for (int i = 1; i <= 20; i++) {
+            tArtist.insert(i, "artist" + i, new java.sql.Date(dateBase + 10000 * i));
+        }
+
+        TableHelper tGallery = new TableHelper(dbHelper, "GALLERY");
+        tGallery.setColumns("GALLERY_ID", "GALLERY_NAME");
+        tGallery.insert(1, "tate modern");
+
+        TableHelper tGallery1 = new TableHelper(dbHelper, "GALLERY");
+        tGallery1.setColumns("GALLERY_ID", "GALLERY_NAME");
+        tGallery1.insert(2, "test gallery");
+
+        TableHelper tPaintings = new TableHelper(dbHelper, "PAINTING");
+        tPaintings.setColumns("PAINTING_ID", "PAINTING_TITLE", "ARTIST_ID", "GALLERY_ID");
+        for (int i = 1; i <= 20; i++) {
+            tPaintings.insert(i, "painting" + i,
+                    i % 2 == 0 ? 4 : i % 5 + 1,
+                    i % 2 == 0 ? 2 : 1);
+        }
+    }
+
+    @Test
+    public void testBeginAlias() {
+        List artists = ObjectSelect.query(Artist.class)
+                .where(Artist.PAINTING_ARRAY.alias("p1").dot(Painting.PAINTING_TITLE).eq("painting2"))
+                .and(Artist.PAINTING_ARRAY.alias("p2").dot(Painting.PAINTING_TITLE).eq("painting4"))
+                .select(context);
+        assertEquals(1, artists.size());
+        assertEquals("artist4", artists.get(0).getArtistName());
+    }
+
+    @Test
+    public void testTheSameAliases() {
+        List results = ObjectSelect.columnQuery(Artist.class,
+                Artist.ARTIST_NAME,
+                Artist.PAINTING_ARRAY.alias("p1").dot(Painting.PAINTING_TITLE),
+                Artist.PAINTING_ARRAY.alias("p2").dot(Painting.PAINTING_TITLE))
+                .where(Artist.PAINTING_ARRAY.alias("p1").dot(Painting.PAINTING_TITLE).eq("painting2"))
+                .and(Artist.PAINTING_ARRAY.alias("p2").dot(Painting.PAINTING_TITLE).eq("painting4"))
+                .select(context);
+        assertEquals(1, results.size());
+        assertEquals("artist4", results.get(0)[0]);
+        assertEquals("painting2", results.get(0)[1]);
+        assertEquals("painting4", results.get(0)[2]);
+    }
+
+    @Test
+    public void testMiddleAlias() {
+        List artists = ObjectSelect.query(Artist.class)
+                .where(Artist.PAINTING_ARRAY.dot(Painting.TO_GALLERY).dot(Gallery.PAINTING_ARRAY).alias("p1").dot(Painting.PAINTING_TITLE).eq("painting2"))
+                .and(Artist.PAINTING_ARRAY.dot(Painting.TO_GALLERY).dot(Gallery.PAINTING_ARRAY).alias("p2").dot(Painting.PAINTING_TITLE).eq("painting4"))
+                .select(context);
+        assertEquals(1, artists.size());
+        assertEquals("artist4", artists.get(0).getArtistName());
+    }
+
+    @Test
+    public void testEntityPropertyAliases() {
+        Artist artist = Cayenne.objectForPK(context, Artist.class, 1);
+
+        SelectQuery query = SelectQuery.query(Painting.class);
+        Expression expression = Painting.TO_ARTIST.alias("p1").eq(artist);
+        query.setQualifier(expression);
+        List paintings = query.select(context);
+        assertEquals(2, paintings.size());
+        assertEquals("painting5", paintings.get(0).getPaintingTitle());
+    }
+
+    @Test
+    public void testAliases() {
+        SelectQuery query1 = new SelectQuery<>(Artist.class);
+        Expression expression = ExpressionFactory.and(
+                Artist.PAINTING_ARRAY.alias("p1").dot(Painting.PAINTING_TITLE).eq("painting2"),
+                Artist.PAINTING_ARRAY.alias("p2").dot(Painting.PAINTING_TITLE).eq("painting4")
+        );
+        query1.setQualifier(expression);
+        List artists = query1.select(context);
+        assertEquals(1, artists.size());
+        assertNotNull(artists.get(0));
+        assertEquals("artist4", artists.get(0).getArtistName());
+    }
+
+    @Test
+    public void testAliasForPath() {
+        ASTPath astPath = new ASTObjPath("a.galleryName");
+        astPath.setPathAliases(Collections.singletonMap("a", "paintingArray.toGallery"));
+        ASTEqual astEqual = new ASTEqual(astPath, "tate modern");
+        List artists = ObjectSelect.columnQuery(Artist.class, Artist.ARTIST_NAME, PropertyFactory.createBase(astPath, String.class))
+                .where(astEqual)
+                .orderBy(Artist.ARTIST_NAME.asc())
+                .select(context);
+        assertEquals(5, artists.size());
+        assertEquals("artist1", artists.get(0)[0]);
+    }
+
+    @Test
+    public void testAggregationWithAliases() {
+        List artistAndPaintingCount = ObjectSelect.columnQuery(Artist.class, Artist.ARTIST_NAME, Artist.PAINTING_ARRAY.count())
+                .having(Artist.PAINTING_ARRAY.alias("p1").count().lt(5L))
+                .select(context);
+        assertEquals(4, artistAndPaintingCount.size());
+        assertTrue((Long)artistAndPaintingCount.get(0)[1] < 5);
+    }
+
+    @Test
+    public void testOrderWithAlias() {
+        ObjectSelect query = ObjectSelect.query(Artist.class)
+                .orderBy(Artist.PAINTING_ARRAY.alias("p1").dot(Painting.ESTIMATED_PRICE).asc())
+                .prefetch(Artist.PAINTING_ARRAY.disjoint());
+        List artists = query.select(context);
+        assertEquals(5, artists.size());
+        assertEquals(2, artists.get(0).getPaintingArray().size());
+    }
+
+    @Test(expected = CayenneRuntimeException.class)
+    public void testPrefetchWithAliases() {
+        ObjectSelect query = ObjectSelect.query(Artist.class);
+        query.prefetch(Artist.PAINTING_ARRAY.alias("p1").disjoint());
+        query.select(context);
+    }
+}
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/exp/property/SetPropertyTest.java b/cayenne-server/src/test/java/org/apache/cayenne/exp/property/SetPropertyTest.java
index 2cd258fa8f..efba282074 100644
--- a/cayenne-server/src/test/java/org/apache/cayenne/exp/property/SetPropertyTest.java
+++ b/cayenne-server/src/test/java/org/apache/cayenne/exp/property/SetPropertyTest.java
@@ -33,21 +33,26 @@
 public class SetPropertyTest {
 
     private SetProperty property;
+    private SetProperty property1;
 
     @Before
     public void createProperty() {
         property = new SetProperty<>("path", null, Artist.class);
+        property1 = new SetProperty<>("path.artist", null, Artist.class);
     }
 
     @Test
     public void alias() {
         assertEquals("path", property.getName());
-        assertNull(property.getAlias());
-
         property = property.alias("alias");
-
         assertEquals("alias", property.getName());
-        assertEquals("alias", property.getAlias());
+        assertEquals(1, property.getExpression().getPathAliases().size());
+
+        assertEquals("path.artist", property1.getName());
+        property1 = property1.alias("a");
+        assertEquals("path.a", property1.getName());
+        assertEquals(1, property1.getExpression().getPathAliases().size());
+        assertEquals("artist", property1.getExpression().getPathAliases().get("a"));
     }
 
     @Test
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/query/ColumnSelectIT.java b/cayenne-server/src/test/java/org/apache/cayenne/query/ColumnSelectIT.java
index 04e45b5222..4d2ebab5ca 100644
--- a/cayenne-server/src/test/java/org/apache/cayenne/query/ColumnSelectIT.java
+++ b/cayenne-server/src/test/java/org/apache/cayenne/query/ColumnSelectIT.java
@@ -19,15 +19,6 @@
 
 package org.apache.cayenne.query;
 
-import java.math.BigDecimal;
-import java.math.BigInteger;
-import java.sql.SQLException;
-import java.sql.Types;
-import java.text.DateFormat;
-import java.util.Date;
-import java.util.List;
-import java.util.Locale;
-
 import org.apache.cayenne.CayenneRuntimeException;
 import org.apache.cayenne.Fault;
 import org.apache.cayenne.ObjectContext;
@@ -36,8 +27,6 @@
 import org.apache.cayenne.access.DataContext;
 import org.apache.cayenne.configuration.server.ServerRuntime;
 import org.apache.cayenne.di.Inject;
-import org.apache.cayenne.exp.Expression;
-import org.apache.cayenne.exp.FunctionExpressionFactory;
 import org.apache.cayenne.exp.property.EntityProperty;
 import org.apache.cayenne.exp.property.NumericProperty;
 import org.apache.cayenne.exp.property.PropertyFactory;
@@ -57,7 +46,21 @@
 import org.junit.Ignore;
 import org.junit.Test;
 
-import static org.junit.Assert.*;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.sql.SQLException;
+import java.sql.Types;
+import java.text.DateFormat;
+import java.util.Date;
+import java.util.List;
+import java.util.Locale;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
 
 /**
  * @since 4.0
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/query/ObjectSelectTest.java b/cayenne-server/src/test/java/org/apache/cayenne/query/ObjectSelectTest.java
index dd9e1f2fc9..ae069525c4 100644
--- a/cayenne-server/src/test/java/org/apache/cayenne/query/ObjectSelectTest.java
+++ b/cayenne-server/src/test/java/org/apache/cayenne/query/ObjectSelectTest.java
@@ -18,6 +18,16 @@
  ****************************************************************/
 package org.apache.cayenne.query;
 
+import org.apache.cayenne.DataRow;
+import org.apache.cayenne.exp.Expression;
+import org.apache.cayenne.exp.ExpressionFactory;
+import org.apache.cayenne.testdo.testmap.Artist;
+import org.junit.Test;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
@@ -25,16 +35,6 @@
 import static org.junit.Assert.assertSame;
 import static org.junit.Assert.assertTrue;
 
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-
-import org.apache.cayenne.DataRow;
-import org.apache.cayenne.exp.Expression;
-import org.apache.cayenne.exp.ExpressionFactory;
-import org.apache.cayenne.testdo.testmap.Artist;
-import org.junit.Test;
-
 public class ObjectSelectTest {
 
 	@Test
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/query/ObjectSelect_AggregateIT.java b/cayenne-server/src/test/java/org/apache/cayenne/query/ObjectSelect_AggregateIT.java
index e88c7cd65b..b5910bc2f4 100644
--- a/cayenne-server/src/test/java/org/apache/cayenne/query/ObjectSelect_AggregateIT.java
+++ b/cayenne-server/src/test/java/org/apache/cayenne/query/ObjectSelect_AggregateIT.java
@@ -19,15 +19,9 @@
 
 package org.apache.cayenne.query;
 
-import java.sql.Types;
-import java.text.DateFormat;
-import java.util.Arrays;
-import java.util.Date;
-import java.util.List;
-import java.util.Locale;
-
 import org.apache.cayenne.access.DataContext;
 import org.apache.cayenne.di.Inject;
+import org.apache.cayenne.exp.FunctionExpressionFactory;
 import org.apache.cayenne.exp.property.BaseProperty;
 import org.apache.cayenne.exp.property.NumericProperty;
 import org.apache.cayenne.exp.property.PropertyFactory;
@@ -42,7 +36,13 @@
 import org.junit.Ignore;
 import org.junit.Test;
 
-import static org.apache.cayenne.exp.FunctionExpressionFactory.*;
+import java.sql.Types;
+import java.text.DateFormat;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.List;
+import java.util.Locale;
+
 import static org.junit.Assert.assertEquals;
 
 /**
@@ -121,7 +121,7 @@ public void testCountDistinct() throws Exception {
     @Test
     @Ignore("Not all databases support AVG(DATE) aggregation")
     public void testAvg() throws Exception {
-        BaseProperty avgProp = PropertyFactory.createBase(avgExp(Artist.DATE_OF_BIRTH.getExpression()), Date.class);
+        BaseProperty avgProp = PropertyFactory.createBase(FunctionExpressionFactory.avgExp(Artist.DATE_OF_BIRTH.getExpression()), Date.class);
 
         Date avg = ObjectSelect.query(Artist.class)
                 .column(avgProp)
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/query/ObjectSelect_RunIT.java b/cayenne-server/src/test/java/org/apache/cayenne/query/ObjectSelect_RunIT.java
index c353e1cdca..df5632f1df 100644
--- a/cayenne-server/src/test/java/org/apache/cayenne/query/ObjectSelect_RunIT.java
+++ b/cayenne-server/src/test/java/org/apache/cayenne/query/ObjectSelect_RunIT.java
@@ -18,8 +18,6 @@
  ****************************************************************/
 package org.apache.cayenne.query;
 
-import java.util.List;
-
 import org.apache.cayenne.CayenneRuntimeException;
 import org.apache.cayenne.DataRow;
 import org.apache.cayenne.ResultBatchIterator;
@@ -35,6 +33,8 @@
 import org.junit.Before;
 import org.junit.Test;
 
+import java.util.List;
+
 import static org.hamcrest.CoreMatchers.instanceOf;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
@@ -195,5 +195,4 @@ public void test_SelectFirst_SubstringInWhere() {
 		assertNotNull(a);
 		assertEquals("artist1", a.getArtistName());
 	}
-
 }
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/query/SelectQueryIT.java b/cayenne-server/src/test/java/org/apache/cayenne/query/SelectQueryIT.java
index 1bd6d1b2e1..235261205a 100644
--- a/cayenne-server/src/test/java/org/apache/cayenne/query/SelectQueryIT.java
+++ b/cayenne-server/src/test/java/org/apache/cayenne/query/SelectQueryIT.java
@@ -19,18 +19,6 @@
 
 package org.apache.cayenne.query;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertSame;
-import static org.junit.Assert.assertTrue;
-
-import java.sql.ResultSet;
-import java.sql.Types;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-
 import org.apache.cayenne.Cayenne;
 import org.apache.cayenne.DataRow;
 import org.apache.cayenne.ObjectContext;
@@ -58,6 +46,17 @@
 import org.junit.Before;
 import org.junit.Test;
 
+import java.sql.Types;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+
 @UseServerRuntime(CayenneProjects.TESTMAP_PROJECT)
 public class SelectQueryIT extends ServerCase {
 

From fa1efbce67608a5267660d606cc9fd1169c363c5 Mon Sep 17 00:00:00 2001
From: Arseni Bulatski 
Date: Fri, 4 Jan 2019 11:58:19 +0300
Subject: [PATCH 2/2] Cleanup

---
 .../apache/cayenne/exp/parser/ASTDbPath.java  | 12 ++---
 .../apache/cayenne/exp/parser/ASTObjPath.java |  6 +--
 .../apache/cayenne/exp/parser/ASTPath.java    |  6 +--
 .../cayenne/exp/property/BaseProperty.java    | 10 ++--
 .../cayenne/exp/property/EntityProperty.java  |  8 ++-
 .../cayenne/exp/property/ListProperty.java    | 12 ++---
 .../cayenne/exp/property/MapProperty.java     | 15 +++---
 .../cayenne/exp/property/PropertyFactory.java |  4 +-
 .../cayenne/exp/property/PropertyUtils.java   | 50 ++++++-------------
 .../exp/property/RelationshipProperty.java    | 31 +++++++-----
 .../cayenne/exp/property/SetProperty.java     | 12 ++---
 .../cayenne/query/SelectQueryMetadata.java    | 30 +++++------
 .../exp/property/ListPropertyTest.java        |  8 +--
 .../cayenne/exp/property/MapPropertyTest.java |  4 +-
 ...pertyAliasesIT.java => PathAliasesIT.java} | 16 ++++--
 .../apache/cayenne/query/ColumnSelectIT.java  | 25 ++++------
 .../cayenne/query/ObjectSelectTest.java       | 20 ++++----
 .../query/ObjectSelect_AggregateIT.java       | 22 ++++----
 .../cayenne/query/ObjectSelect_RunIT.java     |  4 +-
 .../apache/cayenne/query/SelectQueryIT.java   | 22 ++++----
 20 files changed, 149 insertions(+), 168 deletions(-)
 rename cayenne-server/src/test/java/org/apache/cayenne/exp/property/{PropertyAliasesIT.java => PathAliasesIT.java} (94%)

diff --git a/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTDbPath.java b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTDbPath.java
index 870b84d160..6503bf245d 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTDbPath.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTDbPath.java
@@ -19,6 +19,12 @@
 
 package org.apache.cayenne.exp.parser;
 
+import java.io.IOException;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
 import org.apache.cayenne.Cayenne;
 import org.apache.cayenne.ObjectContext;
 import org.apache.cayenne.ObjectId;
@@ -33,12 +39,6 @@
 import org.apache.cayenne.query.SelectById;
 import org.apache.cayenne.util.CayenneMapEntry;
 
-import java.io.IOException;
-import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-
 /**
  * Path expression traversing DB relationships and attributes.
  * 
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTObjPath.java b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTObjPath.java
index c536fc664e..8535b2864b 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTObjPath.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTObjPath.java
@@ -19,6 +19,9 @@
 
 package org.apache.cayenne.exp.parser;
 
+import java.io.IOException;
+import java.util.List;
+
 import org.apache.cayenne.CayenneRuntimeException;
 import org.apache.cayenne.DataObject;
 import org.apache.cayenne.exp.Expression;
@@ -28,9 +31,6 @@
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import java.io.IOException;
-import java.util.List;
-
 public class ASTObjPath extends ASTPath {
 
 	private static final long serialVersionUID = -3574281576491705706L;
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTPath.java b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTPath.java
index f88b5703e7..8ab1a395ae 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTPath.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTPath.java
@@ -19,12 +19,12 @@
 
 package org.apache.cayenne.exp.parser;
 
-import org.apache.cayenne.map.Entity;
-import org.apache.cayenne.util.CayenneMapEntry;
-
 import java.util.Iterator;
 import java.util.Map;
 
+import org.apache.cayenne.map.Entity;
+import org.apache.cayenne.util.CayenneMapEntry;
+
 /**
  * Generic path expression.
  * 
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/exp/property/BaseProperty.java b/cayenne-server/src/main/java/org/apache/cayenne/exp/property/BaseProperty.java
index 28bc26408c..1ce3836bfb 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/exp/property/BaseProperty.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/exp/property/BaseProperty.java
@@ -19,6 +19,11 @@
 
 package org.apache.cayenne.exp.property;
 
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.function.Supplier;
+
 import org.apache.cayenne.exp.Expression;
 import org.apache.cayenne.exp.ExpressionFactory;
 import org.apache.cayenne.exp.FunctionExpressionFactory;
@@ -28,11 +33,6 @@
 import org.apache.cayenne.query.SortOrder;
 import org.apache.cayenne.reflect.PropertyUtils;
 
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-import java.util.function.Supplier;
-
 /**
  * Property that represents generic attribute.
  * 

diff --git a/cayenne-server/src/main/java/org/apache/cayenne/exp/property/EntityProperty.java b/cayenne-server/src/main/java/org/apache/cayenne/exp/property/EntityProperty.java index 1f087d60db..41c225f3db 100644 --- a/cayenne-server/src/main/java/org/apache/cayenne/exp/property/EntityProperty.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/exp/property/EntityProperty.java @@ -21,6 +21,7 @@ import org.apache.cayenne.Persistent; import org.apache.cayenne.exp.Expression; +import org.apache.cayenne.exp.parser.ASTPath; /** * Property that represents to-one relationships. @@ -56,11 +57,8 @@ protected EntityProperty(String name, Expression expression, Class type) { */ @Override public EntityProperty alias(String alias) { - String substrPath = PropertyUtils.substringPath(this.getName()); - String aliasedPath = substrPath + alias; - return PropertyFactory.createEntity(aliasedPath, - PropertyUtils.createPathExp(aliasedPath, this.getName().substring(substrPath.length()), alias, getExpression().getPathAliases()), - this.getType()); + ASTPath exp = PropertyUtils.createPathExp(this.getName(), alias, getExpression().getPathAliases()); + return PropertyFactory.createEntity(exp.getPath(), exp, this.getType()); } /** diff --git a/cayenne-server/src/main/java/org/apache/cayenne/exp/property/ListProperty.java b/cayenne-server/src/main/java/org/apache/cayenne/exp/property/ListProperty.java index d8021dc78e..ded00842c3 100644 --- a/cayenne-server/src/main/java/org/apache/cayenne/exp/property/ListProperty.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/exp/property/ListProperty.java @@ -19,10 +19,11 @@ package org.apache.cayenne.exp.property; +import java.util.List; + import org.apache.cayenne.Persistent; import org.apache.cayenne.exp.Expression; - -import java.util.List; +import org.apache.cayenne.exp.parser.ASTPath; /** * Property that represents to-many relationship mapped on {@link List}. @@ -52,11 +53,8 @@ protected ListProperty(String name, Expression expression, Class entityType) */ @Override public ListProperty alias(String alias) { - String substrPath = PropertyUtils.substringPath(this.getName()); - String aliasedPath = substrPath + alias; - return PropertyFactory.createList(aliasedPath, - PropertyUtils.createPathExp(aliasedPath, this.getName().substring(substrPath.length()), alias, getExpression().getPathAliases()), - this.getEntityType()); + ASTPath exp = PropertyUtils.createPathExp(this.getName(), alias, getExpression().getPathAliases()); + return PropertyFactory.createList(exp.getPath(), exp, this.getEntityType()); } /** diff --git a/cayenne-server/src/main/java/org/apache/cayenne/exp/property/MapProperty.java b/cayenne-server/src/main/java/org/apache/cayenne/exp/property/MapProperty.java index 1974de471a..b9b24b3c07 100644 --- a/cayenne-server/src/main/java/org/apache/cayenne/exp/property/MapProperty.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/exp/property/MapProperty.java @@ -19,12 +19,13 @@ package org.apache.cayenne.exp.property; +import java.util.Collection; +import java.util.Map; + import org.apache.cayenne.Persistent; import org.apache.cayenne.exp.Expression; import org.apache.cayenne.exp.ExpressionFactory; - -import java.util.Collection; -import java.util.Map; +import org.apache.cayenne.exp.parser.ASTPath; /** * Property that represents to-many relationship mapped on {@link Map}. @@ -204,12 +205,8 @@ public Expression notContainsId(Collection ids) { */ @Override public MapProperty alias(String alias) { - String substrPath = PropertyUtils.substringPath(this.getName()); - String aliasedPath = substrPath + alias; - return PropertyFactory.createMap(aliasedPath, - PropertyUtils.createPathExp(aliasedPath, this.getName().substring(substrPath.length()), alias, getExpression().getPathAliases()), - this.getKeyType(), - this.getEntityType()); + ASTPath exp = PropertyUtils.createPathExp(this.getName(), alias, getExpression().getPathAliases()); + return PropertyFactory.createMap(exp.getPath(), exp, this.getKeyType(), this.getEntityType()); } /** diff --git a/cayenne-server/src/main/java/org/apache/cayenne/exp/property/PropertyFactory.java b/cayenne-server/src/main/java/org/apache/cayenne/exp/property/PropertyFactory.java index e16d936869..c355d734ea 100644 --- a/cayenne-server/src/main/java/org/apache/cayenne/exp/property/PropertyFactory.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/exp/property/PropertyFactory.java @@ -19,14 +19,14 @@ package org.apache.cayenne.exp.property; +import java.time.LocalDateTime; + import org.apache.cayenne.ObjectContext; import org.apache.cayenne.Persistent; import org.apache.cayenne.exp.Expression; import org.apache.cayenne.exp.ExpressionFactory; import org.apache.cayenne.exp.FunctionExpressionFactory; -import java.time.LocalDateTime; - /** * * Factory class that produces all property types. diff --git a/cayenne-server/src/main/java/org/apache/cayenne/exp/property/PropertyUtils.java b/cayenne-server/src/main/java/org/apache/cayenne/exp/property/PropertyUtils.java index 43b41fc88b..098fc40bcd 100644 --- a/cayenne-server/src/main/java/org/apache/cayenne/exp/property/PropertyUtils.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/exp/property/PropertyUtils.java @@ -19,52 +19,30 @@ package org.apache.cayenne.exp.property; -import org.apache.cayenne.CayenneRuntimeException; -import org.apache.cayenne.exp.Expression; -import org.apache.cayenne.exp.parser.ASTObjPath; -import org.apache.cayenne.exp.parser.ASTPath; - import java.util.HashMap; import java.util.Map; +import org.apache.cayenne.exp.parser.ASTObjPath; +import org.apache.cayenne.exp.parser.ASTPath; + /** * @since 4.2 */ class PropertyUtils { - static ASTPath createPathExp(String aliasedPath, String segment, String alias, Map aliasMap) { - ASTPath pathExp = new ASTObjPath(aliasedPath); - Map aliases = new HashMap<>(aliasMap); - aliases.put(alias, segment); - pathExp.setPathAliases(aliases); + static ASTPath createPathExp(String path, String alias, Map aliasMap) { + int index = path.lastIndexOf("."); + String aliasedPath = index != -1 ? path.substring(0, index + 1) + alias : alias; + String segmentPath = path.substring(index != -1 ? index + 1 : 0); - return pathExp; + Map pathAliases = new HashMap<>(aliasMap); + pathAliases.put(alias, segmentPath); + return buildExp(aliasedPath, pathAliases); } - static ASTPath createExpressionWithCopiedAliases(String name, Expression expression) { - if(expression instanceof ASTPath) { - ASTPath pathExp = new ASTObjPath(name); - pathExp.setPathAliases(expression.getPathAliases()); - return pathExp; - } - - throw new CayenneRuntimeException("Dot is used only with path expressions."); - } - - static String substringPath(String propertyName){ - for(int i = propertyName.length() - 1; i >= 0; i--) { - if(propertyName.charAt(i) == '.') { - return propertyName.substring(0, i + 1); - } - } - - return ""; - } - - static void checkAliases(Expression expression) { - if(!expression.getPathAliases().isEmpty()) { - throw new CayenneRuntimeException("Can't use aliases with prefetch"); - } + static ASTPath buildExp(String path, Map pathAliases) { + ASTPath pathExp = new ASTObjPath(path); + pathExp.setPathAliases(pathAliases); + return pathExp; } - } diff --git a/cayenne-server/src/main/java/org/apache/cayenne/exp/property/RelationshipProperty.java b/cayenne-server/src/main/java/org/apache/cayenne/exp/property/RelationshipProperty.java index 76e9d5a10c..a4e4ba57c5 100644 --- a/cayenne-server/src/main/java/org/apache/cayenne/exp/property/RelationshipProperty.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/exp/property/RelationshipProperty.java @@ -19,6 +19,7 @@ package org.apache.cayenne.exp.property; +import org.apache.cayenne.CayenneRuntimeException; import org.apache.cayenne.Persistent; import org.apache.cayenne.query.PrefetchTreeNode; @@ -40,7 +41,7 @@ public interface RelationshipProperty extends Property { default BaseProperty dot(String property) { String path = getName() + "." + property; return PropertyFactory.createBase(path, - PropertyUtils.createExpressionWithCopiedAliases(path, getExpression()), + PropertyUtils.buildExp(path, getExpression().getPathAliases()), null); } @@ -53,7 +54,7 @@ default BaseProperty dot(String property) { default BaseProperty dot(BaseProperty property) { String path = getName() + "." + property.getName(); return PropertyFactory.createBase(path, - PropertyUtils.createExpressionWithCopiedAliases(path, getExpression()), + PropertyUtils.buildExp(path, getExpression().getPathAliases()), property.getType()); } @@ -66,7 +67,7 @@ default BaseProperty dot(BaseProperty property) { default NumericProperty dot(NumericProperty property) { String path = getName() + "." + property.getName(); return PropertyFactory.createNumeric(path, - PropertyUtils.createExpressionWithCopiedAliases(path, getExpression()), + PropertyUtils.buildExp(path, getExpression().getPathAliases()), property.getType()); } @@ -79,7 +80,7 @@ default NumericProperty dot(NumericProperty property) { default StringProperty dot(StringProperty property) { String path = getName() + "." + property.getName(); return PropertyFactory.createString(path, - PropertyUtils.createExpressionWithCopiedAliases(path, getExpression()), + PropertyUtils.buildExp(path, getExpression().getPathAliases()), property.getType()); } @@ -92,7 +93,7 @@ default StringProperty dot(StringProperty propert default DateProperty dot(DateProperty property) { String path = getName() + "." + property.getName(); return PropertyFactory.createDate(path, - PropertyUtils.createExpressionWithCopiedAliases(path, getExpression()), + PropertyUtils.buildExp(path, getExpression().getPathAliases()), property.getType()); } @@ -105,7 +106,7 @@ default DateProperty dot(DateProperty property) { default EntityProperty dot(EntityProperty property) { String path = getName() + "." + property.getName(); return PropertyFactory.createEntity(path, - PropertyUtils.createExpressionWithCopiedAliases(path, getExpression()), + PropertyUtils.buildExp(path, getExpression().getPathAliases()), property.getType()); } @@ -118,7 +119,7 @@ default EntityProperty dot(EntityProperty property) default ListProperty dot(ListProperty property) { String path = getName() + "." + property.getName(); return PropertyFactory.createList(path, - PropertyUtils.createExpressionWithCopiedAliases(path, getExpression()), + PropertyUtils.buildExp(path, getExpression().getPathAliases()), property.getEntityType()); } @@ -131,7 +132,7 @@ default ListProperty dot(ListProperty property) { default SetProperty dot(SetProperty property) { String path = getName() + "." + property.getName(); return PropertyFactory.createSet(path, - PropertyUtils.createExpressionWithCopiedAliases(path, getExpression()), + PropertyUtils.buildExp(path, getExpression().getPathAliases()), property.getEntityType()); } @@ -144,7 +145,7 @@ default SetProperty dot(SetProperty property) { default MapProperty dot(MapProperty property) { String path = getName() + "." + property.getName(); return PropertyFactory.createMap(path, - PropertyUtils.createExpressionWithCopiedAliases(path, getExpression()), + PropertyUtils.buildExp(path, getExpression().getPathAliases()), property.getKeyType(), property.getEntityType()); } @@ -162,7 +163,9 @@ default MapProperty dot(MapProperty proper * prefetch semantics. */ default PrefetchTreeNode joint() { - PropertyUtils.checkAliases(getExpression()); + if(!getExpression().getPathAliases().isEmpty()) { + throw new CayenneRuntimeException("Can't use aliases with prefetch"); + } return PrefetchTreeNode.withPath(getName(), PrefetchTreeNode.JOINT_PREFETCH_SEMANTICS); } @@ -172,7 +175,9 @@ default PrefetchTreeNode joint() { * "disjoint" prefetch semantics. */ default PrefetchTreeNode disjoint() { - PropertyUtils.checkAliases(getExpression()); + if(!getExpression().getPathAliases().isEmpty()) { + throw new CayenneRuntimeException("Can't use aliases with prefetch"); + } return PrefetchTreeNode.withPath(getName(), PrefetchTreeNode.DISJOINT_PREFETCH_SEMANTICS); } @@ -182,7 +187,9 @@ default PrefetchTreeNode disjoint() { * "disjoint by id" prefetch semantics. */ default PrefetchTreeNode disjointById() { - PropertyUtils.checkAliases(getExpression()); + if(!getExpression().getPathAliases().isEmpty()) { + throw new CayenneRuntimeException("Can't use aliases with prefetch"); + } return PrefetchTreeNode.withPath(getName(), PrefetchTreeNode.DISJOINT_BY_ID_PREFETCH_SEMANTICS); } diff --git a/cayenne-server/src/main/java/org/apache/cayenne/exp/property/SetProperty.java b/cayenne-server/src/main/java/org/apache/cayenne/exp/property/SetProperty.java index 908fab7ce8..12b1fd2994 100644 --- a/cayenne-server/src/main/java/org/apache/cayenne/exp/property/SetProperty.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/exp/property/SetProperty.java @@ -19,10 +19,11 @@ package org.apache.cayenne.exp.property; +import java.util.Set; + import org.apache.cayenne.Persistent; import org.apache.cayenne.exp.Expression; - -import java.util.Set; +import org.apache.cayenne.exp.parser.ASTPath; /** * Property that represents to-many relationship mapped on {@link Set}. @@ -48,11 +49,8 @@ protected SetProperty(String name, Expression expression, Class entityType) { */ @Override public SetProperty alias(String alias) { - String substrPath = PropertyUtils.substringPath(this.getName()); - String aliasedPath = substrPath + alias; - return PropertyFactory.createSet(aliasedPath, - PropertyUtils.createPathExp(aliasedPath, this.getName().substring(substrPath.length()), alias, getExpression().getPathAliases()), - this.getEntityType()); + ASTPath exp = PropertyUtils.createPathExp(this.getName(), alias, getExpression().getPathAliases()); + return PropertyFactory.createSet(exp.getPath(), exp, this.getEntityType()); } /** diff --git a/cayenne-server/src/main/java/org/apache/cayenne/query/SelectQueryMetadata.java b/cayenne-server/src/main/java/org/apache/cayenne/query/SelectQueryMetadata.java index 63e72f83ad..8a9e7d9e84 100644 --- a/cayenne-server/src/main/java/org/apache/cayenne/query/SelectQueryMetadata.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/query/SelectQueryMetadata.java @@ -18,6 +18,15 @@ ****************************************************************/ package org.apache.cayenne.query; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; + import org.apache.cayenne.CayenneRuntimeException; import org.apache.cayenne.ObjectId; import org.apache.cayenne.Persistent; @@ -48,15 +57,6 @@ import org.apache.cayenne.reflect.ToOneProperty; import org.apache.cayenne.util.CayenneMapEntry; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Set; - /** * @since 3.0 */ @@ -206,14 +206,16 @@ private void resolveAutoAliases(Expression expression) { if (pathSplitAliases == null) { pathSplitAliases = new HashMap<>(); } - for(String key : aliases.keySet()) { - if(pathSplitAliases.containsKey(key)) { - if(!pathSplitAliases.get(key).equals(aliases.get(key))) { + + for(Map.Entry entry : aliases.entrySet()) { + pathSplitAliases.compute(entry.getKey(), (key, value) -> { + if(value != null && !value.equals(entry.getValue())){ throw new CayenneRuntimeException("Can't add the same alias to different path segments."); + } else { + return entry.getValue(); } - } + }); } - pathSplitAliases.putAll(aliases); } int len = expression.getOperandCount(); diff --git a/cayenne-server/src/test/java/org/apache/cayenne/exp/property/ListPropertyTest.java b/cayenne-server/src/test/java/org/apache/cayenne/exp/property/ListPropertyTest.java index df875393d9..c8df661566 100644 --- a/cayenne-server/src/test/java/org/apache/cayenne/exp/property/ListPropertyTest.java +++ b/cayenne-server/src/test/java/org/apache/cayenne/exp/property/ListPropertyTest.java @@ -19,16 +19,16 @@ package org.apache.cayenne.exp.property; +import java.util.Arrays; +import java.util.Collection; + import org.apache.cayenne.exp.Expression; import org.apache.cayenne.exp.ExpressionFactory; import org.apache.cayenne.testdo.testmap.Artist; import org.junit.Before; import org.junit.Test; -import java.util.Arrays; -import java.util.Collection; - -import static org.junit.Assert.assertEquals; +import static org.junit.Assert.*; /** * @since 4.2 diff --git a/cayenne-server/src/test/java/org/apache/cayenne/exp/property/MapPropertyTest.java b/cayenne-server/src/test/java/org/apache/cayenne/exp/property/MapPropertyTest.java index 5545942633..0a866adb45 100644 --- a/cayenne-server/src/test/java/org/apache/cayenne/exp/property/MapPropertyTest.java +++ b/cayenne-server/src/test/java/org/apache/cayenne/exp/property/MapPropertyTest.java @@ -19,14 +19,14 @@ package org.apache.cayenne.exp.property; +import java.util.Arrays; + import org.apache.cayenne.exp.Expression; import org.apache.cayenne.exp.ExpressionFactory; import org.apache.cayenne.testdo.testmap.Artist; import org.junit.Before; import org.junit.Test; -import java.util.Arrays; - import static org.junit.Assert.assertEquals; /** diff --git a/cayenne-server/src/test/java/org/apache/cayenne/exp/property/PropertyAliasesIT.java b/cayenne-server/src/test/java/org/apache/cayenne/exp/property/PathAliasesIT.java similarity index 94% rename from cayenne-server/src/test/java/org/apache/cayenne/exp/property/PropertyAliasesIT.java rename to cayenne-server/src/test/java/org/apache/cayenne/exp/property/PathAliasesIT.java index 5dbfe1423d..45fdee740e 100644 --- a/cayenne-server/src/test/java/org/apache/cayenne/exp/property/PropertyAliasesIT.java +++ b/cayenne-server/src/test/java/org/apache/cayenne/exp/property/PathAliasesIT.java @@ -19,6 +19,9 @@ package org.apache.cayenne.exp.property; +import java.util.Collections; +import java.util.List; + import org.apache.cayenne.Cayenne; import org.apache.cayenne.CayenneRuntimeException; import org.apache.cayenne.access.DataContext; @@ -41,9 +44,6 @@ import org.junit.Before; import org.junit.Test; -import java.util.Collections; -import java.util.List; - import static junit.framework.TestCase.assertTrue; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; @@ -52,7 +52,7 @@ * @since 4.2 */ @UseServerRuntime(CayenneProjects.TESTMAP_PROJECT) -public class PropertyAliasesIT extends ServerCase { +public class PathAliasesIT extends ServerCase { @Inject private DataContext context; @@ -186,4 +186,12 @@ public void testPrefetchWithAliases() { query.prefetch(Artist.PAINTING_ARRAY.alias("p1").disjoint()); query.select(context); } + + @Test(expected = CayenneRuntimeException.class) + public void testTheSameAliasesToDifferentProperties() { + ObjectSelect query = ObjectSelect.query(Artist.class); + query.where(Artist.PAINTING_ARRAY.alias("p1").dot(Painting.PAINTING_TITLE).eq("p1")); + query.and(Artist.PAINTING_ARRAY.dot(Painting.TO_GALLERY).alias("p1").dot(Gallery.GALLERY_NAME).eq("g1")); + query.select(context); + } } diff --git a/cayenne-server/src/test/java/org/apache/cayenne/query/ColumnSelectIT.java b/cayenne-server/src/test/java/org/apache/cayenne/query/ColumnSelectIT.java index 4d2ebab5ca..5e8f95aafe 100644 --- a/cayenne-server/src/test/java/org/apache/cayenne/query/ColumnSelectIT.java +++ b/cayenne-server/src/test/java/org/apache/cayenne/query/ColumnSelectIT.java @@ -19,6 +19,15 @@ package org.apache.cayenne.query; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.sql.SQLException; +import java.sql.Types; +import java.text.DateFormat; +import java.util.Date; +import java.util.List; +import java.util.Locale; + import org.apache.cayenne.CayenneRuntimeException; import org.apache.cayenne.Fault; import org.apache.cayenne.ObjectContext; @@ -46,21 +55,7 @@ import org.junit.Ignore; import org.junit.Test; -import java.math.BigDecimal; -import java.math.BigInteger; -import java.sql.SQLException; -import java.sql.Types; -import java.text.DateFormat; -import java.util.Date; -import java.util.List; -import java.util.Locale; - -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; +import static org.junit.Assert.*; /** * @since 4.0 diff --git a/cayenne-server/src/test/java/org/apache/cayenne/query/ObjectSelectTest.java b/cayenne-server/src/test/java/org/apache/cayenne/query/ObjectSelectTest.java index ae069525c4..dd9e1f2fc9 100644 --- a/cayenne-server/src/test/java/org/apache/cayenne/query/ObjectSelectTest.java +++ b/cayenne-server/src/test/java/org/apache/cayenne/query/ObjectSelectTest.java @@ -18,16 +18,6 @@ ****************************************************************/ package org.apache.cayenne.query; -import org.apache.cayenne.DataRow; -import org.apache.cayenne.exp.Expression; -import org.apache.cayenne.exp.ExpressionFactory; -import org.apache.cayenne.testdo.testmap.Artist; -import org.junit.Test; - -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; - import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; @@ -35,6 +25,16 @@ import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; + +import org.apache.cayenne.DataRow; +import org.apache.cayenne.exp.Expression; +import org.apache.cayenne.exp.ExpressionFactory; +import org.apache.cayenne.testdo.testmap.Artist; +import org.junit.Test; + public class ObjectSelectTest { @Test diff --git a/cayenne-server/src/test/java/org/apache/cayenne/query/ObjectSelect_AggregateIT.java b/cayenne-server/src/test/java/org/apache/cayenne/query/ObjectSelect_AggregateIT.java index b5910bc2f4..80a67b4542 100644 --- a/cayenne-server/src/test/java/org/apache/cayenne/query/ObjectSelect_AggregateIT.java +++ b/cayenne-server/src/test/java/org/apache/cayenne/query/ObjectSelect_AggregateIT.java @@ -19,9 +19,15 @@ package org.apache.cayenne.query; +import java.sql.Types; +import java.text.DateFormat; +import java.util.Arrays; +import java.util.Date; +import java.util.List; +import java.util.Locale; + import org.apache.cayenne.access.DataContext; import org.apache.cayenne.di.Inject; -import org.apache.cayenne.exp.FunctionExpressionFactory; import org.apache.cayenne.exp.property.BaseProperty; import org.apache.cayenne.exp.property.NumericProperty; import org.apache.cayenne.exp.property.PropertyFactory; @@ -36,13 +42,7 @@ import org.junit.Ignore; import org.junit.Test; -import java.sql.Types; -import java.text.DateFormat; -import java.util.Arrays; -import java.util.Date; -import java.util.List; -import java.util.Locale; - +import static org.apache.cayenne.exp.FunctionExpressionFactory.*; import static org.junit.Assert.assertEquals; /** @@ -101,7 +101,7 @@ public void testCount() { .selectOne(context); assertEquals(20L, count); } - + @Test public void testCountDistinct() throws Exception { List artists = ObjectSelect.query(Artist.class).select(context); @@ -121,7 +121,7 @@ public void testCountDistinct() throws Exception { @Test @Ignore("Not all databases support AVG(DATE) aggregation") public void testAvg() throws Exception { - BaseProperty avgProp = PropertyFactory.createBase(FunctionExpressionFactory.avgExp(Artist.DATE_OF_BIRTH.getExpression()), Date.class); + BaseProperty avgProp = PropertyFactory.createBase(avgExp(Artist.DATE_OF_BIRTH.getExpression()), Date.class); Date avg = ObjectSelect.query(Artist.class) .column(avgProp) @@ -178,4 +178,4 @@ public void testSelectRelationshipCountWithAnotherField() throws Exception { assertEquals("artist1", result[0]); assertEquals(4L, result[1]); } -} +} \ No newline at end of file diff --git a/cayenne-server/src/test/java/org/apache/cayenne/query/ObjectSelect_RunIT.java b/cayenne-server/src/test/java/org/apache/cayenne/query/ObjectSelect_RunIT.java index df5632f1df..3414ba7bf0 100644 --- a/cayenne-server/src/test/java/org/apache/cayenne/query/ObjectSelect_RunIT.java +++ b/cayenne-server/src/test/java/org/apache/cayenne/query/ObjectSelect_RunIT.java @@ -18,6 +18,8 @@ ****************************************************************/ package org.apache.cayenne.query; +import java.util.List; + import org.apache.cayenne.CayenneRuntimeException; import org.apache.cayenne.DataRow; import org.apache.cayenne.ResultBatchIterator; @@ -33,8 +35,6 @@ import org.junit.Before; import org.junit.Test; -import java.util.List; - import static org.hamcrest.CoreMatchers.instanceOf; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; diff --git a/cayenne-server/src/test/java/org/apache/cayenne/query/SelectQueryIT.java b/cayenne-server/src/test/java/org/apache/cayenne/query/SelectQueryIT.java index 235261205a..869a5e0d52 100644 --- a/cayenne-server/src/test/java/org/apache/cayenne/query/SelectQueryIT.java +++ b/cayenne-server/src/test/java/org/apache/cayenne/query/SelectQueryIT.java @@ -19,6 +19,17 @@ package org.apache.cayenne.query; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; + +import java.sql.Types; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + import org.apache.cayenne.Cayenne; import org.apache.cayenne.DataRow; import org.apache.cayenne.ObjectContext; @@ -46,17 +57,6 @@ import org.junit.Before; import org.junit.Test; -import java.sql.Types; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertSame; -import static org.junit.Assert.assertTrue; - @UseServerRuntime(CayenneProjects.TESTMAP_PROJECT) public class SelectQueryIT extends ServerCase {