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/09/02 10:37:56 UTC

[cayenne] branch master updated (51958e1 -> 93bcf44)

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

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


    from 51958e1  fix tests
     new 2debe8e  CAY-2565 New PK properties
     new 6233d03  Merge PR #403
     new b61e0e6  Merge PR #403
     new 93bcf44  fix tests

The 4 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../java/org/apache/cayenne/gen/PropertyUtils.java |  33 +-
 .../main/resources/templates/v4_1/singleclass.vm   |   2 +-
 .../main/resources/templates/v4_1/superclass.vm    |   2 +-
 .../cayenne/event/JMSBridgeProviderTest.java       |   6 +
 ...nResult.java => DbIdPathTranslationResult.java} |  38 +-
 .../translator/select/PathTranslationResult.java   |   6 +-
 .../access/translator/select/PathTranslator.java   |  36 +
 .../translator/select/QualifierTranslator.java     |  14 +-
 .../java/org/apache/cayenne/exp/Expression.java    |   5 +
 .../org/apache/cayenne/exp/ExpressionFactory.java  |  76 +++
 .../org/apache/cayenne/exp/parser/ASTDbIdPath.java | 169 +++++
 .../cayenne/exp/parser/ExpressionParser.java       |  32 +-
 .../exp/parser/ExpressionParserConstants.java      |  35 +-
 .../exp/parser/ExpressionParserTokenManager.java   | 740 +++++++++++----------
 .../exp/parser/ExpressionParserTreeConstants.java  |   4 +-
 .../cayenne/exp/property/BaseIdProperty.java       |  59 ++
 .../apache/cayenne/exp/property/IdProperty.java    |  58 ++
 .../cayenne/exp/property/NumericIdProperty.java    |  59 ++
 .../cayenne/exp/property/PropertyFactory.java      |  16 +
 .../cayenne/exp/property/RelationshipProperty.java |   8 +
 .../apache/cayenne/exp/parser/ExpressionParser.jjt |   2 +
 .../apache/cayenne/exp/ExpressionFactoryTest.java  |  40 ++
 .../apache/cayenne/exp/parser/ASTDbIdPathTest.java |  77 +++
 .../apache/cayenne/exp/property/IdPropertyIT.java  | 244 +++++++
 .../cayenne/exp/property/IdPropertyTest.java       |  81 +++
 .../cayenne/testdo/testmap/auto/_ArtGroup.java     |   5 +-
 .../cayenne/testdo/testmap/auto/_Artist.java       |   5 +-
 .../testdo/testmap/auto/_ArtistCallback.java       |   5 +-
 .../testdo/testmap/auto/_ArtistExhibit.java        |   7 +-
 .../testdo/testmap/auto/_CompoundPainting.java     |   4 +-
 .../testmap/auto/_CompoundPaintingLongNames.java   |   4 +-
 .../cayenne/testdo/testmap/auto/_Exhibit.java      |   5 +-
 .../cayenne/testdo/testmap/auto/_Gallery.java      |   5 +-
 .../auto/_MeaningfulGeneratedColumnTestEntity.java |   1 -
 .../testdo/testmap/auto/_NullTestEntity.java       |   5 +-
 .../cayenne/testdo/testmap/auto/_Painting.java     |   4 +-
 .../cayenne/testdo/testmap/auto/_Painting1.java    |   4 +-
 .../cayenne/testdo/testmap/auto/_PaintingInfo.java |   5 +-
 .../cayenne/testdo/testmap/auto/_ROArtist.java     |   5 +-
 .../cayenne/testdo/testmap/auto/_ROPainting.java   |   4 +-
 .../testdo/testmap/auto/_RWCompoundPainting.java   |   4 +-
 .../cayenne/testdo/testmap/auto/_SubPainting.java  |   5 +-
 cayenne-server/src/test/resources/testmap.map.xml  |  50 +-
 .../cayenne/event/XMPPBridgeProviderTest.java      |   6 +
 44 files changed, 1474 insertions(+), 501 deletions(-)
 copy cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/{PathTranslationResult.java => DbIdPathTranslationResult.java} (61%)
 create mode 100644 cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTDbIdPath.java
 create mode 100644 cayenne-server/src/main/java/org/apache/cayenne/exp/property/BaseIdProperty.java
 create mode 100644 cayenne-server/src/main/java/org/apache/cayenne/exp/property/IdProperty.java
 create mode 100644 cayenne-server/src/main/java/org/apache/cayenne/exp/property/NumericIdProperty.java
 create mode 100644 cayenne-server/src/test/java/org/apache/cayenne/exp/parser/ASTDbIdPathTest.java
 create mode 100644 cayenne-server/src/test/java/org/apache/cayenne/exp/property/IdPropertyIT.java
 create mode 100644 cayenne-server/src/test/java/org/apache/cayenne/exp/property/IdPropertyTest.java


[cayenne] 03/04: Merge PR #403

Posted by nt...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit b61e0e670108607cd45e8ed0df89b77cafd1881b
Author: Nikita Timofeev <st...@gmail.com>
AuthorDate: Mon Sep 2 13:32:26 2019 +0300

    Merge PR #403
---
 .../cayenne/access/translator/select/PathTranslationResult.java     | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/PathTranslationResult.java b/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/PathTranslationResult.java
index fe4968b..39ee889 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/PathTranslationResult.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/PathTranslationResult.java
@@ -37,12 +37,14 @@ interface PathTranslationResult {
 
     Optional<DbRelationship> getDbRelationship();
 
-    Optional<Embeddable> getEmbeddable();
-
     List<DbAttribute> getDbAttributes();
 
     List<String> getAttributePaths();
 
+    default Optional<Embeddable> getEmbeddable() {
+        return Optional.empty();
+    }
+
     default DbAttribute getLastAttribute() {
         return getDbAttributes().get(getDbAttributes().size() - 1);
     }


[cayenne] 02/04: Merge PR #403

Posted by nt...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 6233d03052346700a996d05e3e0766814a3711f6
Merge: 51958e1 2debe8e
Author: Nikita Timofeev <st...@gmail.com>
AuthorDate: Mon Sep 2 13:19:54 2019 +0300

    Merge PR #403

 .../java/org/apache/cayenne/gen/PropertyUtils.java |  33 +-
 .../main/resources/templates/v4_1/singleclass.vm   |   2 +-
 .../main/resources/templates/v4_1/superclass.vm    |   2 +-
 .../select/DbIdPathTranslationResult.java          |  62 ++
 .../access/translator/select/PathTranslator.java   |  36 +
 .../translator/select/QualifierTranslator.java     |  14 +-
 .../java/org/apache/cayenne/exp/Expression.java    |   5 +
 .../org/apache/cayenne/exp/ExpressionFactory.java  |  76 +++
 .../org/apache/cayenne/exp/parser/ASTDbIdPath.java | 169 +++++
 .../cayenne/exp/parser/ExpressionParser.java       |  32 +-
 .../exp/parser/ExpressionParserConstants.java      |  35 +-
 .../exp/parser/ExpressionParserTokenManager.java   | 740 +++++++++++----------
 .../exp/parser/ExpressionParserTreeConstants.java  |   4 +-
 .../cayenne/exp/property/BaseIdProperty.java       |  59 ++
 .../apache/cayenne/exp/property/IdProperty.java    |  58 ++
 .../cayenne/exp/property/NumericIdProperty.java    |  59 ++
 .../cayenne/exp/property/PropertyFactory.java      |  16 +
 .../cayenne/exp/property/RelationshipProperty.java |   8 +
 .../apache/cayenne/exp/parser/ExpressionParser.jjt |   2 +
 .../apache/cayenne/exp/ExpressionFactoryTest.java  |  40 ++
 .../apache/cayenne/exp/parser/ASTDbIdPathTest.java |  77 +++
 .../apache/cayenne/exp/property/IdPropertyIT.java  | 244 +++++++
 .../cayenne/exp/property/IdPropertyTest.java       |  81 +++
 .../cayenne/testdo/testmap/auto/_ArtGroup.java     |   5 +-
 .../cayenne/testdo/testmap/auto/_Artist.java       |   5 +-
 .../testdo/testmap/auto/_ArtistCallback.java       |   5 +-
 .../testdo/testmap/auto/_ArtistExhibit.java        |   7 +-
 .../testdo/testmap/auto/_CompoundPainting.java     |   4 +-
 .../testmap/auto/_CompoundPaintingLongNames.java   |   4 +-
 .../cayenne/testdo/testmap/auto/_Exhibit.java      |   5 +-
 .../cayenne/testdo/testmap/auto/_Gallery.java      |   5 +-
 .../auto/_MeaningfulGeneratedColumnTestEntity.java |   1 -
 .../testdo/testmap/auto/_NullTestEntity.java       |   5 +-
 .../cayenne/testdo/testmap/auto/_Painting.java     |   4 +-
 .../cayenne/testdo/testmap/auto/_Painting1.java    |   4 +-
 .../cayenne/testdo/testmap/auto/_PaintingInfo.java |   5 +-
 .../cayenne/testdo/testmap/auto/_ROArtist.java     |   5 +-
 .../cayenne/testdo/testmap/auto/_ROPainting.java   |   4 +-
 .../testdo/testmap/auto/_RWCompoundPainting.java   |   4 +-
 .../cayenne/testdo/testmap/auto/_SubPainting.java  |   5 +-
 cayenne-server/src/test/resources/testmap.map.xml  |  50 +-
 41 files changed, 1497 insertions(+), 484 deletions(-)


[cayenne] 01/04: CAY-2565 New PK properties

Posted by nt...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 2debe8ebc7bc27ea4140543c39ce634210f9ce90
Author: Nikita Timofeev <st...@gmail.com>
AuthorDate: Wed Aug 28 17:47:47 2019 +0300

    CAY-2565 New PK properties
---
 .../java/org/apache/cayenne/gen/PropertyUtils.java |  33 +-
 .../main/resources/templates/v4_1/singleclass.vm   |   2 +-
 .../main/resources/templates/v4_1/superclass.vm    |   2 +-
 .../select/DbIdPathTranslationResult.java          |  62 ++
 .../access/translator/select/PathTranslator.java   |  36 +
 .../translator/select/QualifierTranslator.java     |  14 +-
 .../java/org/apache/cayenne/exp/Expression.java    |   5 +
 .../org/apache/cayenne/exp/ExpressionFactory.java  |  76 +++
 .../org/apache/cayenne/exp/parser/ASTDbIdPath.java | 169 +++++
 .../cayenne/exp/parser/ExpressionParser.java       |  32 +-
 .../exp/parser/ExpressionParserConstants.java      |  35 +-
 .../exp/parser/ExpressionParserTokenManager.java   | 740 +++++++++++----------
 .../exp/parser/ExpressionParserTreeConstants.java  |   4 +-
 .../cayenne/exp/property/BaseIdProperty.java       |  59 ++
 .../apache/cayenne/exp/property/IdProperty.java    |  58 ++
 .../cayenne/exp/property/NumericIdProperty.java    |  59 ++
 .../cayenne/exp/property/PropertyFactory.java      |  16 +
 .../cayenne/exp/property/RelationshipProperty.java |   8 +
 .../apache/cayenne/exp/parser/ExpressionParser.jjt |   2 +
 .../apache/cayenne/exp/ExpressionFactoryTest.java  |  40 ++
 .../apache/cayenne/exp/parser/ASTDbIdPathTest.java |  77 +++
 .../apache/cayenne/exp/property/IdPropertyIT.java  | 244 +++++++
 .../cayenne/exp/property/IdPropertyTest.java       |  81 +++
 .../cayenne/testdo/testmap/auto/_ArtGroup.java     |   5 +-
 .../cayenne/testdo/testmap/auto/_Artist.java       |   5 +-
 .../testdo/testmap/auto/_ArtistCallback.java       |   5 +-
 .../testdo/testmap/auto/_ArtistExhibit.java        |   7 +-
 .../testdo/testmap/auto/_CompoundPainting.java     |   4 +-
 .../testmap/auto/_CompoundPaintingLongNames.java   |   4 +-
 .../cayenne/testdo/testmap/auto/_Exhibit.java      |   5 +-
 .../cayenne/testdo/testmap/auto/_Gallery.java      |   5 +-
 .../auto/_MeaningfulGeneratedColumnTestEntity.java |   1 -
 .../testdo/testmap/auto/_NullTestEntity.java       |   5 +-
 .../cayenne/testdo/testmap/auto/_Painting.java     |   4 +-
 .../cayenne/testdo/testmap/auto/_Painting1.java    |   4 +-
 .../cayenne/testdo/testmap/auto/_PaintingInfo.java |   5 +-
 .../cayenne/testdo/testmap/auto/_ROArtist.java     |   5 +-
 .../cayenne/testdo/testmap/auto/_ROPainting.java   |   4 +-
 .../testdo/testmap/auto/_RWCompoundPainting.java   |   4 +-
 .../cayenne/testdo/testmap/auto/_SubPainting.java  |   5 +-
 cayenne-server/src/test/resources/testmap.map.xml  |  50 +-
 41 files changed, 1497 insertions(+), 484 deletions(-)

diff --git a/cayenne-cgen/src/main/java/org/apache/cayenne/gen/PropertyUtils.java b/cayenne-cgen/src/main/java/org/apache/cayenne/gen/PropertyUtils.java
index 08277e1..e337889 100644
--- a/cayenne-cgen/src/main/java/org/apache/cayenne/gen/PropertyUtils.java
+++ b/cayenne-cgen/src/main/java/org/apache/cayenne/gen/PropertyUtils.java
@@ -33,12 +33,14 @@ import org.apache.cayenne.dba.TypesMapping;
 import org.apache.cayenne.di.AdhocObjectFactory;
 import org.apache.cayenne.di.DIRuntimeException;
 import org.apache.cayenne.exp.ExpressionFactory;
+import org.apache.cayenne.exp.property.BaseIdProperty;
 import org.apache.cayenne.exp.property.BaseProperty;
 import org.apache.cayenne.exp.property.DateProperty;
 import org.apache.cayenne.exp.property.EmbeddableProperty;
 import org.apache.cayenne.exp.property.EntityProperty;
 import org.apache.cayenne.exp.property.ListProperty;
 import org.apache.cayenne.exp.property.MapProperty;
+import org.apache.cayenne.exp.property.NumericIdProperty;
 import org.apache.cayenne.exp.property.NumericProperty;
 import org.apache.cayenne.exp.property.PropertyFactory;
 import org.apache.cayenne.exp.property.SetProperty;
@@ -72,6 +74,8 @@ public class PropertyUtils {
         FACTORY_METHODS.put(SetProperty.class.getName(), "createSet");
         FACTORY_METHODS.put(MapProperty.class.getName(), "createMap");
         FACTORY_METHODS.put(EmbeddableProperty.class.getName(), "createEmbeddable");
+        FACTORY_METHODS.put(NumericIdProperty.class.getName(), "createNumericId");
+        FACTORY_METHODS.put(BaseIdProperty.class.getName(), "createBaseId");
     }
 
     private static final List<Class<?>> JAVA_DATE_TYPES = Arrays.asList(
@@ -115,14 +119,13 @@ public class PropertyUtils {
             if(!entityUtils.declaresDbAttribute(attribute)) {
                 String javaBySqlType = TypesMapping.getJavaBySqlType(attribute.getType());
                 importUtils.addType(javaBySqlType);
-                importUtils.addType(getPropertyTypeForType(javaBySqlType));
+                importUtils.addType(getPkPropertyTypeForType(javaBySqlType));
                 needToCreatePK = true;
             }
         }
 
         if(needToCreatePK) {
             importUtils.addType(PropertyFactory.class.getName());
-            importUtils.addType(ExpressionFactory.class.getName());
         }
     }
 
@@ -164,20 +167,21 @@ public class PropertyUtils {
         }
     }
 
-    public String propertyDefinition(DbAttribute attribute) throws ClassNotFoundException {
+    public String propertyDefinition(ObjEntity entity, DbAttribute attribute) throws ClassNotFoundException {
         StringUtils utils = StringUtils.getInstance();
 
         String attributeType = TypesMapping.getJavaBySqlType(attribute.getType());
-        String propertyType = getPropertyTypeForType(TypesMapping.getJavaBySqlType(attribute.getType()));
+        String propertyType = getPkPropertyTypeForType(TypesMapping.getJavaBySqlType(attribute.getType()));
         String propertyFactoryMethod = factoryMethodForPropertyType(propertyType);
         attributeType = importUtils.formatJavaType(attributeType, false);
 
-        return String.format("public static final %s<%s> %s = PropertyFactory.%s(ExpressionFactory.dbPathExp(\"%s\"), %s.class);",
+        return String.format("public static final %s<%s> %s = PropertyFactory.%s(\"%s\", \"%s\", %s.class);",
                 importUtils.formatJavaType(propertyType),
                 attributeType,
                 utils.capitalizedAsConstant(attribute.getName()) + PK_PROPERTY_SUFFIX,
                 propertyFactoryMethod,
                 attribute.getName(),
+                entity.getName(),
                 attributeType
         );
     }
@@ -338,25 +342,12 @@ public class PropertyUtils {
         return FACTORY_METHODS.get(propertyType);
     }
 
-    private String getPropertyTypeForType(String attributeType) throws ClassNotFoundException {
-        if(TypesMapping.JAVA_BYTES.equals(attributeType)) {
-            return BaseProperty.class.getName();
-        }
-
+    private String getPkPropertyTypeForType(String attributeType) throws ClassNotFoundException {
         Class<?> javaClass = Class.forName(attributeType);
         if (Number.class.isAssignableFrom(javaClass)) {
-            return NumericProperty.class.getName();
-        }
-
-        if (CharSequence.class.isAssignableFrom(javaClass)) {
-            return StringProperty.class.getName();
+            return NumericIdProperty.class.getName();
         }
-
-        if (JAVA_DATE_TYPES.contains(javaClass)) {
-            return DateProperty.class.getName();
-        }
-
-        return BaseProperty.class.getName();
+        return BaseIdProperty.class.getName();
     }
 
     private String getPropertyTypeForJavaClass(ObjRelationship relationship) {
diff --git a/cayenne-cgen/src/main/resources/templates/v4_1/singleclass.vm b/cayenne-cgen/src/main/resources/templates/v4_1/singleclass.vm
index f7eeea3..ed607be 100644
--- a/cayenne-cgen/src/main/resources/templates/v4_1/singleclass.vm
+++ b/cayenne-cgen/src/main/resources/templates/v4_1/singleclass.vm
@@ -73,7 +73,7 @@ public#if("true" == "${object.isAbstract()}") abstract#end class ${subClassName}
     #foreach( $idAttr in ${object.DbEntity.PrimaryKeys} )
         #if( $createPKProperties && !${entityUtils.declaresDbAttribute($idAttr)})
             #set ( $type = "$importUtils.dbAttributeToJavaType($idAttr)")
-    $propertyUtils.propertyDefinition($idAttr)
+    $propertyUtils.propertyDefinition($object, $idAttr)
         #else
     public static final String ${stringUtils.capitalizedAsConstant($idAttr.Name)}_PK_COLUMN = "${idAttr.Name}";
         #end
diff --git a/cayenne-cgen/src/main/resources/templates/v4_1/superclass.vm b/cayenne-cgen/src/main/resources/templates/v4_1/superclass.vm
index 8acf724..a269bbb 100644
--- a/cayenne-cgen/src/main/resources/templates/v4_1/superclass.vm
+++ b/cayenne-cgen/src/main/resources/templates/v4_1/superclass.vm
@@ -81,7 +81,7 @@ public abstract class ${superClassName} extends ${baseClassName} {
     #foreach( $idAttr in ${object.DbEntity.PrimaryKeys} )
         #if( $createPKProperties && !${entityUtils.declaresDbAttribute($idAttr)})
             #set ( $type = "$importUtils.dbAttributeToJavaType($idAttr)")
-    $propertyUtils.propertyDefinition($idAttr)
+    $propertyUtils.propertyDefinition($object, $idAttr)
         #end
     public static final String ${stringUtils.capitalizedAsConstant($idAttr.Name)}_PK_COLUMN = "${idAttr.Name}";
     #end
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/DbIdPathTranslationResult.java b/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/DbIdPathTranslationResult.java
new file mode 100644
index 0000000..bf7da72
--- /dev/null
+++ b/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/DbIdPathTranslationResult.java
@@ -0,0 +1,62 @@
+/*****************************************************************
+ *   Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    https://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ ****************************************************************/
+
+package org.apache.cayenne.access.translator.select;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+
+import org.apache.cayenne.map.DbAttribute;
+import org.apache.cayenne.map.DbRelationship;
+
+/**
+ * @since 4.2
+ */
+class DbIdPathTranslationResult implements PathTranslationResult {
+
+    private final String path;
+    private final DbAttribute pk;
+
+    DbIdPathTranslationResult(String path, DbAttribute pk) {
+        this.path = Objects.requireNonNull(path);
+        this.pk = Objects.requireNonNull(pk);
+    }
+
+    @Override
+    public String getFinalPath() {
+        return path;
+    }
+
+    @Override
+    public Optional<DbRelationship> getDbRelationship() {
+        return Optional.empty();
+    }
+
+    @Override
+    public List<DbAttribute> getDbAttributes() {
+        return Collections.singletonList(pk);
+    }
+
+    @Override
+    public List<String> getAttributePaths() {
+        return Collections.singletonList(path);
+    }
+}
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/PathTranslator.java b/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/PathTranslator.java
index 442b7aa..f010503 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/PathTranslator.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/PathTranslator.java
@@ -22,7 +22,11 @@ package org.apache.cayenne.access.translator.select;
 import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
 
+import org.apache.cayenne.CayenneRuntimeException;
+import org.apache.cayenne.map.DbAttribute;
 import org.apache.cayenne.map.DbEntity;
+import org.apache.cayenne.map.DbRelationship;
+import org.apache.cayenne.map.JoinType;
 import org.apache.cayenne.map.ObjEntity;
 
 /**
@@ -61,4 +65,36 @@ class PathTranslator {
         return translatePath(entity, path, null);
     }
 
+    PathTranslationResult translateIdPath(ObjEntity entity, String path) {
+        int lastSegmentPos = path.lastIndexOf('.');
+        String objPathPart = lastSegmentPos == -1 ? "" : path.substring(0, lastSegmentPos);
+        String pkName = lastSegmentPos == -1 ? path : path.substring(lastSegmentPos + 1);
+        if(objPathPart.isEmpty()) {
+            // get PK directly from the query root
+            if(pkName.isEmpty()) {
+                throw new CayenneRuntimeException("Can't translate empty dbid path");
+            }
+            DbAttribute pk = entity.getDbEntity().getAttribute(pkName);
+            if(pk == null) {
+                throw new CayenneRuntimeException("Can't translate dbid path '%s', no such pk", path);
+            }
+            return new DbIdPathTranslationResult("", pk);
+        } else {
+            // resolve object path part and get PK from the target entity
+            PathTranslationResult dbIdResult = translatePath(entity, objPathPart);
+            DbRelationship relationship = dbIdResult.getDbRelationship()
+                    .orElseThrow(() -> new CayenneRuntimeException("Can't translate dbid path '%s', can't resolve relationship %s", path, objPathPart));
+
+            // manually join last segment as obj path translation would skip it
+            JoinType joinType = objPathPart.endsWith("+") ? JoinType.LEFT_OUTER : JoinType.INNER;
+            context.getTableTree().addJoinTable(dbIdResult.getFinalPath(), relationship, joinType);
+
+            DbAttribute pk = relationship.getTargetEntity().getAttribute(pkName);
+            if(pk == null) {
+                throw new CayenneRuntimeException("Can't translate dbid path '%s', no such pk", path);
+            }
+            return new DbIdPathTranslationResult(dbIdResult.getFinalPath(), pk);
+        }
+    }
+
 }
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/QualifierTranslator.java b/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/QualifierTranslator.java
index bc08469..8112ce1 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/QualifierTranslator.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/access/translator/select/QualifierTranslator.java
@@ -47,6 +47,7 @@ import org.apache.cayenne.access.sqlbuilder.sqltree.OpExpressionNode;
 import org.apache.cayenne.access.sqlbuilder.sqltree.TextNode;
 import org.apache.cayenne.exp.Expression;
 import org.apache.cayenne.exp.TraversalHandler;
+import org.apache.cayenne.exp.parser.ASTDbIdPath;
 import org.apache.cayenne.exp.parser.ASTDbPath;
 import org.apache.cayenne.exp.parser.ASTFullObject;
 import org.apache.cayenne.exp.parser.ASTFunctionCall;
@@ -177,6 +178,11 @@ class QualifierTranslator implements TraversalHandler {
                 PathTranslationResult dbResult = pathTranslator.translatePath(context.getMetadata().getDbEntity(), dbPath);
                 return processPathTranslationResult(node, parentNode, dbResult);
 
+            case DBID_PATH:
+                String dbIdPath = (String)node.getOperand(0);
+                PathTranslationResult dbIdResult = pathTranslator.translateIdPath(context.getMetadata().getObjEntity(), dbIdPath);
+                return processPathTranslationResult(node, parentNode, dbIdResult);
+
             case FUNCTION_CALL:
                 ASTFunctionCall functionCall = (ASTFunctionCall)node;
                 return function(functionCall.getFunctionName()).build();
@@ -359,7 +365,7 @@ class QualifierTranslator implements TraversalHandler {
         switch (node.getType()) {
             case NOT_IN: case IN: case NOT_BETWEEN: case BETWEEN: case NOT:
             case BITWISE_NOT: case EQUAL_TO: case NOT_EQUAL_TO: case LIKE: case NOT_LIKE:
-            case LIKE_IGNORE_CASE: case NOT_LIKE_IGNORE_CASE: case OBJ_PATH: case DB_PATH:
+            case LIKE_IGNORE_CASE: case NOT_LIKE_IGNORE_CASE: case OBJ_PATH: case DBID_PATH: case DB_PATH:
             case FUNCTION_CALL: case ADD: case SUBTRACT: case MULTIPLY: case DIVIDE: case NEGATIVE:
             case BITWISE_AND: case BITWISE_LEFT_SHIFT: case BITWISE_OR: case BITWISE_RIGHT_SHIFT: case BITWISE_XOR:
             case OR: case AND: case LESS_THAN: case LESS_THAN_EQUAL_TO: case GREATER_THAN: case GREATER_THAN_EQUAL_TO:
@@ -387,7 +393,7 @@ class QualifierTranslator implements TraversalHandler {
         if(expressionsToSkip.contains(parentNode)) {
             return;
         }
-        if(parentNode.getType() == Expression.OBJ_PATH || parentNode.getType() == Expression.DB_PATH) {
+        if(parentNode.getType() == OBJ_PATH || parentNode.getType() == DB_PATH || parentNode.getType() == DBID_PATH) {
             return;
         }
 
@@ -421,6 +427,9 @@ class QualifierTranslator implements TraversalHandler {
             if(op instanceof ASTObjPath) {
                 result = pathTranslator.translatePath(context.getMetadata().getObjEntity(), ((ASTObjPath) op).getPath());
                 break;
+            } else if(op instanceof ASTDbIdPath) {
+                result = pathTranslator.translateIdPath(context.getMetadata().getObjEntity(), ((ASTDbIdPath)op).getPath());
+                break;
             } else if(op instanceof ASTDbPath) {
                 result = pathTranslator.translatePath(context.getMetadata().getDbEntity(), ((ASTDbPath) op).getPath());
                 break;
@@ -484,5 +493,4 @@ class QualifierTranslator implements TraversalHandler {
                 return "{other}";
         }
     }
-
 }
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/exp/Expression.java b/cayenne-server/src/main/java/org/apache/cayenne/exp/Expression.java
index e5bfa6f..689745f 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/exp/Expression.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/exp/Expression.java
@@ -179,6 +179,11 @@ public abstract class Expression implements Serializable, XMLSerializable {
 	 */
 	public static final int SUBQUERY = 50;
 
+	/**
+	 * @since 4.2
+	 */
+	public static final int DBID_PATH = 51;
+
 	protected int type;
 
 	/**
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 9ce4bca..3eaf00b 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
@@ -29,6 +29,7 @@ import org.apache.cayenne.exp.parser.ASTBitwiseNot;
 import org.apache.cayenne.exp.parser.ASTBitwiseOr;
 import org.apache.cayenne.exp.parser.ASTBitwiseRightShift;
 import org.apache.cayenne.exp.parser.ASTBitwiseXor;
+import org.apache.cayenne.exp.parser.ASTDbIdPath;
 import org.apache.cayenne.exp.parser.ASTDbPath;
 import org.apache.cayenne.exp.parser.ASTDivide;
 import org.apache.cayenne.exp.parser.ASTEnclosingObject;
@@ -415,6 +416,22 @@ public class ExpressionFactory {
 	}
 
 	/**
+	 * A convenience method to create an DBID_PATH "equal to" expression.
+	 * @since 4.2
+	 */
+	public static Expression matchDbIdExp(String pathSpec, Object value) {
+		return matchExp(new ASTDbIdPath(pathSpec), value);
+	}
+
+	/**
+	 * A convenience method to create an DBID_PATH "not equal to" expression.
+	 * @since 4.2
+	 */
+	public static Expression noMatchDbIdExp(String pathSpec, Object value) {
+		return noMatchExp(new ASTDbIdPath(pathSpec), value);
+	}
+
+	/**
 	 * A convenience method to create an OBJ_PATH "less than" expression.
 	 */
 	public static Expression lessExp(String pathSpec, Object value) {
@@ -568,6 +585,18 @@ public class ExpressionFactory {
 	}
 
 	/**
+	 * A convenience shortcut for building IN DBID expression. Return ASTFalse for
+	 * empty collection.
+	 * @since 4.2
+	 */
+	public static Expression inDbIdExp(String pathSpec, Object... values) {
+		if (values.length == 0) {
+			return new ASTFalse();
+		}
+		return new ASTIn(new ASTDbIdPath(pathSpec), new ASTList(values));
+	}
+
+	/**
 	 * @since 4.0
 	 * @see ExpressionFactory#inExp(String, Collection)
 	 */
@@ -593,6 +622,18 @@ public class ExpressionFactory {
 	}
 
 	/**
+	 * A convenience shortcut for building IN DBID expression. Return ASTFalse for
+	 * empty collection.
+	 * @since 4.2
+	 */
+	public static Expression inDbIdExp(String pathSpec, Collection<?> values) {
+		if (values.isEmpty()) {
+			return new ASTFalse();
+		}
+		return new ASTIn(new ASTDbIdPath(pathSpec), new ASTList(values));
+	}
+
+	/**
 	 * A convenience shortcut for building NOT_IN expression. Return ASTTrue for
 	 * empty collection.
 	 */
@@ -630,6 +671,19 @@ public class ExpressionFactory {
 	/**
 	 * A convenience shortcut for building NOT_IN expression. Return ASTTrue for
 	 * empty collection.
+	 *
+	 * @since 4.2
+	 */
+	public static Expression notInDbIdExp(String pathSpec, Collection<?> values) {
+		if (values.isEmpty()) {
+			return new ASTTrue();
+		}
+		return new ASTNotIn(new ASTDbIdPath(pathSpec), new ASTList(values));
+	}
+
+	/**
+	 * A convenience shortcut for building NOT_IN expression. Return ASTTrue for
+	 * empty collection.
 	 * 
 	 * @since 1.0.6
 	 */
@@ -665,6 +719,19 @@ public class ExpressionFactory {
 	}
 
 	/**
+	 * A convenience shortcut for building NOT_IN expression. Return ASTTrue for
+	 * empty collection.
+	 *
+	 * @since 4.2
+	 */
+	public static Expression notInDbIdExp(String pathSpec, Object... values) {
+		if (values.length == 0) {
+			return new ASTTrue();
+		}
+		return new ASTNotIn(new ASTDbIdPath(pathSpec), new ASTList(values));
+	}
+
+	/**
 	 * A convenience shortcut for building BETWEEN expressions.
 	 */
 	public static Expression betweenExp(String pathSpec, Object value1, Object value2) {
@@ -1148,6 +1215,15 @@ public class ExpressionFactory {
 	}
 
 	/**
+	 * @param pathSpec a String "dbid:" path
+	 * @return a new "dbid:" path expressiob fofr the specified String path
+	 * @since 4.2
+	 */
+	public static Expression dbIdPathExp(String pathSpec) {
+		return new ASTDbIdPath(pathSpec);
+	}
+
+	/**
 	 * A convenience shortcut for boolean true expression.
 	 * 
 	 * @since 3.0
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTDbIdPath.java b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTDbIdPath.java
new file mode 100644
index 0000000..30568f7
--- /dev/null
+++ b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ASTDbIdPath.java
@@ -0,0 +1,169 @@
+/*****************************************************************
+ *   Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    https://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ ****************************************************************/
+
+package org.apache.cayenne.exp.parser;
+
+import java.io.IOException;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import org.apache.cayenne.CayenneRuntimeException;
+import org.apache.cayenne.DataObject;
+import org.apache.cayenne.ObjectId;
+import org.apache.cayenne.Persistent;
+import org.apache.cayenne.exp.Expression;
+import org.apache.cayenne.map.DbAttribute;
+import org.apache.cayenne.map.Entity;
+import org.apache.cayenne.map.ObjEntity;
+import org.apache.cayenne.map.ObjRelationship;
+import org.apache.cayenne.util.CayenneMapEntry;
+
+/**
+ * @since 4.2
+ */
+public class ASTDbIdPath extends ASTDbPath {
+
+    public static final String DBID_PREFIX = "dbid:";
+
+    ASTDbIdPath(int id) {
+        super(id);
+    }
+
+    public ASTDbIdPath() {
+        super(ExpressionParserTreeConstants.JJTDBIDPATH);
+    }
+
+    public ASTDbIdPath(Object value) {
+        super(ExpressionParserTreeConstants.JJTDBIDPATH);
+        setPath(value);
+    }
+
+    /**
+     * Creates a copy of this expression node, without copying children.
+     */
+    @Override
+    public Expression shallowCopy() {
+        ASTDbIdPath copy = new ASTDbIdPath(id);
+        copy.path = path;
+        return copy;
+    }
+
+    protected Object evaluateNode(Object o, String localPath) {
+        int lastDot = localPath.lastIndexOf('.');
+        String id;
+        String nextSegment;
+        if (lastDot != -1) {
+            // nested entity
+            String objPath = localPath.substring(0, lastDot);
+            id = localPath.substring(lastDot + 1);
+            if(o instanceof DataObject) {
+                o = ((DataObject) o).readNestedProperty(objPath);
+                nextSegment = id;
+            } else {
+                nextSegment = localPath;
+            }
+        } else {
+            id = localPath;
+            nextSegment = localPath;
+        }
+
+        if (o instanceof DataObject) {
+            return toMap(o).get(id);
+        } else if(o instanceof Collection) {
+            return ((Collection<?>) o).stream()
+                    .map(o1 -> evaluateNode(o1, nextSegment))
+                    .collect(Collectors.toList());
+        }
+
+        return null;
+    }
+
+    @Override
+    protected Object evaluateNode(Object o) {
+        if (o instanceof Entity) {
+            return evaluateEntityNode((Entity) o);
+        }
+        return evaluateNode(o, path);
+    }
+
+    @Override
+    protected CayenneMapEntry evaluateEntityNode(Entity entity) {
+        int lastDot = path.lastIndexOf('.');
+        String objPath = null;
+        String id = path;
+        if(lastDot > -1) {
+            objPath = path.substring(0, lastDot);
+            id = path.substring(lastDot + 1);
+        }
+
+        if(!(entity instanceof ObjEntity)) {
+            throw new CayenneRuntimeException("Unable to evaluate DBID path for DbEntity");
+        }
+
+        ObjEntity objEntity = (ObjEntity)entity;
+
+        if(objPath != null) {
+            CayenneMapEntry entry = new ASTObjPath(objPath).evaluateEntityNode(objEntity);
+            if(!(entry instanceof ObjRelationship)) {
+                throw new CayenneRuntimeException("Unable to evaluate DBID path %s, relationship expected", path);
+            }
+            objEntity = ((ObjRelationship) entry).getTargetEntity();
+        }
+
+        DbAttribute pk = objEntity.getDbEntity().getAttribute(id);
+        if(pk == null || !pk.isPrimaryKey()) {
+            throw new CayenneRuntimeException("Unable to find PK %s for entity %s", id, objEntity.getName());
+        }
+        return pk;
+    }
+
+    @Override
+    protected Map<?, ?> toMap(Object o) {
+        if (o instanceof Map) {
+            return (Map<?, ?>) o;
+        } else if (o instanceof ObjectId) {
+            return ((ObjectId) o).getIdSnapshot();
+        } else if (o instanceof Persistent) {
+            return ((Persistent) o).getObjectId().getIdSnapshot();
+        } else {
+            return null;
+        }
+    }
+
+    @Override
+    public int getType() {
+        return Expression.DBID_PATH;
+    }
+
+    @Override
+    public void appendAsEJBQL(List<Object> parameterAccumulator, Appendable out, String rootId) throws IOException {
+        // NOTE: append as db path
+        out.append(DB_PREFIX);
+        out.append(rootId);
+        out.append('.');
+        out.append(path);
+    }
+
+    @Override
+    public void appendAsString(Appendable out) throws IOException {
+        out.append(DBID_PREFIX).append(path);
+    }
+}
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ExpressionParser.java b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ExpressionParser.java
index 9c0e3f0..ed46175 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ExpressionParser.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ExpressionParser.java
@@ -201,6 +201,7 @@ public class ExpressionParser/*@bgen(jjtree)*/implements ExpressionParserTreeCon
     case 67:
     case 68:
     case 69:
+    case 70:
     case PROPERTY_PATH:
     case SINGLE_QUOTED_STRING:
     case DOUBLE_QUOTED_STRING:
@@ -279,6 +280,7 @@ public class ExpressionParser/*@bgen(jjtree)*/implements ExpressionParserTreeCon
     case 67:
     case 68:
     case 69:
+    case 70:
     case PROPERTY_PATH:
     case SINGLE_QUOTED_STRING:
     case DOUBLE_QUOTED_STRING:
@@ -842,6 +844,7 @@ public class ExpressionParser/*@bgen(jjtree)*/implements ExpressionParserTreeCon
     case 67:
     case 68:
     case 69:
+    case 70:
     case PROPERTY_PATH:
     case INT_LITERAL:
     case FLOAT_LITERAL:
@@ -892,6 +895,7 @@ public class ExpressionParser/*@bgen(jjtree)*/implements ExpressionParserTreeCon
     case 67:
     case 68:
     case 69:
+    case 70:
     case PROPERTY_PATH:
       pathExpression();
       break;
@@ -1010,6 +1014,7 @@ public class ExpressionParser/*@bgen(jjtree)*/implements ExpressionParserTreeCon
     case 67:
     case 68:
     case 69:
+    case 70:
     case PROPERTY_PATH:
     case SINGLE_QUOTED_STRING:
     case DOUBLE_QUOTED_STRING:
@@ -1533,6 +1538,7 @@ public class ExpressionParser/*@bgen(jjtree)*/implements ExpressionParserTreeCon
     case 67:
     case 68:
     case 69:
+    case 70:
     case PROPERTY_PATH:
     case INT_LITERAL:
     case FLOAT_LITERAL:
@@ -1596,6 +1602,7 @@ public class ExpressionParser/*@bgen(jjtree)*/implements ExpressionParserTreeCon
     case 67:
     case 68:
     case 69:
+    case 70:
     case PROPERTY_PATH:
     case INT_LITERAL:
     case FLOAT_LITERAL:
@@ -1703,6 +1710,7 @@ public class ExpressionParser/*@bgen(jjtree)*/implements ExpressionParserTreeCon
     case 67:
     case 68:
     case 69:
+    case 70:
     case PROPERTY_PATH:
       pathExpression();
       break;
@@ -1794,6 +1802,7 @@ public class ExpressionParser/*@bgen(jjtree)*/implements ExpressionParserTreeCon
         case 67:
         case 68:
         case 69:
+        case 70:
         case PROPERTY_PATH:
         case INT_LITERAL:
         case FLOAT_LITERAL:
@@ -2263,6 +2272,7 @@ public class ExpressionParser/*@bgen(jjtree)*/implements ExpressionParserTreeCon
       case 67:
       case 68:
       case 69:
+      case 70:
       case PROPERTY_PATH:
         pathExpression();
         break;
@@ -2669,6 +2679,22 @@ public class ExpressionParser/*@bgen(jjtree)*/implements ExpressionParserTreeCon
                                    }
       }
       break;
+    case 70:
+      jj_consume_token(70);
+      t = jj_consume_token(PROPERTY_PATH);
+                                   ASTDbIdPath jjtn005 = new ASTDbIdPath(JJTDBIDPATH);
+                                   boolean jjtc005 = true;
+                                   jjtree.openNodeScope(jjtn005);
+      try {
+                                   jjtree.closeNodeScope(jjtn005,  0);
+                                   jjtc005 = false;
+                                   ExpressionUtils.parsePath(jjtn005, t.image);
+      } finally {
+                                   if (jjtc005) {
+                                     jjtree.closeNodeScope(jjtn005,  0);
+                                   }
+      }
+      break;
     default:
       jj_la1[44] = jj_gen;
       jj_consume_token(-1);
@@ -2701,7 +2727,7 @@ public class ExpressionParser/*@bgen(jjtree)*/implements ExpressionParserTreeCon
       jj_la1_1 = new int[] {0x0,0x0,0x0,0xfffffdfe,0x0,0x0,0x0,0x0,0x0,0xfffffdfe,0x0,0x0,0x0,0x0,0xfffffdf2,0xf800,0x0,0xf800,0xfffffdfe,0xc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff1f0400,0x0,0xff1f0400,0xff1f0400,0xf800,0x0,0xff1ffc00,0x0,0x0,0xff1f0000,0x0,0x1f0,0x200,0xe00000,0xff000000,0x0,};
    }
    private static void jj_la1_init_2() {
-      jj_la1_2 = new int[] {0x0,0x0,0x0,0x1c80bf,0x0,0x0,0x4,0x0,0x0,0x1c80bf,0x0,0x4,0x0,0x0,0x1c80bf,0x480b8,0x48000,0x48000,0x1c80bf,0x1c8004,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x40,0x40,0x1800bf,0x0,0x1800bf,0x1800bf,0x0,0x0,0x1c80bf,0x0,0x0,0x3,0x0,0x0,0xf8,0x0,0x3,0xb8,};
+      jj_la1_2 = new int[] {0x0,0x0,0x0,0x39017f,0x0,0x0,0x4,0x0,0x0,0x39017f,0x0,0x4,0x0,0x0,0x39017f,0x90178,0x90000,0x90000,0x39017f,0x390004,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x80,0x80,0x30017f,0x0,0x30017f,0x30017f,0x0,0x0,0x39017f,0x0,0x0,0x3,0x0,0x0,0x1f8,0x0,0x3,0x178,};
    }
 
   /** Constructor with InputStream. */
@@ -2821,7 +2847,7 @@ public class ExpressionParser/*@bgen(jjtree)*/implements ExpressionParserTreeCon
   /** Generate ParseException. */
   public ParseException generateParseException() {
     jj_expentries.clear();
-    boolean[] la1tokens = new boolean[89];
+    boolean[] la1tokens = new boolean[90];
     if (jj_kind >= 0) {
       la1tokens[jj_kind] = true;
       jj_kind = -1;
@@ -2841,7 +2867,7 @@ public class ExpressionParser/*@bgen(jjtree)*/implements ExpressionParserTreeCon
         }
       }
     }
-    for (int i = 0; i < 89; i++) {
+    for (int i = 0; i < 90; i++) {
       if (la1tokens[i]) {
         jj_expentry = new int[1];
         jj_expentry[0] = i;
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ExpressionParserConstants.java b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ExpressionParserConstants.java
index 58ba093..ee0b22b 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ExpressionParserConstants.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ExpressionParserConstants.java
@@ -97,35 +97,35 @@ public interface ExpressionParserConstants {
   /** RegularExpression Id. */
   int SECOND = 65;
   /** RegularExpression Id. */
-  int ASTERISK = 70;
+  int ASTERISK = 71;
   /** RegularExpression Id. */
-  int PROPERTY_PATH = 71;
+  int PROPERTY_PATH = 72;
   /** RegularExpression Id. */
-  int IDENTIFIER = 72;
+  int IDENTIFIER = 73;
   /** RegularExpression Id. */
-  int LETTER = 73;
+  int LETTER = 74;
   /** RegularExpression Id. */
-  int DIGIT = 74;
+  int DIGIT = 75;
   /** RegularExpression Id. */
-  int ESC = 77;
+  int ESC = 78;
   /** RegularExpression Id. */
-  int SINGLE_QUOTED_STRING = 79;
+  int SINGLE_QUOTED_STRING = 80;
   /** RegularExpression Id. */
-  int STRING_ESC = 80;
+  int STRING_ESC = 81;
   /** RegularExpression Id. */
-  int DOUBLE_QUOTED_STRING = 82;
+  int DOUBLE_QUOTED_STRING = 83;
   /** RegularExpression Id. */
-  int INT_LITERAL = 83;
+  int INT_LITERAL = 84;
   /** RegularExpression Id. */
-  int FLOAT_LITERAL = 84;
+  int FLOAT_LITERAL = 85;
   /** RegularExpression Id. */
-  int DEC_FLT = 85;
+  int DEC_FLT = 86;
   /** RegularExpression Id. */
-  int DEC_DIGITS = 86;
+  int DEC_DIGITS = 87;
   /** RegularExpression Id. */
-  int EXPONENT = 87;
+  int EXPONENT = 88;
   /** RegularExpression Id. */
-  int FLT_SUFF = 88;
+  int FLT_SUFF = 89;
 
   /** Lexical state. */
   int DEFAULT = 0;
@@ -206,6 +206,7 @@ public interface ExpressionParserConstants {
     "\"obj:\"",
     "\"db:\"",
     "\"enum:\"",
+    "\"dbid:\"",
     "\"*\"",
     "<PROPERTY_PATH>",
     "<IDENTIFIER>",
@@ -214,10 +215,10 @@ public interface ExpressionParserConstants {
     "\"\\\'\"",
     "\"\\\"\"",
     "<ESC>",
-    "<token of kind 78>",
+    "<token of kind 79>",
     "\"\\\'\"",
     "<STRING_ESC>",
-    "<token of kind 81>",
+    "<token of kind 82>",
     "\"\\\"\"",
     "<INT_LITERAL>",
     "<FLOAT_LITERAL>",
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ExpressionParserTokenManager.java b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ExpressionParserTokenManager.java
index 45dcb1f..268e281 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ExpressionParserTokenManager.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ExpressionParserTokenManager.java
@@ -108,56 +108,56 @@ private final int jjStopStringLiteralDfa_0(int pos, long active0, long active1)
    switch (pos)
    {
       case 0:
-         if ((active0 & 0xff1fd2f00004e006L) != 0L || (active1 & 0x3bL) != 0L)
-         {
-            jjmatchedKind = 71;
-            return 83;
-         }
          if ((active0 & 0x60090000000000L) != 0L)
          {
-            jjmatchedKind = 71;
+            jjmatchedKind = 72;
             return 36;
          }
          if ((active0 & 0x8L) != 0L)
          {
-            jjmatchedKind = 71;
+            jjmatchedKind = 72;
             return 63;
          }
          if ((active0 & 0x200000000000L) != 0L)
          {
-            jjmatchedKind = 71;
+            jjmatchedKind = 72;
             return 6;
          }
          if ((active0 & 0x40000000000L) != 0L)
          {
-            jjmatchedKind = 71;
+            jjmatchedKind = 72;
             return 15;
          }
+         if ((active0 & 0xff1fd2f00004e006L) != 0L || (active1 & 0x7bL) != 0L)
+         {
+            jjmatchedKind = 72;
+            return 83;
+         }
          return -1;
       case 1:
          if ((active0 & 0x40000008002L) != 0L)
             return 83;
-         if ((active0 & 0xff1fdbf000046004L) != 0L || (active1 & 0x3bL) != 0L)
+         if ((active0 & 0x200000000000L) != 0L)
          {
-            jjmatchedKind = 71;
+            jjmatchedKind = 72;
             jjmatchedPos = 1;
-            return 83;
+            return 5;
          }
-         if ((active0 & 0x200000000000L) != 0L)
+         if ((active0 & 0xff1fdbf000046004L) != 0L || (active1 & 0x7bL) != 0L)
          {
-            jjmatchedKind = 71;
+            jjmatchedKind = 72;
             jjmatchedPos = 1;
-            return 5;
+            return 83;
          }
          if ((active0 & 0x60000000000000L) != 0L)
          {
-            jjmatchedKind = 71;
+            jjmatchedKind = 72;
             jjmatchedPos = 1;
             return 35;
          }
          if ((active0 & 0x8L) != 0L)
          {
-            jjmatchedKind = 71;
+            jjmatchedKind = 72;
             jjmatchedPos = 1;
             return 64;
          }
@@ -169,16 +169,16 @@ private final int jjStopStringLiteralDfa_0(int pos, long active0, long active1)
          {
             if (jjmatchedPos != 2)
             {
-               jjmatchedKind = 71;
+               jjmatchedKind = 72;
                jjmatchedPos = 2;
             }
             return 34;
          }
-         if ((active0 & 0x870bfb0000046000L) != 0L || (active1 & 0x2aL) != 0L)
+         if ((active0 & 0x870bfb0000046000L) != 0L || (active1 & 0x6aL) != 0L)
          {
             if (jjmatchedPos != 2)
             {
-               jjmatchedKind = 71;
+               jjmatchedKind = 72;
                jjmatchedPos = 2;
             }
             return 83;
@@ -187,11 +187,11 @@ private final int jjStopStringLiteralDfa_0(int pos, long active0, long active1)
       case 3:
          if ((active0 & 0x8508200000006000L) != 0L)
             return 83;
-         if ((active0 & 0x6a03db0000040000L) != 0L || (active1 & 0x23L) != 0L)
+         if ((active0 & 0x6a03db0000040000L) != 0L || (active1 & 0x63L) != 0L)
          {
             if (jjmatchedPos != 3)
             {
-               jjmatchedKind = 71;
+               jjmatchedKind = 72;
                jjmatchedPos = 3;
             }
             return 83;
@@ -200,7 +200,7 @@ private final int jjStopStringLiteralDfa_0(int pos, long active0, long active1)
          {
             if (jjmatchedPos != 3)
             {
-               jjmatchedKind = 71;
+               jjmatchedKind = 72;
                jjmatchedPos = 3;
             }
             return 33;
@@ -211,31 +211,31 @@ private final int jjStopStringLiteralDfa_0(int pos, long active0, long active1)
             return 83;
          if ((active0 & 0x60000000000000L) != 0L)
          {
-            jjmatchedKind = 71;
+            jjmatchedKind = 72;
             jjmatchedPos = 4;
             return 32;
          }
          if ((active0 & 0x68031a0000044000L) != 0L || (active1 & 0x3L) != 0L)
          {
-            jjmatchedKind = 71;
+            jjmatchedKind = 72;
             jjmatchedPos = 4;
             return 83;
          }
          return -1;
       case 5:
-         if ((active0 & 0x60000000000000L) != 0L)
+         if ((active0 & 0x6800120000044000L) != 0L)
          {
-            jjmatchedKind = 71;
+            jjmatchedKind = 72;
             jjmatchedPos = 5;
-            return 31;
+            return 83;
          }
          if ((active0 & 0x3080000000000L) != 0L || (active1 & 0x3L) != 0L)
             return 83;
-         if ((active0 & 0x6800120000044000L) != 0L)
+         if ((active0 & 0x60000000000000L) != 0L)
          {
-            jjmatchedKind = 71;
+            jjmatchedKind = 72;
             jjmatchedPos = 5;
-            return 83;
+            return 31;
          }
          return -1;
       case 6:
@@ -243,13 +243,13 @@ private final int jjStopStringLiteralDfa_0(int pos, long active0, long active1)
             return 83;
          if ((active0 & 0x6800120000004000L) != 0L)
          {
-            jjmatchedKind = 71;
+            jjmatchedKind = 72;
             jjmatchedPos = 6;
             return 83;
          }
          if ((active0 & 0x60000000000000L) != 0L)
          {
-            jjmatchedKind = 71;
+            jjmatchedKind = 72;
             jjmatchedPos = 6;
             return 30;
          }
@@ -259,13 +259,13 @@ private final int jjStopStringLiteralDfa_0(int pos, long active0, long active1)
             return 83;
          if ((active0 & 0x40000000000000L) != 0L)
          {
-            jjmatchedKind = 71;
+            jjmatchedKind = 72;
             jjmatchedPos = 7;
             return 29;
          }
          if ((active0 & 0x6820100000004000L) != 0L)
          {
-            jjmatchedKind = 71;
+            jjmatchedKind = 72;
             jjmatchedPos = 7;
             return 83;
          }
@@ -275,13 +275,13 @@ private final int jjStopStringLiteralDfa_0(int pos, long active0, long active1)
             return 83;
          if ((active0 & 0x2020000000004000L) != 0L)
          {
-            jjmatchedKind = 71;
+            jjmatchedKind = 72;
             jjmatchedPos = 8;
             return 83;
          }
          if ((active0 & 0x40000000000000L) != 0L)
          {
-            jjmatchedKind = 71;
+            jjmatchedKind = 72;
             jjmatchedPos = 8;
             return 28;
          }
@@ -291,33 +291,33 @@ private final int jjStopStringLiteralDfa_0(int pos, long active0, long active1)
             return 83;
          if ((active0 & 0x40000000000000L) != 0L)
          {
-            jjmatchedKind = 71;
+            jjmatchedKind = 72;
             jjmatchedPos = 9;
             return 27;
          }
          if ((active0 & 0x20000000004000L) != 0L)
          {
-            jjmatchedKind = 71;
+            jjmatchedKind = 72;
             jjmatchedPos = 9;
             return 83;
          }
          return -1;
       case 10:
+         if ((active0 & 0x20000000000000L) != 0L)
+            return 83;
+         if ((active0 & 0x40000000000000L) != 0L)
+            return 26;
          if ((active0 & 0x4000L) != 0L)
          {
-            jjmatchedKind = 71;
+            jjmatchedKind = 72;
             jjmatchedPos = 10;
             return 83;
          }
-         if ((active0 & 0x20000000000000L) != 0L)
-            return 83;
-         if ((active0 & 0x40000000000000L) != 0L)
-            return 26;
          return -1;
       case 11:
          if ((active0 & 0x4000L) != 0L)
          {
-            jjmatchedKind = 71;
+            jjmatchedKind = 72;
             jjmatchedPos = 11;
             return 83;
          }
@@ -325,7 +325,7 @@ private final int jjStopStringLiteralDfa_0(int pos, long active0, long active1)
       case 12:
          if ((active0 & 0x4000L) != 0L)
          {
-            jjmatchedKind = 71;
+            jjmatchedKind = 72;
             jjmatchedPos = 12;
             return 83;
          }
@@ -352,19 +352,19 @@ private int jjMoveStringLiteralDfa0_0()
          jjmatchedKind = 4;
          return jjMoveStringLiteralDfa1_0(0x80L, 0x0L);
       case 34:
-         return jjStopAtPos(0, 76);
+         return jjStopAtPos(0, 77);
       case 36:
          return jjStopAtPos(0, 66);
       case 38:
          return jjStopAtPos(0, 22);
       case 39:
-         return jjStopAtPos(0, 75);
+         return jjStopAtPos(0, 76);
       case 40:
          return jjStopAtPos(0, 16);
       case 41:
          return jjStopAtPos(0, 17);
       case 42:
-         return jjStopAtPos(0, 70);
+         return jjStopAtPos(0, 71);
       case 43:
          return jjStopAtPos(0, 25);
       case 44:
@@ -391,7 +391,7 @@ private int jjMoveStringLiteralDfa0_0()
       case 99:
          return jjMoveStringLiteralDfa1_0(0x60090000000000L, 0x0L);
       case 100:
-         return jjMoveStringLiteralDfa1_0(0x7800020000000000L, 0x10L);
+         return jjMoveStringLiteralDfa1_0(0x7800020000000000L, 0x50L);
       case 101:
          return jjMoveStringLiteralDfa1_0(0x0L, 0x20L);
       case 102:
@@ -458,7 +458,7 @@ private int jjMoveStringLiteralDfa1_0(long active0, long active1)
       case 97:
          return jjMoveStringLiteralDfa2_0(active0, 0x7800004000000000L, active1, 0L);
       case 98:
-         return jjMoveStringLiteralDfa2_0(active0, 0x4000000000000L, active1, 0x18L);
+         return jjMoveStringLiteralDfa2_0(active0, 0x4000000000000L, active1, 0x58L);
       case 101:
          return jjMoveStringLiteralDfa2_0(active0, 0x501000000040000L, active1, 0x2L);
       case 105:
@@ -522,7 +522,7 @@ private int jjMoveStringLiteralDfa2_0(long old0, long active0, long old1, long a
             return jjStartNfaWithStates_0(2, 36, 83);
          break;
       case 105:
-         return jjMoveStringLiteralDfa3_0(active0, 0x200000000000L, active1, 0L);
+         return jjMoveStringLiteralDfa3_0(active0, 0x200000000000L, active1, 0x40L);
       case 106:
          return jjMoveStringLiteralDfa3_0(active0, 0L, active1, 0x8L);
       case 107:
@@ -591,6 +591,8 @@ private int jjMoveStringLiteralDfa3_0(long old0, long active0, long old1, long a
          return jjMoveStringLiteralDfa4_0(active0, 0x2000000000000L, active1, 0L);
       case 99:
          return jjMoveStringLiteralDfa4_0(active0, 0x80000000000L, active1, 0L);
+      case 100:
+         return jjMoveStringLiteralDfa4_0(active0, 0L, active1, 0x40L);
       case 101:
          if ((active0 & 0x2000L) != 0L)
          {
@@ -647,6 +649,8 @@ private int jjMoveStringLiteralDfa4_0(long old0, long active0, long old1, long a
       case 58:
          if ((active1 & 0x20L) != 0L)
             return jjStopAtPos(4, 69);
+         else if ((active1 & 0x40L) != 0L)
+            return jjStopAtPos(4, 70);
          break;
       case 73:
          return jjMoveStringLiteralDfa5_0(active0, 0x4000L, active1, 0L);
@@ -956,14 +960,14 @@ private int jjMoveNfa_0(int startState, int curPos)
                case 33:
                   if ((0x3ff000000000000L & l) != 0L)
                   {
-                     if (kind > 72)
-                        kind = 72;
+                     if (kind > 73)
+                        kind = 73;
                      jjCheckNAddStates(0, 2);
                   }
                   else if (curChar == 43)
                   {
-                     if (kind > 72)
-                        kind = 72;
+                     if (kind > 73)
+                        kind = 73;
                   }
                   else if (curChar == 35)
                      jjstateSet[jjnewStateCnt++] = 80;
@@ -971,14 +975,14 @@ private int jjMoveNfa_0(int startState, int curPos)
                      jjstateSet[jjnewStateCnt++] = 73;
                   if ((0x3ff000000000000L & l) != 0L)
                   {
-                     if (kind > 71)
-                        kind = 71;
+                     if (kind > 72)
+                        kind = 72;
                      jjCheckNAddStates(3, 6);
                   }
                   else if (curChar == 43)
                   {
-                     if (kind > 71)
-                        kind = 71;
+                     if (kind > 72)
+                        kind = 72;
                      jjCheckNAdd(72);
                   }
                   else if (curChar == 35)
@@ -987,14 +991,14 @@ private int jjMoveNfa_0(int startState, int curPos)
                case 5:
                   if ((0x3ff000000000000L & l) != 0L)
                   {
-                     if (kind > 72)
-                        kind = 72;
+                     if (kind > 73)
+                        kind = 73;
                      jjCheckNAddStates(0, 2);
                   }
                   else if (curChar == 43)
                   {
-                     if (kind > 72)
-                        kind = 72;
+                     if (kind > 73)
+                        kind = 73;
                   }
                   else if (curChar == 35)
                      jjstateSet[jjnewStateCnt++] = 80;
@@ -1002,14 +1006,14 @@ private int jjMoveNfa_0(int startState, int curPos)
                      jjstateSet[jjnewStateCnt++] = 73;
                   if ((0x3ff000000000000L & l) != 0L)
                   {
-                     if (kind > 71)
-                        kind = 71;
+                     if (kind > 72)
+                        kind = 72;
                      jjCheckNAddStates(3, 6);
                   }
                   else if (curChar == 43)
                   {
-                     if (kind > 71)
-                        kind = 71;
+                     if (kind > 72)
+                        kind = 72;
                      jjCheckNAdd(72);
                   }
                   else if (curChar == 35)
@@ -1018,14 +1022,14 @@ private int jjMoveNfa_0(int startState, int curPos)
                case 28:
                   if ((0x3ff000000000000L & l) != 0L)
                   {
-                     if (kind > 72)
-                        kind = 72;
+                     if (kind > 73)
+                        kind = 73;
                      jjCheckNAddStates(0, 2);
                   }
                   else if (curChar == 43)
                   {
-                     if (kind > 72)
-                        kind = 72;
+                     if (kind > 73)
+                        kind = 73;
                   }
                   else if (curChar == 35)
                      jjstateSet[jjnewStateCnt++] = 80;
@@ -1033,14 +1037,14 @@ private int jjMoveNfa_0(int startState, int curPos)
                      jjstateSet[jjnewStateCnt++] = 73;
                   if ((0x3ff000000000000L & l) != 0L)
                   {
-                     if (kind > 71)
-                        kind = 71;
+                     if (kind > 72)
+                        kind = 72;
                      jjCheckNAddStates(3, 6);
                   }
                   else if (curChar == 43)
                   {
-                     if (kind > 71)
-                        kind = 71;
+                     if (kind > 72)
+                        kind = 72;
                      jjCheckNAdd(72);
                   }
                   else if (curChar == 35)
@@ -1049,14 +1053,14 @@ private int jjMoveNfa_0(int startState, int curPos)
                case 64:
                   if ((0x3ff000000000000L & l) != 0L)
                   {
-                     if (kind > 72)
-                        kind = 72;
+                     if (kind > 73)
+                        kind = 73;
                      jjCheckNAddStates(0, 2);
                   }
                   else if (curChar == 43)
                   {
-                     if (kind > 72)
-                        kind = 72;
+                     if (kind > 73)
+                        kind = 73;
                   }
                   else if (curChar == 35)
                      jjstateSet[jjnewStateCnt++] = 80;
@@ -1064,14 +1068,14 @@ private int jjMoveNfa_0(int startState, int curPos)
                      jjstateSet[jjnewStateCnt++] = 73;
                   if ((0x3ff000000000000L & l) != 0L)
                   {
-                     if (kind > 71)
-                        kind = 71;
+                     if (kind > 72)
+                        kind = 72;
                      jjCheckNAddStates(3, 6);
                   }
                   else if (curChar == 43)
                   {
-                     if (kind > 71)
-                        kind = 71;
+                     if (kind > 72)
+                        kind = 72;
                      jjCheckNAdd(72);
                   }
                   else if (curChar == 35)
@@ -1080,14 +1084,14 @@ private int jjMoveNfa_0(int startState, int curPos)
                case 32:
                   if ((0x3ff000000000000L & l) != 0L)
                   {
-                     if (kind > 72)
-                        kind = 72;
+                     if (kind > 73)
+                        kind = 73;
                      jjCheckNAddStates(0, 2);
                   }
                   else if (curChar == 43)
                   {
-                     if (kind > 72)
-                        kind = 72;
+                     if (kind > 73)
+                        kind = 73;
                   }
                   else if (curChar == 35)
                      jjstateSet[jjnewStateCnt++] = 80;
@@ -1095,14 +1099,14 @@ private int jjMoveNfa_0(int startState, int curPos)
                      jjstateSet[jjnewStateCnt++] = 73;
                   if ((0x3ff000000000000L & l) != 0L)
                   {
-                     if (kind > 71)
-                        kind = 71;
+                     if (kind > 72)
+                        kind = 72;
                      jjCheckNAddStates(3, 6);
                   }
                   else if (curChar == 43)
                   {
-                     if (kind > 71)
-                        kind = 71;
+                     if (kind > 72)
+                        kind = 72;
                      jjCheckNAdd(72);
                   }
                   else if (curChar == 35)
@@ -1111,14 +1115,14 @@ private int jjMoveNfa_0(int startState, int curPos)
                case 36:
                   if ((0x3ff000000000000L & l) != 0L)
                   {
-                     if (kind > 72)
-                        kind = 72;
+                     if (kind > 73)
+                        kind = 73;
                      jjCheckNAddStates(0, 2);
                   }
                   else if (curChar == 43)
                   {
-                     if (kind > 72)
-                        kind = 72;
+                     if (kind > 73)
+                        kind = 73;
                   }
                   else if (curChar == 35)
                      jjstateSet[jjnewStateCnt++] = 80;
@@ -1126,14 +1130,14 @@ private int jjMoveNfa_0(int startState, int curPos)
                      jjstateSet[jjnewStateCnt++] = 73;
                   if ((0x3ff000000000000L & l) != 0L)
                   {
-                     if (kind > 71)
-                        kind = 71;
+                     if (kind > 72)
+                        kind = 72;
                      jjCheckNAddStates(3, 6);
                   }
                   else if (curChar == 43)
                   {
-                     if (kind > 71)
-                        kind = 71;
+                     if (kind > 72)
+                        kind = 72;
                      jjCheckNAdd(72);
                   }
                   else if (curChar == 35)
@@ -1142,14 +1146,14 @@ private int jjMoveNfa_0(int startState, int curPos)
                case 27:
                   if ((0x3ff000000000000L & l) != 0L)
                   {
-                     if (kind > 72)
-                        kind = 72;
+                     if (kind > 73)
+                        kind = 73;
                      jjCheckNAddStates(0, 2);
                   }
                   else if (curChar == 43)
                   {
-                     if (kind > 72)
-                        kind = 72;
+                     if (kind > 73)
+                        kind = 73;
                   }
                   else if (curChar == 35)
                      jjstateSet[jjnewStateCnt++] = 80;
@@ -1157,14 +1161,14 @@ private int jjMoveNfa_0(int startState, int curPos)
                      jjstateSet[jjnewStateCnt++] = 73;
                   if ((0x3ff000000000000L & l) != 0L)
                   {
-                     if (kind > 71)
-                        kind = 71;
+                     if (kind > 72)
+                        kind = 72;
                      jjCheckNAddStates(3, 6);
                   }
                   else if (curChar == 43)
                   {
-                     if (kind > 71)
-                        kind = 71;
+                     if (kind > 72)
+                        kind = 72;
                      jjCheckNAdd(72);
                   }
                   else if (curChar == 35)
@@ -1177,28 +1181,28 @@ private int jjMoveNfa_0(int startState, int curPos)
                      jjCheckNAdd(42);
                   if ((0x3fe000000000000L & l) != 0L)
                   {
-                     if (kind > 83)
-                        kind = 83;
+                     if (kind > 84)
+                        kind = 84;
                      jjCheckNAddTwoStates(39, 40);
                   }
                   else if (curChar == 48)
                   {
-                     if (kind > 83)
-                        kind = 83;
+                     if (kind > 84)
+                        kind = 84;
                      jjCheckNAddStates(13, 15);
                   }
                   break;
                case 31:
                   if ((0x3ff000000000000L & l) != 0L)
                   {
-                     if (kind > 72)
-                        kind = 72;
+                     if (kind > 73)
+                        kind = 73;
                      jjCheckNAddStates(0, 2);
                   }
                   else if (curChar == 43)
                   {
-                     if (kind > 72)
-                        kind = 72;
+                     if (kind > 73)
+                        kind = 73;
                   }
                   else if (curChar == 35)
                      jjstateSet[jjnewStateCnt++] = 80;
@@ -1206,14 +1210,14 @@ private int jjMoveNfa_0(int startState, int curPos)
                      jjstateSet[jjnewStateCnt++] = 73;
                   if ((0x3ff000000000000L & l) != 0L)
                   {
-                     if (kind > 71)
-                        kind = 71;
+                     if (kind > 72)
+                        kind = 72;
                      jjCheckNAddStates(3, 6);
                   }
                   else if (curChar == 43)
                   {
-                     if (kind > 71)
-                        kind = 71;
+                     if (kind > 72)
+                        kind = 72;
                      jjCheckNAdd(72);
                   }
                   else if (curChar == 35)
@@ -1222,14 +1226,14 @@ private int jjMoveNfa_0(int startState, int curPos)
                case 63:
                   if ((0x3ff000000000000L & l) != 0L)
                   {
-                     if (kind > 72)
-                        kind = 72;
+                     if (kind > 73)
+                        kind = 73;
                      jjCheckNAddStates(0, 2);
                   }
                   else if (curChar == 43)
                   {
-                     if (kind > 72)
-                        kind = 72;
+                     if (kind > 73)
+                        kind = 73;
                   }
                   else if (curChar == 35)
                      jjstateSet[jjnewStateCnt++] = 80;
@@ -1237,14 +1241,14 @@ private int jjMoveNfa_0(int startState, int curPos)
                      jjstateSet[jjnewStateCnt++] = 73;
                   if ((0x3ff000000000000L & l) != 0L)
                   {
-                     if (kind > 71)
-                        kind = 71;
+                     if (kind > 72)
+                        kind = 72;
                      jjCheckNAddStates(3, 6);
                   }
                   else if (curChar == 43)
                   {
-                     if (kind > 71)
-                        kind = 71;
+                     if (kind > 72)
+                        kind = 72;
                      jjCheckNAdd(72);
                   }
                   else if (curChar == 35)
@@ -1253,14 +1257,14 @@ private int jjMoveNfa_0(int startState, int curPos)
                case 15:
                   if ((0x3ff000000000000L & l) != 0L)
                   {
-                     if (kind > 72)
-                        kind = 72;
+                     if (kind > 73)
+                        kind = 73;
                      jjCheckNAddStates(0, 2);
                   }
                   else if (curChar == 43)
                   {
-                     if (kind > 72)
-                        kind = 72;
+                     if (kind > 73)
+                        kind = 73;
                   }
                   else if (curChar == 35)
                      jjstateSet[jjnewStateCnt++] = 80;
@@ -1268,14 +1272,14 @@ private int jjMoveNfa_0(int startState, int curPos)
                      jjstateSet[jjnewStateCnt++] = 73;
                   if ((0x3ff000000000000L & l) != 0L)
                   {
-                     if (kind > 71)
-                        kind = 71;
+                     if (kind > 72)
+                        kind = 72;
                      jjCheckNAddStates(3, 6);
                   }
                   else if (curChar == 43)
                   {
-                     if (kind > 71)
-                        kind = 71;
+                     if (kind > 72)
+                        kind = 72;
                      jjCheckNAdd(72);
                   }
                   else if (curChar == 35)
@@ -1284,14 +1288,14 @@ private int jjMoveNfa_0(int startState, int curPos)
                case 35:
                   if ((0x3ff000000000000L & l) != 0L)
                   {
-                     if (kind > 72)
-                        kind = 72;
+                     if (kind > 73)
+                        kind = 73;
                      jjCheckNAddStates(0, 2);
                   }
                   else if (curChar == 43)
                   {
-                     if (kind > 72)
-                        kind = 72;
+                     if (kind > 73)
+                        kind = 73;
                   }
                   else if (curChar == 35)
                      jjstateSet[jjnewStateCnt++] = 80;
@@ -1299,14 +1303,14 @@ private int jjMoveNfa_0(int startState, int curPos)
                      jjstateSet[jjnewStateCnt++] = 73;
                   if ((0x3ff000000000000L & l) != 0L)
                   {
-                     if (kind > 71)
-                        kind = 71;
+                     if (kind > 72)
+                        kind = 72;
                      jjCheckNAddStates(3, 6);
                   }
                   else if (curChar == 43)
                   {
-                     if (kind > 71)
-                        kind = 71;
+                     if (kind > 72)
+                        kind = 72;
                      jjCheckNAdd(72);
                   }
                   else if (curChar == 35)
@@ -1315,14 +1319,14 @@ private int jjMoveNfa_0(int startState, int curPos)
                case 26:
                   if ((0x3ff000000000000L & l) != 0L)
                   {
-                     if (kind > 72)
-                        kind = 72;
+                     if (kind > 73)
+                        kind = 73;
                      jjCheckNAddStates(0, 2);
                   }
                   else if (curChar == 43)
                   {
-                     if (kind > 72)
-                        kind = 72;
+                     if (kind > 73)
+                        kind = 73;
                   }
                   else if (curChar == 35)
                      jjstateSet[jjnewStateCnt++] = 80;
@@ -1330,14 +1334,14 @@ private int jjMoveNfa_0(int startState, int curPos)
                      jjstateSet[jjnewStateCnt++] = 73;
                   if ((0x3ff000000000000L & l) != 0L)
                   {
-                     if (kind > 71)
-                        kind = 71;
+                     if (kind > 72)
+                        kind = 72;
                      jjCheckNAddStates(3, 6);
                   }
                   else if (curChar == 43)
                   {
-                     if (kind > 71)
-                        kind = 71;
+                     if (kind > 72)
+                        kind = 72;
                      jjCheckNAdd(72);
                   }
                   else if (curChar == 35)
@@ -1346,14 +1350,14 @@ private int jjMoveNfa_0(int startState, int curPos)
                case 30:
                   if ((0x3ff000000000000L & l) != 0L)
                   {
-                     if (kind > 72)
-                        kind = 72;
+                     if (kind > 73)
+                        kind = 73;
                      jjCheckNAddStates(0, 2);
                   }
                   else if (curChar == 43)
                   {
-                     if (kind > 72)
-                        kind = 72;
+                     if (kind > 73)
+                        kind = 73;
                   }
                   else if (curChar == 35)
                      jjstateSet[jjnewStateCnt++] = 80;
@@ -1361,14 +1365,14 @@ private int jjMoveNfa_0(int startState, int curPos)
                      jjstateSet[jjnewStateCnt++] = 73;
                   if ((0x3ff000000000000L & l) != 0L)
                   {
-                     if (kind > 71)
-                        kind = 71;
+                     if (kind > 72)
+                        kind = 72;
                      jjCheckNAddStates(3, 6);
                   }
                   else if (curChar == 43)
                   {
-                     if (kind > 71)
-                        kind = 71;
+                     if (kind > 72)
+                        kind = 72;
                      jjCheckNAdd(72);
                   }
                   else if (curChar == 35)
@@ -1377,14 +1381,14 @@ private int jjMoveNfa_0(int startState, int curPos)
                case 83:
                   if ((0x3ff000000000000L & l) != 0L)
                   {
-                     if (kind > 72)
-                        kind = 72;
+                     if (kind > 73)
+                        kind = 73;
                      jjCheckNAddStates(0, 2);
                   }
                   else if (curChar == 43)
                   {
-                     if (kind > 72)
-                        kind = 72;
+                     if (kind > 73)
+                        kind = 73;
                   }
                   else if (curChar == 35)
                      jjstateSet[jjnewStateCnt++] = 80;
@@ -1392,14 +1396,14 @@ private int jjMoveNfa_0(int startState, int curPos)
                      jjstateSet[jjnewStateCnt++] = 73;
                   if ((0x3ff000000000000L & l) != 0L)
                   {
-                     if (kind > 71)
-                        kind = 71;
+                     if (kind > 72)
+                        kind = 72;
                      jjCheckNAddStates(3, 6);
                   }
                   else if (curChar == 43)
                   {
-                     if (kind > 71)
-                        kind = 71;
+                     if (kind > 72)
+                        kind = 72;
                      jjCheckNAdd(72);
                   }
                   else if (curChar == 35)
@@ -1408,14 +1412,14 @@ private int jjMoveNfa_0(int startState, int curPos)
                case 34:
                   if ((0x3ff000000000000L & l) != 0L)
                   {
-                     if (kind > 72)
-                        kind = 72;
+                     if (kind > 73)
+                        kind = 73;
                      jjCheckNAddStates(0, 2);
                   }
                   else if (curChar == 43)
                   {
-                     if (kind > 72)
-                        kind = 72;
+                     if (kind > 73)
+                        kind = 73;
                   }
                   else if (curChar == 35)
                      jjstateSet[jjnewStateCnt++] = 80;
@@ -1423,14 +1427,14 @@ private int jjMoveNfa_0(int startState, int curPos)
                      jjstateSet[jjnewStateCnt++] = 73;
                   if ((0x3ff000000000000L & l) != 0L)
                   {
-                     if (kind > 71)
-                        kind = 71;
+                     if (kind > 72)
+                        kind = 72;
                      jjCheckNAddStates(3, 6);
                   }
                   else if (curChar == 43)
                   {
-                     if (kind > 71)
-                        kind = 71;
+                     if (kind > 72)
+                        kind = 72;
                      jjCheckNAdd(72);
                   }
                   else if (curChar == 35)
@@ -1439,14 +1443,14 @@ private int jjMoveNfa_0(int startState, int curPos)
                case 6:
                   if ((0x3ff000000000000L & l) != 0L)
                   {
-                     if (kind > 72)
-                        kind = 72;
+                     if (kind > 73)
+                        kind = 73;
                      jjCheckNAddStates(0, 2);
                   }
                   else if (curChar == 43)
                   {
-                     if (kind > 72)
-                        kind = 72;
+                     if (kind > 73)
+                        kind = 73;
                   }
                   else if (curChar == 35)
                      jjstateSet[jjnewStateCnt++] = 80;
@@ -1454,14 +1458,14 @@ private int jjMoveNfa_0(int startState, int curPos)
                      jjstateSet[jjnewStateCnt++] = 73;
                   if ((0x3ff000000000000L & l) != 0L)
                   {
-                     if (kind > 71)
-                        kind = 71;
+                     if (kind > 72)
+                        kind = 72;
                      jjCheckNAddStates(3, 6);
                   }
                   else if (curChar == 43)
                   {
-                     if (kind > 71)
-                        kind = 71;
+                     if (kind > 72)
+                        kind = 72;
                      jjCheckNAdd(72);
                   }
                   else if (curChar == 35)
@@ -1470,14 +1474,14 @@ private int jjMoveNfa_0(int startState, int curPos)
                case 29:
                   if ((0x3ff000000000000L & l) != 0L)
                   {
-                     if (kind > 72)
-                        kind = 72;
+                     if (kind > 73)
+                        kind = 73;
                      jjCheckNAddStates(0, 2);
                   }
                   else if (curChar == 43)
                   {
-                     if (kind > 72)
-                        kind = 72;
+                     if (kind > 73)
+                        kind = 73;
                   }
                   else if (curChar == 35)
                      jjstateSet[jjnewStateCnt++] = 80;
@@ -1485,14 +1489,14 @@ private int jjMoveNfa_0(int startState, int curPos)
                      jjstateSet[jjnewStateCnt++] = 73;
                   if ((0x3ff000000000000L & l) != 0L)
                   {
-                     if (kind > 71)
-                        kind = 71;
+                     if (kind > 72)
+                        kind = 72;
                      jjCheckNAddStates(3, 6);
                   }
                   else if (curChar == 43)
                   {
-                     if (kind > 71)
-                        kind = 71;
+                     if (kind > 72)
+                        kind = 72;
                      jjCheckNAdd(72);
                   }
                   else if (curChar == 35)
@@ -1501,15 +1505,15 @@ private int jjMoveNfa_0(int startState, int curPos)
                case 38:
                   if ((0x3fe000000000000L & l) == 0L)
                      break;
-                  if (kind > 83)
-                     kind = 83;
+                  if (kind > 84)
+                     kind = 84;
                   jjCheckNAddTwoStates(39, 40);
                   break;
                case 39:
                   if ((0x3ff000000000000L & l) == 0L)
                      break;
-                  if (kind > 83)
-                     kind = 83;
+                  if (kind > 84)
+                     kind = 84;
                   jjCheckNAddTwoStates(39, 40);
                   break;
                case 41:
@@ -1519,8 +1523,8 @@ private int jjMoveNfa_0(int startState, int curPos)
                case 42:
                   if ((0x3ff000000000000L & l) == 0L)
                      break;
-                  if (kind > 84)
-                     kind = 84;
+                  if (kind > 85)
+                     kind = 85;
                   jjCheckNAddStates(16, 18);
                   break;
                case 44:
@@ -1530,8 +1534,8 @@ private int jjMoveNfa_0(int startState, int curPos)
                case 45:
                   if ((0x3ff000000000000L & l) == 0L)
                      break;
-                  if (kind > 84)
-                     kind = 84;
+                  if (kind > 85)
+                     kind = 85;
                   jjCheckNAddTwoStates(45, 46);
                   break;
                case 47:
@@ -1545,15 +1549,15 @@ private int jjMoveNfa_0(int startState, int curPos)
                case 49:
                   if (curChar != 46)
                      break;
-                  if (kind > 84)
-                     kind = 84;
+                  if (kind > 85)
+                     kind = 85;
                   jjCheckNAddStates(19, 21);
                   break;
                case 50:
                   if ((0x3ff000000000000L & l) == 0L)
                      break;
-                  if (kind > 84)
-                     kind = 84;
+                  if (kind > 85)
+                     kind = 85;
                   jjCheckNAddStates(19, 21);
                   break;
                case 51:
@@ -1567,8 +1571,8 @@ private int jjMoveNfa_0(int startState, int curPos)
                case 54:
                   if ((0x3ff000000000000L & l) == 0L)
                      break;
-                  if (kind > 84)
-                     kind = 84;
+                  if (kind > 85)
+                     kind = 85;
                   jjCheckNAddTwoStates(54, 46);
                   break;
                case 55:
@@ -1578,29 +1582,29 @@ private int jjMoveNfa_0(int startState, int curPos)
                case 56:
                   if (curChar != 48)
                      break;
-                  if (kind > 83)
-                     kind = 83;
+                  if (kind > 84)
+                     kind = 84;
                   jjCheckNAddStates(13, 15);
                   break;
                case 57:
                   if ((0xff000000000000L & l) == 0L)
                      break;
-                  if (kind > 83)
-                     kind = 83;
+                  if (kind > 84)
+                     kind = 84;
                   jjCheckNAddTwoStates(57, 40);
                   break;
                case 59:
                   if ((0x3ff000000000000L & l) == 0L)
                      break;
-                  if (kind > 83)
-                     kind = 83;
+                  if (kind > 84)
+                     kind = 84;
                   jjCheckNAddTwoStates(59, 40);
                   break;
                case 67:
                   if ((0x3ff000000000000L & l) == 0L)
                      break;
-                  if (kind > 71)
-                     kind = 71;
+                  if (kind > 72)
+                     kind = 72;
                   jjCheckNAddStates(3, 6);
                   break;
                case 68:
@@ -1610,15 +1614,15 @@ private int jjMoveNfa_0(int startState, int curPos)
                case 70:
                   if ((0x3ff000000000000L & l) == 0L)
                      break;
-                  if (kind > 71)
-                     kind = 71;
+                  if (kind > 72)
+                     kind = 72;
                   jjCheckNAddStates(22, 24);
                   break;
                case 71:
                   if (curChar != 43)
                      break;
-                  if (kind > 71)
-                     kind = 71;
+                  if (kind > 72)
+                     kind = 72;
                   jjCheckNAdd(72);
                   break;
                case 72:
@@ -1628,8 +1632,8 @@ private int jjMoveNfa_0(int startState, int curPos)
                case 74:
                   if ((0x3ff000000000000L & l) == 0L)
                      break;
-                  if (kind > 71)
-                     kind = 71;
+                  if (kind > 72)
+                     kind = 72;
                   jjCheckNAddStates(25, 28);
                   break;
                case 75:
@@ -1639,15 +1643,15 @@ private int jjMoveNfa_0(int startState, int curPos)
                case 77:
                   if ((0x3ff000000000000L & l) == 0L)
                      break;
-                  if (kind > 71)
-                     kind = 71;
+                  if (kind > 72)
+                     kind = 72;
                   jjCheckNAddStates(29, 31);
                   break;
                case 78:
                   if ((0x3ff000000000000L & l) == 0L)
                      break;
-                  if (kind > 72)
-                     kind = 72;
+                  if (kind > 73)
+                     kind = 73;
                   jjCheckNAddStates(0, 2);
                   break;
                case 79:
@@ -1657,13 +1661,13 @@ private int jjMoveNfa_0(int startState, int curPos)
                case 81:
                   if ((0x3ff000000000000L & l) == 0L)
                      break;
-                  if (kind > 72)
-                     kind = 72;
+                  if (kind > 73)
+                     kind = 73;
                   jjCheckNAddTwoStates(81, 82);
                   break;
                case 82:
-                  if (curChar == 43 && kind > 72)
-                     kind = 72;
+                  if (curChar == 43 && kind > 73)
+                     kind = 73;
                   break;
                default : break;
             }
@@ -1679,14 +1683,14 @@ private int jjMoveNfa_0(int startState, int curPos)
                case 33:
                   if ((0x7fffffe87fffffeL & l) != 0L)
                   {
-                     if (kind > 72)
-                        kind = 72;
+                     if (kind > 73)
+                        kind = 73;
                      jjCheckNAddStates(0, 2);
                   }
                   if ((0x7fffffe87fffffeL & l) != 0L)
                   {
-                     if (kind > 71)
-                        kind = 71;
+                     if (kind > 72)
+                        kind = 72;
                      jjCheckNAddStates(3, 6);
                   }
                   if (curChar == 101)
@@ -1695,14 +1699,14 @@ private int jjMoveNfa_0(int startState, int curPos)
                case 5:
                   if ((0x7fffffe87fffffeL & l) != 0L)
                   {
-                     if (kind > 72)
-                        kind = 72;
+                     if (kind > 73)
+                        kind = 73;
                      jjCheckNAddStates(0, 2);
                   }
                   if ((0x7fffffe87fffffeL & l) != 0L)
                   {
-                     if (kind > 71)
-                        kind = 71;
+                     if (kind > 72)
+                        kind = 72;
                      jjCheckNAddStates(3, 6);
                   }
                   if (curChar == 117)
@@ -1711,14 +1715,14 @@ private int jjMoveNfa_0(int startState, int curPos)
                case 28:
                   if ((0x7fffffe87fffffeL & l) != 0L)
                   {
-                     if (kind > 72)
-                        kind = 72;
+                     if (kind > 73)
+                        kind = 73;
                      jjCheckNAddStates(0, 2);
                   }
                   if ((0x7fffffe87fffffeL & l) != 0L)
                   {
-                     if (kind > 71)
-                        kind = 71;
+                     if (kind > 72)
+                        kind = 72;
                      jjCheckNAddStates(3, 6);
                   }
                   if (curChar == 109)
@@ -1727,14 +1731,14 @@ private int jjMoveNfa_0(int startState, int curPos)
                case 64:
                   if ((0x7fffffe87fffffeL & l) != 0L)
                   {
-                     if (kind > 72)
-                        kind = 72;
+                     if (kind > 73)
+                        kind = 73;
                      jjCheckNAddStates(0, 2);
                   }
                   if ((0x7fffffe87fffffeL & l) != 0L)
                   {
-                     if (kind > 71)
-                        kind = 71;
+                     if (kind > 72)
+                        kind = 72;
                      jjCheckNAddStates(3, 6);
                   }
                   if (curChar == 119)
@@ -1746,14 +1750,14 @@ private int jjMoveNfa_0(int startState, int curPos)
                case 32:
                   if ((0x7fffffe87fffffeL & l) != 0L)
                   {
-                     if (kind > 72)
-                        kind = 72;
+                     if (kind > 73)
+                        kind = 73;
                      jjCheckNAddStates(0, 2);
                   }
                   if ((0x7fffffe87fffffeL & l) != 0L)
                   {
-                     if (kind > 71)
-                        kind = 71;
+                     if (kind > 72)
+                        kind = 72;
                      jjCheckNAddStates(3, 6);
                   }
                   if (curChar == 110)
@@ -1762,14 +1766,14 @@ private int jjMoveNfa_0(int startState, int curPos)
                case 36:
                   if ((0x7fffffe87fffffeL & l) != 0L)
                   {
-                     if (kind > 72)
-                        kind = 72;
+                     if (kind > 73)
+                        kind = 73;
                      jjCheckNAddStates(0, 2);
                   }
                   if ((0x7fffffe87fffffeL & l) != 0L)
                   {
-                     if (kind > 71)
-                        kind = 71;
+                     if (kind > 72)
+                        kind = 72;
                      jjCheckNAddStates(3, 6);
                   }
                   if (curChar == 117)
@@ -1778,14 +1782,14 @@ private int jjMoveNfa_0(int startState, int curPos)
                case 27:
                   if ((0x7fffffe87fffffeL & l) != 0L)
                   {
-                     if (kind > 72)
-                        kind = 72;
+                     if (kind > 73)
+                        kind = 73;
                      jjCheckNAddStates(0, 2);
                   }
                   if ((0x7fffffe87fffffeL & l) != 0L)
                   {
-                     if (kind > 71)
-                        kind = 71;
+                     if (kind > 72)
+                        kind = 72;
                      jjCheckNAddStates(3, 6);
                   }
                   if (curChar == 101)
@@ -1794,8 +1798,8 @@ private int jjMoveNfa_0(int startState, int curPos)
                case 3:
                   if ((0x7fffffe87fffffeL & l) != 0L)
                   {
-                     if (kind > 71)
-                        kind = 71;
+                     if (kind > 72)
+                        kind = 72;
                      jjCheckNAddStates(32, 38);
                   }
                   if (curChar == 110)
@@ -1816,14 +1820,14 @@ private int jjMoveNfa_0(int startState, int curPos)
                case 31:
                   if ((0x7fffffe87fffffeL & l) != 0L)
                   {
-                     if (kind > 72)
-                        kind = 72;
+                     if (kind > 73)
+                        kind = 73;
                      jjCheckNAddStates(0, 2);
                   }
                   if ((0x7fffffe87fffffeL & l) != 0L)
                   {
-                     if (kind > 71)
-                        kind = 71;
+                     if (kind > 72)
+                        kind = 72;
                      jjCheckNAddStates(3, 6);
                   }
                   if (curChar == 116)
@@ -1832,14 +1836,14 @@ private int jjMoveNfa_0(int startState, int curPos)
                case 63:
                   if ((0x7fffffe87fffffeL & l) != 0L)
                   {
-                     if (kind > 72)
-                        kind = 72;
+                     if (kind > 73)
+                        kind = 73;
                      jjCheckNAddStates(0, 2);
                   }
                   if ((0x7fffffe87fffffeL & l) != 0L)
                   {
-                     if (kind > 71)
-                        kind = 71;
+                     if (kind > 72)
+                        kind = 72;
                      jjCheckNAddStates(3, 6);
                   }
                   if (curChar == 111)
@@ -1850,14 +1854,14 @@ private int jjMoveNfa_0(int startState, int curPos)
                case 15:
                   if ((0x7fffffe87fffffeL & l) != 0L)
                   {
-                     if (kind > 72)
-                        kind = 72;
+                     if (kind > 73)
+                        kind = 73;
                      jjCheckNAddStates(0, 2);
                   }
                   if ((0x7fffffe87fffffeL & l) != 0L)
                   {
-                     if (kind > 71)
-                        kind = 71;
+                     if (kind > 72)
+                        kind = 72;
                      jjCheckNAddStates(3, 6);
                   }
                   if (curChar == 97)
@@ -1866,14 +1870,14 @@ private int jjMoveNfa_0(int startState, int curPos)
                case 35:
                   if ((0x7fffffe87fffffeL & l) != 0L)
                   {
-                     if (kind > 72)
-                        kind = 72;
+                     if (kind > 73)
+                        kind = 73;
                      jjCheckNAddStates(0, 2);
                   }
                   if ((0x7fffffe87fffffeL & l) != 0L)
                   {
-                     if (kind > 71)
-                        kind = 71;
+                     if (kind > 72)
+                        kind = 72;
                      jjCheckNAddStates(3, 6);
                   }
                   if (curChar == 114)
@@ -1882,14 +1886,14 @@ private int jjMoveNfa_0(int startState, int curPos)
                case 26:
                   if ((0x7fffffe87fffffeL & l) != 0L)
                   {
-                     if (kind > 72)
-                        kind = 72;
+                     if (kind > 73)
+                        kind = 73;
                      jjCheckNAddStates(0, 2);
                   }
                   if ((0x7fffffe87fffffeL & l) != 0L)
                   {
-                     if (kind > 71)
-                        kind = 71;
+                     if (kind > 72)
+                        kind = 72;
                      jjCheckNAddStates(3, 6);
                   }
                   if (curChar == 115)
@@ -1898,14 +1902,14 @@ private int jjMoveNfa_0(int startState, int curPos)
                case 30:
                   if ((0x7fffffe87fffffeL & l) != 0L)
                   {
-                     if (kind > 72)
-                        kind = 72;
+                     if (kind > 73)
+                        kind = 73;
                      jjCheckNAddStates(0, 2);
                   }
                   if ((0x7fffffe87fffffeL & l) != 0L)
                   {
-                     if (kind > 71)
-                        kind = 71;
+                     if (kind > 72)
+                        kind = 72;
                      jjCheckNAddStates(3, 6);
                   }
                   if (curChar == 84)
@@ -1914,28 +1918,28 @@ private int jjMoveNfa_0(int startState, int curPos)
                case 83:
                   if ((0x7fffffe87fffffeL & l) != 0L)
                   {
-                     if (kind > 72)
-                        kind = 72;
+                     if (kind > 73)
+                        kind = 73;
                      jjCheckNAddStates(0, 2);
                   }
                   if ((0x7fffffe87fffffeL & l) != 0L)
                   {
-                     if (kind > 71)
-                        kind = 71;
+                     if (kind > 72)
+                        kind = 72;
                      jjCheckNAddStates(3, 6);
                   }
                   break;
                case 34:
                   if ((0x7fffffe87fffffeL & l) != 0L)
                   {
-                     if (kind > 72)
-                        kind = 72;
+                     if (kind > 73)
+                        kind = 73;
                      jjCheckNAddStates(0, 2);
                   }
                   if ((0x7fffffe87fffffeL & l) != 0L)
                   {
-                     if (kind > 71)
-                        kind = 71;
+                     if (kind > 72)
+                        kind = 72;
                      jjCheckNAddStates(3, 6);
                   }
                   if (curChar == 114)
@@ -1944,14 +1948,14 @@ private int jjMoveNfa_0(int startState, int curPos)
                case 6:
                   if ((0x7fffffe87fffffeL & l) != 0L)
                   {
-                     if (kind > 72)
-                        kind = 72;
+                     if (kind > 73)
+                        kind = 73;
                      jjCheckNAddStates(0, 2);
                   }
                   if ((0x7fffffe87fffffeL & l) != 0L)
                   {
-                     if (kind > 71)
-                        kind = 71;
+                     if (kind > 72)
+                        kind = 72;
                      jjCheckNAddStates(3, 6);
                   }
                   if (curChar == 114)
@@ -1960,14 +1964,14 @@ private int jjMoveNfa_0(int startState, int curPos)
                case 29:
                   if ((0x7fffffe87fffffeL & l) != 0L)
                   {
-                     if (kind > 72)
-                        kind = 72;
+                     if (kind > 73)
+                        kind = 73;
                      jjCheckNAddStates(0, 2);
                   }
                   if ((0x7fffffe87fffffeL & l) != 0L)
                   {
-                     if (kind > 71)
-                        kind = 71;
+                     if (kind > 72)
+                        kind = 72;
                      jjCheckNAddStates(3, 6);
                   }
                   if (curChar == 105)
@@ -2066,16 +2070,16 @@ private int jjMoveNfa_0(int startState, int curPos)
                      jjstateSet[jjnewStateCnt++] = 36;
                   break;
                case 40:
-                  if ((0x110000001100L & l) != 0L && kind > 83)
-                     kind = 83;
+                  if ((0x110000001100L & l) != 0L && kind > 84)
+                     kind = 84;
                   break;
                case 43:
                   if ((0x2000000020L & l) != 0L)
                      jjAddStates(41, 42);
                   break;
                case 46:
-                  if ((0x5400000054L & l) != 0L && kind > 84)
-                     kind = 84;
+                  if ((0x5400000054L & l) != 0L && kind > 85)
+                     kind = 85;
                   break;
                case 52:
                   if ((0x2000000020L & l) != 0L)
@@ -2088,8 +2092,8 @@ private int jjMoveNfa_0(int startState, int curPos)
                case 59:
                   if ((0x7e0000007eL & l) == 0L)
                      break;
-                  if (kind > 83)
-                     kind = 83;
+                  if (kind > 84)
+                     kind = 84;
                   jjCheckNAddTwoStates(59, 40);
                   break;
                case 60:
@@ -2111,54 +2115,54 @@ private int jjMoveNfa_0(int startState, int curPos)
                case 66:
                   if ((0x7fffffe87fffffeL & l) == 0L)
                      break;
-                  if (kind > 71)
-                     kind = 71;
+                  if (kind > 72)
+                     kind = 72;
                   jjCheckNAddStates(32, 38);
                   break;
                case 67:
                   if ((0x7fffffe87fffffeL & l) == 0L)
                      break;
-                  if (kind > 71)
-                     kind = 71;
+                  if (kind > 72)
+                     kind = 72;
                   jjCheckNAddStates(3, 6);
                   break;
                case 69:
                case 70:
                   if ((0x7fffffe87fffffeL & l) == 0L)
                      break;
-                  if (kind > 71)
-                     kind = 71;
+                  if (kind > 72)
+                     kind = 72;
                   jjCheckNAddStates(22, 24);
                   break;
                case 73:
                case 74:
                   if ((0x7fffffe87fffffeL & l) == 0L)
                      break;
-                  if (kind > 71)
-                     kind = 71;
+                  if (kind > 72)
+                     kind = 72;
                   jjCheckNAddStates(25, 28);
                   break;
                case 76:
                case 77:
                   if ((0x7fffffe87fffffeL & l) == 0L)
                      break;
-                  if (kind > 71)
-                     kind = 71;
+                  if (kind > 72)
+                     kind = 72;
                   jjCheckNAddStates(29, 31);
                   break;
                case 78:
                   if ((0x7fffffe87fffffeL & l) == 0L)
                      break;
-                  if (kind > 72)
-                     kind = 72;
+                  if (kind > 73)
+                     kind = 73;
                   jjCheckNAddStates(0, 2);
                   break;
                case 80:
                case 81:
                   if ((0x7fffffe87fffffeL & l) == 0L)
                      break;
-                  if (kind > 72)
-                     kind = 72;
+                  if (kind > 73)
+                     kind = 73;
                   jjCheckNAddTwoStates(81, 82);
                   break;
                default : break;
@@ -2210,7 +2214,7 @@ private int jjMoveStringLiteralDfa0_1()
    switch(curChar)
    {
       case 39:
-         return jjStopAtPos(0, 79);
+         return jjStopAtPos(0, 80);
       default :
          return jjMoveNfa_1(0, 0);
    }
@@ -2240,12 +2244,12 @@ private int jjMoveNfa_1(int startState, int curPos)
             switch(jjstateSet[--i])
             {
                case 0:
-                  if ((0xffffff7fffffffffL & l) != 0L && kind > 78)
-                     kind = 78;
+                  if ((0xffffff7fffffffffL & l) != 0L && kind > 79)
+                     kind = 79;
                   break;
                case 1:
-                  if ((0x8400000000L & l) != 0L && kind > 77)
-                     kind = 77;
+                  if ((0x8400000000L & l) != 0L && kind > 78)
+                     kind = 78;
                   break;
                case 2:
                   if ((0xf000000000000L & l) != 0L)
@@ -2254,13 +2258,13 @@ private int jjMoveNfa_1(int startState, int curPos)
                case 3:
                   if ((0xff000000000000L & l) == 0L)
                      break;
-                  if (kind > 77)
-                     kind = 77;
+                  if (kind > 78)
+                     kind = 78;
                   jjstateSet[jjnewStateCnt++] = 4;
                   break;
                case 4:
-                  if ((0xff000000000000L & l) != 0L && kind > 77)
-                     kind = 77;
+                  if ((0xff000000000000L & l) != 0L && kind > 78)
+                     kind = 78;
                   break;
                default : break;
             }
@@ -2276,19 +2280,19 @@ private int jjMoveNfa_1(int startState, int curPos)
                case 0:
                   if ((0xffffffffefffffffL & l) != 0L)
                   {
-                     if (kind > 78)
-                        kind = 78;
+                     if (kind > 79)
+                        kind = 79;
                   }
                   else if (curChar == 92)
                      jjAddStates(45, 47);
                   break;
                case 1:
-                  if ((0x14404510000000L & l) != 0L && kind > 77)
-                     kind = 77;
+                  if ((0x14404510000000L & l) != 0L && kind > 78)
+                     kind = 78;
                   break;
                case 5:
-                  if ((0xffffffffefffffffL & l) != 0L && kind > 78)
-                     kind = 78;
+                  if ((0xffffffffefffffffL & l) != 0L && kind > 79)
+                     kind = 79;
                   break;
                default : break;
             }
@@ -2306,8 +2310,8 @@ private int jjMoveNfa_1(int startState, int curPos)
             switch(jjstateSet[--i])
             {
                case 0:
-                  if (jjCanMove_0(hiByte, i1, i2, l1, l2) && kind > 78)
-                     kind = 78;
+                  if (jjCanMove_0(hiByte, i1, i2, l1, l2) && kind > 79)
+                     kind = 79;
                   break;
                default : break;
             }
@@ -2343,7 +2347,7 @@ private int jjMoveStringLiteralDfa0_2()
    switch(curChar)
    {
       case 34:
-         return jjStopAtPos(0, 82);
+         return jjStopAtPos(0, 83);
       default :
          return jjMoveNfa_2(0, 0);
    }
@@ -2367,12 +2371,12 @@ private int jjMoveNfa_2(int startState, int curPos)
             switch(jjstateSet[--i])
             {
                case 0:
-                  if ((0xfffffffbffffffffL & l) != 0L && kind > 81)
-                     kind = 81;
+                  if ((0xfffffffbffffffffL & l) != 0L && kind > 82)
+                     kind = 82;
                   break;
                case 1:
-                  if ((0x8400000000L & l) != 0L && kind > 80)
-                     kind = 80;
+                  if ((0x8400000000L & l) != 0L && kind > 81)
+                     kind = 81;
                   break;
                case 2:
                   if ((0xf000000000000L & l) != 0L)
@@ -2381,13 +2385,13 @@ private int jjMoveNfa_2(int startState, int curPos)
                case 3:
                   if ((0xff000000000000L & l) == 0L)
                      break;
-                  if (kind > 80)
-                     kind = 80;
+                  if (kind > 81)
+                     kind = 81;
                   jjstateSet[jjnewStateCnt++] = 4;
                   break;
                case 4:
-                  if ((0xff000000000000L & l) != 0L && kind > 80)
-                     kind = 80;
+                  if ((0xff000000000000L & l) != 0L && kind > 81)
+                     kind = 81;
                   break;
                default : break;
             }
@@ -2403,19 +2407,19 @@ private int jjMoveNfa_2(int startState, int curPos)
                case 0:
                   if ((0xffffffffefffffffL & l) != 0L)
                   {
-                     if (kind > 81)
-                        kind = 81;
+                     if (kind > 82)
+                        kind = 82;
                   }
                   else if (curChar == 92)
                      jjAddStates(45, 47);
                   break;
                case 1:
-                  if ((0x14404510000000L & l) != 0L && kind > 80)
-                     kind = 80;
+                  if ((0x14404510000000L & l) != 0L && kind > 81)
+                     kind = 81;
                   break;
                case 5:
-                  if ((0xffffffffefffffffL & l) != 0L && kind > 81)
-                     kind = 81;
+                  if ((0xffffffffefffffffL & l) != 0L && kind > 82)
+                     kind = 82;
                   break;
                default : break;
             }
@@ -2433,8 +2437,8 @@ private int jjMoveNfa_2(int startState, int curPos)
             switch(jjstateSet[--i])
             {
                case 0:
-                  if (jjCanMove_0(hiByte, i1, i2, l1, l2) && kind > 81)
-                     kind = 81;
+                  if (jjCanMove_0(hiByte, i1, i2, l1, l2) && kind > 82)
+                     kind = 82;
                   break;
                default : break;
             }
@@ -2485,9 +2489,9 @@ null, null, null, "\141\166\147", "\155\151\156", "\155\141\170", "\163\165\155"
 "\143\165\162\162\145\156\164\124\151\155\145", null, "\171\145\141\162", "\155\157\156\164\150", "\167\145\145\153", 
 "\144\141\171\117\146\131\145\141\162", "\144\141\171", "\144\141\171\117\146\115\157\156\164\150", 
 "\144\141\171\117\146\127\145\145\153", "\150\157\165\162", "\155\151\156\165\164\145", "\163\145\143\157\156\144", 
-"\44", "\157\142\152\72", "\144\142\72", "\145\156\165\155\72", "\52", null, null, 
-null, null, null, null, null, null, null, null, null, null, null, null, null, null, 
-null, null, };
+"\44", "\157\142\152\72", "\144\142\72", "\145\156\165\155\72", 
+"\144\142\151\144\72", "\52", null, null, null, null, null, null, null, null, null, null, null, null, 
+null, null, null, null, null, null, };
 
 /** Lexer state names. */
 public static final String[] lexStateNames = {
@@ -2501,16 +2505,16 @@ public static final int[] jjnewLexState = {
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 
-   1, 2, -1, -1, 0, -1, -1, 0, -1, -1, -1, -1, -1, -1, 
+   -1, 1, 2, -1, -1, 0, -1, -1, 0, -1, -1, -1, -1, -1, -1, 
 };
 static final long[] jjtoToken = {
-   0xfffffffe1fffffffL, 0x1c81ffL, 
+   0xfffffffe1fffffffL, 0x3903ffL, 
 };
 static final long[] jjtoSkip = {
    0x1e0000000L, 0x0L, 
 };
 static final long[] jjtoMore = {
-   0x0L, 0x37800L, 
+   0x0L, 0x6f000L, 
 };
 protected JavaCharStream input_stream;
 private final int[] jjrounds = new int[83];
@@ -2702,32 +2706,32 @@ void MoreLexicalActions()
    jjimageLen += (lengthOfMatch = jjmatchedPos + 1);
    switch(jjmatchedKind)
    {
-      case 75 :
+      case 76 :
          image.append(input_stream.GetSuffix(jjimageLen));
          jjimageLen = 0;
            stringBuffer = new StringBuffer();
          break;
-      case 76 :
+      case 77 :
          image.append(input_stream.GetSuffix(jjimageLen));
          jjimageLen = 0;
             stringBuffer = new StringBuffer();
          break;
-      case 77 :
+      case 78 :
          image.append(input_stream.GetSuffix(jjimageLen));
          jjimageLen = 0;
           stringBuffer.append( escapeChar() );
          break;
-      case 78 :
+      case 79 :
          image.append(input_stream.GetSuffix(jjimageLen));
          jjimageLen = 0;
           stringBuffer.append( image.charAt(image.length()-1) );
          break;
-      case 80 :
+      case 81 :
          image.append(input_stream.GetSuffix(jjimageLen));
          jjimageLen = 0;
           stringBuffer.append( escapeChar() );
          break;
-      case 81 :
+      case 82 :
          image.append(input_stream.GetSuffix(jjimageLen));
          jjimageLen = 0;
           stringBuffer.append( image.charAt(image.length()-1) );
@@ -2740,19 +2744,19 @@ void TokenLexicalActions(Token matchedToken)
 {
    switch(jjmatchedKind)
    {
-      case 79 :
+      case 80 :
         image.append(input_stream.GetSuffix(jjimageLen + (lengthOfMatch = jjmatchedPos + 1)));
           literalValue = stringBuffer.toString();
          break;
-      case 82 :
+      case 83 :
         image.append(input_stream.GetSuffix(jjimageLen + (lengthOfMatch = jjmatchedPos + 1)));
           literalValue = stringBuffer.toString();
          break;
-      case 83 :
+      case 84 :
         image.append(input_stream.GetSuffix(jjimageLen + (lengthOfMatch = jjmatchedPos + 1)));
           literalValue = makeInt();
          break;
-      case 84 :
+      case 85 :
         image.append(input_stream.GetSuffix(jjimageLen + (lengthOfMatch = jjmatchedPos + 1)));
           literalValue = makeFloat();
          break;
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ExpressionParserTreeConstants.java b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ExpressionParserTreeConstants.java
index adbfdb9..b23f38b 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ExpressionParserTreeConstants.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/exp/parser/ExpressionParserTreeConstants.java
@@ -62,6 +62,7 @@ public interface ExpressionParserTreeConstants
   public int JJTOBJPATH = 56;
   public int JJTDBPATH = 57;
   public int JJTENUM = 58;
+  public int JJTDBIDPATH = 59;
 
 
   public String[] jjtNodeName = {
@@ -124,6 +125,7 @@ public interface ExpressionParserTreeConstants
     "ObjPath",
     "DbPath",
     "Enum",
+    "DbIdPath",
   };
 }
-/* JavaCC - OriginalChecksum=a6d7ef3729cf32ee51d4115c98944e5e (do not edit this line) */
+/* JavaCC - OriginalChecksum=d21da7d665d0ef7c43630d13a09f2c1d (do not edit this line) */
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/exp/property/BaseIdProperty.java b/cayenne-server/src/main/java/org/apache/cayenne/exp/property/BaseIdProperty.java
new file mode 100644
index 0000000..44cff17
--- /dev/null
+++ b/cayenne-server/src/main/java/org/apache/cayenne/exp/property/BaseIdProperty.java
@@ -0,0 +1,59 @@
+/*****************************************************************
+ *   Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    https://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ ****************************************************************/
+
+package org.apache.cayenne.exp.property;
+
+import java.util.Objects;
+
+import org.apache.cayenne.exp.ExpressionFactory;
+
+/**
+ * @since 4.2
+ */
+public class BaseIdProperty<E> extends BaseProperty<E> implements IdProperty<E> {
+
+    private final String entityName;
+
+    private final String attributeName;
+
+    /**
+     * Constructs a new property with the given name and expression
+     *
+     * @param attribute  PK attribute name (optional, can be omitted for single PK entity)
+     * @param path       cayenne path (optional, can be omitted for  ID of the root)
+     * @param entityName name of the entity (mandatory)
+     * @param type       of the property (mandatory)
+     * @see PropertyFactory#createBaseId(String, String, String, Class)
+     */
+    protected BaseIdProperty(String attribute, String path, String entityName, Class<? super E> type) {
+        super(null, ExpressionFactory.dbIdPathExp(path == null ? attribute : path + '.' + attribute), type);
+        this.entityName = Objects.requireNonNull(entityName);
+        this.attributeName = Objects.requireNonNull(attribute);
+    }
+
+    @Override
+    public String getEntityName() {
+        return entityName;
+    }
+
+    @Override
+    public String getAttributeName() {
+        return attributeName;
+    }
+}
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/exp/property/IdProperty.java b/cayenne-server/src/main/java/org/apache/cayenne/exp/property/IdProperty.java
new file mode 100644
index 0000000..6f6e528
--- /dev/null
+++ b/cayenne-server/src/main/java/org/apache/cayenne/exp/property/IdProperty.java
@@ -0,0 +1,58 @@
+/*****************************************************************
+ *   Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    https://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ ****************************************************************/
+
+package org.apache.cayenne.exp.property;
+
+import java.util.Map;
+
+import org.apache.cayenne.CayenneRuntimeException;
+import org.apache.cayenne.ObjectId;
+import org.apache.cayenne.exp.Expression;
+import org.apache.cayenne.exp.ExpressionFactory;
+
+/**
+ * @since 4.2
+ */
+public interface IdProperty<E> extends Property<E> {
+
+    default Expression eq(ObjectId value) {
+        if(!getEntityName().equals(value.getEntityName())) {
+            throw new CayenneRuntimeException("Can match IdProperty only with ObjectId for same entity");
+        }
+        Map<String, Object> idSnapshot = value.getIdSnapshot();
+        Object pkValue;
+        if(getAttributeName() == null) {
+            if (idSnapshot.size() > 1) {
+                throw new CayenneRuntimeException("Can't match IdProperty with compound PK");
+            }
+            pkValue = idSnapshot.values().iterator().next();
+        } else {
+            pkValue = idSnapshot.get(getAttributeName());
+            if(pkValue == null && !idSnapshot.containsKey(getAttributeName())) {
+                throw new CayenneRuntimeException("No PK attribute %s for ObjectId %s", getAttributeName(), value);
+            }
+        }
+        return ExpressionFactory.matchExp(getExpression(), pkValue);
+    }
+
+    String getEntityName();
+
+    String getAttributeName();
+
+}
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/exp/property/NumericIdProperty.java b/cayenne-server/src/main/java/org/apache/cayenne/exp/property/NumericIdProperty.java
new file mode 100644
index 0000000..83c98c4
--- /dev/null
+++ b/cayenne-server/src/main/java/org/apache/cayenne/exp/property/NumericIdProperty.java
@@ -0,0 +1,59 @@
+/*****************************************************************
+ *   Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    https://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ ****************************************************************/
+
+package org.apache.cayenne.exp.property;
+
+import java.util.Objects;
+
+import org.apache.cayenne.exp.ExpressionFactory;
+
+/**
+ * @since 4.2
+ */
+public class NumericIdProperty<E extends Number> extends NumericProperty<E> implements IdProperty<E> {
+
+    private final String entityName;
+
+    private final String attributeName;
+
+    /**
+     * Constructs a new property with the given name and expression
+     *
+     * @param attribute  PK attribute name (optional, can be omitted for single PK entity)
+     * @param path       cayenne path (optional, can be omitted for  ID of the root)
+     * @param entityName name of the entity (mandatory)
+     * @param type       of the property (mandatory)
+     * @see PropertyFactory#createNumericId(String, String, String, Class)
+     */
+    protected NumericIdProperty(String attribute, String path, String entityName, Class<E> type) {
+        super(null, ExpressionFactory.dbIdPathExp(path == null ? attribute : path + '.' + attribute), type);
+        this.entityName = Objects.requireNonNull(entityName);
+        this.attributeName = Objects.requireNonNull(attribute);
+    }
+
+    @Override
+    public String getEntityName() {
+        return entityName;
+    }
+
+    @Override
+    public String getAttributeName() {
+        return attributeName;
+    }
+}
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 701ec43..a6da6f3 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
@@ -398,4 +398,20 @@ public class PropertyFactory {
     public static <T extends EmbeddableObject> EmbeddableProperty<T> createEmbeddable(String name, Expression exp, Class<T> embeddableType) {
         return new EmbeddableProperty<>(name, exp, embeddableType);
     }
+
+    public static <T> BaseIdProperty<T> createBaseId(String attribute, String entityName, Class<T> propertyType) {
+        return createBaseId(attribute, null, entityName, propertyType);
+    }
+
+    public static <T> BaseIdProperty<T> createBaseId(String attribute, String path, String entityName, Class<T> propertyType) {
+        return new BaseIdProperty<>(attribute, path, entityName, propertyType);
+    }
+
+    public static <T extends Number> NumericIdProperty<T> createNumericId(String attribute, String entityName, Class<T> propertyType) {
+        return createNumericId(attribute, null, entityName, propertyType);
+    }
+
+    public static <T extends Number> NumericIdProperty<T> createNumericId(String attribute, String path, String entityName, Class<T> propertyType) {
+        return new NumericIdProperty<>(attribute, path, entityName, propertyType);
+    }
 }
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 4ccd893..0d0b0cd 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
@@ -75,4 +75,12 @@ public interface RelationshipProperty<E> extends PathProperty<E> {
         return PrefetchTreeNode.withPath(getName(), PrefetchTreeNode.DISJOINT_BY_ID_PREFETCH_SEMANTICS);
     }
 
+    default <T> BaseIdProperty<T> dot(BaseIdProperty<T> property) {
+        return PropertyFactory.createBaseId(property.getAttributeName(), getName(), property.getEntityName(), property.getType());
+    }
+
+    default <T extends Number> NumericIdProperty<T> dot(NumericIdProperty<T> property) {
+        return PropertyFactory.createNumericId(property.getAttributeName(), getName(), property.getEntityName(), property.getType());
+    }
+
 }
diff --git a/cayenne-server/src/main/jjtree/org/apache/cayenne/exp/parser/ExpressionParser.jjt b/cayenne-server/src/main/jjtree/org/apache/cayenne/exp/parser/ExpressionParser.jjt
index a51598a..38afc39 100644
--- a/cayenne-server/src/main/jjtree/org/apache/cayenne/exp/parser/ExpressionParser.jjt
+++ b/cayenne-server/src/main/jjtree/org/apache/cayenne/exp/parser/ExpressionParser.jjt
@@ -598,6 +598,8 @@ void pathExpression() : {
      "db:"   t = <PROPERTY_PATH> { ExpressionUtils.parsePath(jjtThis, t.image); } #DbPath(0)
    |
      "enum:" t = <PROPERTY_PATH> { jjtThis.setEnumValue(t.image); } #Enum(0)
+   |
+     "dbid:" t = <PROPERTY_PATH> { ExpressionUtils.parsePath(jjtThis, t.image); } #DbIdPath(0)
    )
 }
 
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/exp/ExpressionFactoryTest.java b/cayenne-server/src/test/java/org/apache/cayenne/exp/ExpressionFactoryTest.java
index ffd2e7f..db0c2fd 100644
--- a/cayenne-server/src/test/java/org/apache/cayenne/exp/ExpressionFactoryTest.java
+++ b/cayenne-server/src/test/java/org/apache/cayenne/exp/ExpressionFactoryTest.java
@@ -541,6 +541,46 @@ public class ExpressionFactoryTest {
         ExpressionFactory.exp("name like %32_65415'");
     }
 
+	@Test
+	public void testMatchDbIdExp() {
+		String v = "abc";
+		Expression exp = ExpressionFactory.matchDbIdExp("abc", v);
+		assertEquals(Expression.EQUAL_TO, exp.getType());
+
+		Expression path = (Expression) exp.getOperand(0);
+		assertEquals(Expression.DBID_PATH, path.getType());
+	}
+
+	@Test
+	public void testNoMatchDbIdExp() {
+		String v = "abc";
+		Expression exp = ExpressionFactory.noMatchDbIdExp("abc", v);
+		assertEquals(Expression.NOT_EQUAL_TO, exp.getType());
+
+		Expression path = (Expression) exp.getOperand(0);
+		assertEquals(Expression.DBID_PATH, path.getType());
+	}
+
+	@Test
+	public void testInDbIdExp() {
+		String v = "abc";
+		Expression exp = ExpressionFactory.inDbIdExp("abc", v);
+		assertEquals(Expression.IN, exp.getType());
+
+		Expression path = (Expression) exp.getOperand(0);
+		assertEquals(Expression.DBID_PATH, path.getType());
+	}
+
+	@Test
+	public void testNotInDbIdExp() {
+		String v = "abc";
+		Expression exp = ExpressionFactory.notInDbIdExp("abc", v);
+		assertEquals(Expression.NOT_IN, exp.getType());
+
+		Expression path = (Expression) exp.getOperand(0);
+		assertEquals(Expression.DBID_PATH, path.getType());
+	}
+
 	public static class Bean {
 		public ExpEnum1 a;
 
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/exp/parser/ASTDbIdPathTest.java b/cayenne-server/src/test/java/org/apache/cayenne/exp/parser/ASTDbIdPathTest.java
new file mode 100644
index 0000000..b45d782
--- /dev/null
+++ b/cayenne-server/src/test/java/org/apache/cayenne/exp/parser/ASTDbIdPathTest.java
@@ -0,0 +1,77 @@
+/*****************************************************************
+ *   Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    https://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ ****************************************************************/
+
+package org.apache.cayenne.exp.parser;
+
+import java.io.IOException;
+
+import org.apache.cayenne.exp.Expression;
+import org.apache.cayenne.exp.ExpressionFactory;
+import org.junit.Test;
+
+import static org.hamcrest.CoreMatchers.instanceOf;
+import static org.junit.Assert.*;
+
+/**
+ * @since 4.2
+ */
+public class ASTDbIdPathTest {
+
+    @Test
+    public void testShallowCopy() {
+        ASTDbIdPath path = new ASTDbIdPath("test");
+
+        Expression exp = path.shallowCopy();
+        assertEquals(exp.getType(), Expression.DBID_PATH);
+        assertThat(exp, instanceOf(ASTDbIdPath.class));
+
+        ASTDbIdPath clone = (ASTDbIdPath)exp;
+        assertEquals("test", clone.getPath());
+    }
+
+    @Test
+    public void testAppendAsString() throws IOException {
+        ASTDbIdPath path = new ASTDbIdPath("test");
+        StringBuilder sb = new StringBuilder();
+        path.appendAsString(sb);
+
+        assertEquals("dbid:test", sb.toString());
+    }
+
+    @Test
+    public void testSimpleParse() {
+        Expression exp = ExpressionFactory.exp("dbid:test");
+        assertThat(exp, instanceOf(ASTDbIdPath.class));
+        ASTDbIdPath path = (ASTDbIdPath)exp;
+        assertEquals("test", path.getPath());
+    }
+
+    @Test
+    public void testExpParse() {
+        Expression exp = ExpressionFactory.exp("dbid:test = 1");
+        assertThat(exp, instanceOf(ASTEqual.class));
+        ASTEqual equal = (ASTEqual)exp;
+
+        Node child0 = equal.jjtGetChild(0);
+        assertThat(child0, instanceOf(ASTDbIdPath.class));
+        ASTDbIdPath path = (ASTDbIdPath)child0;
+        assertEquals("test", path.getPath());
+    }
+
+}
\ No newline at end of file
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/exp/property/IdPropertyIT.java b/cayenne-server/src/test/java/org/apache/cayenne/exp/property/IdPropertyIT.java
new file mode 100644
index 0000000..9749c42
--- /dev/null
+++ b/cayenne-server/src/test/java/org/apache/cayenne/exp/property/IdPropertyIT.java
@@ -0,0 +1,244 @@
+/*****************************************************************
+ *   Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    https://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ ****************************************************************/
+
+package org.apache.cayenne.exp.property;
+
+import java.util.List;
+
+import org.apache.cayenne.Cayenne;
+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.map.DbAttribute;
+import org.apache.cayenne.map.ObjEntity;
+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.testmap.Artist;
+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 static org.hamcrest.CoreMatchers.instanceOf;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * @since 4.2
+ */
+@UseServerRuntime(CayenneProjects.TESTMAP_PROJECT)
+public class IdPropertyIT 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();
+        int artistCount = 4;
+        for (int i = 1; i <= artistCount; 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 tPaintings = new TableHelper(dbHelper, "PAINTING");
+        tPaintings.setColumns("PAINTING_ID", "PAINTING_TITLE", "ARTIST_ID", "GALLERY_ID");
+        for (int i = 1; i <= 16; i++) {
+            tPaintings.insert(i, "painting" + i, i % artistCount + 1, 1);
+        }
+    }
+
+    @Test
+    public void filterDb() {
+        List<Artist> artists = ObjectSelect.query(Artist.class)
+                .where(Artist.ARTIST_ID_PK_PROPERTY.gt(2L))
+                .select(context);
+        assertEquals(2, artists.size());
+    }
+
+    @Test
+    public void filterDbRelated() {
+        List<Artist> artists = ObjectSelect.query(Artist.class)
+                .where(Artist.PAINTING_ARRAY.dot(Painting.PAINTING_ID_PK_PROPERTY).gt(13))
+                .select(context);
+        assertEquals(3, artists.size());
+    }
+
+    @Test
+    public void filterInMemory() {
+        List<Artist> artists = Artist.ARTIST_ID_PK_PROPERTY.gt(2L)
+                .filterObjects(ObjectSelect.query(Artist.class).select(context));
+        assertEquals(2, artists.size());
+    }
+
+    @Test
+    public void filterInMemoryRelated() {
+        Expression exp = Artist.PAINTING_ARRAY.dot(Painting.PAINTING_ID_PK_PROPERTY).gt(13);
+        List<Artist> artists = exp
+                .filterObjects(ObjectSelect.query(Artist.class).select(context));
+        assertEquals(3, artists.size());
+    }
+
+    @Test
+    public void orderingDb() {
+        List<Painting> paintings = ObjectSelect.query(Painting.class)
+                .orderBy(Painting.PAINTING_ID_PK_PROPERTY.desc())
+                .select(context);
+
+        assertEquals(16, paintings.size());
+        assertEquals(16L, Cayenne.longPKForObject(paintings.get(0)));
+        assertEquals(1L, Cayenne.longPKForObject(paintings.get(15)));
+    }
+
+    @Test
+    public void orderingDbRelated() {
+        List<Painting> paintings = ObjectSelect.query(Painting.class)
+                .orderBy(Painting.TO_ARTIST.dot(Artist.ARTIST_ID_PK_PROPERTY).asc())
+                .select(context);
+
+        assertEquals(16, paintings.size());
+        assertEquals(1L, Cayenne.longPKForObject(paintings.get(0).getToArtist()));
+        assertEquals(4L, Cayenne.longPKForObject(paintings.get(15).getToArtist()));
+    }
+
+    @Test
+    public void orderingInMemory() {
+        List<Painting> paintings = ObjectSelect.query(Painting.class)
+                .select(context);
+        Painting.PAINTING_ID_PK_PROPERTY.desc().orderList(paintings);
+
+        assertEquals(16, paintings.size());
+        assertEquals(16L, Cayenne.longPKForObject(paintings.get(0)));
+        assertEquals(1L, Cayenne.longPKForObject(paintings.get(15)));
+    }
+
+    @Test
+    public void orderingInMemoryRelated() {
+        List<Painting> paintings = ObjectSelect.query(Painting.class)
+                .select(context);
+        Painting.TO_ARTIST.dot(Artist.ARTIST_ID_PK_PROPERTY).asc().orderList(paintings);
+
+        assertEquals(16, paintings.size());
+        assertEquals(1L, Cayenne.longPKForObject(paintings.get(0).getToArtist()));
+        assertEquals(4L, Cayenne.longPKForObject(paintings.get(15).getToArtist()));
+    }
+
+    @Test
+    public void columnQuery() {
+        List<Long> ids = ObjectSelect.columnQuery(Artist.class, Artist.ARTIST_ID_PK_PROPERTY)
+                .orderBy(Artist.ARTIST_ID_PK_PROPERTY.desc())
+                .select(context);
+        assertEquals(4, ids.size());
+        assertEquals(Long.valueOf(1L), ids.get(3));
+        assertEquals(Long.valueOf(2L), ids.get(2));
+        assertEquals(Long.valueOf(3L), ids.get(1));
+        assertEquals(Long.valueOf(4L), ids.get(0));
+    }
+
+    @Test
+    public void columnQueryWithGroupBy() {
+        List<Object[]> ids = ObjectSelect.columnQuery(Artist.class, Artist.ARTIST_ID_PK_PROPERTY, Artist.PAINTING_ARRAY.count())
+                .orderBy(Artist.ARTIST_ID_PK_PROPERTY.asc())
+                .select(context);
+        assertEquals(4, ids.size());
+        assertEquals(1L, ids.get(0)[0]);
+        assertEquals(2L, ids.get(1)[0]);
+        assertEquals(3L, ids.get(2)[0]);
+        assertEquals(4L, ids.get(3)[0]);
+    }
+
+    @Test
+    public void filterDirectAttributeExpression() {
+        List<Artist> artists = ObjectSelect.query(Artist.class)
+                .where(ExpressionFactory.matchDbIdExp("ARTIST_ID", 2))
+                .select(context);
+        assertEquals(1, artists.size());
+    }
+
+    @Test
+    public void filterStringExpression() {
+        List<Painting> paintings = ObjectSelect.query(Painting.class)
+                .where(ExpressionFactory.exp("dbid:toArtist.ARTIST_ID in (1)"))
+                .select(context);
+        assertEquals(4, paintings.size());
+    }
+
+    @Test
+    public void filterByObjectId() {
+        Artist artist = ObjectSelect.query(Artist.class).selectFirst(context);
+        assertNotNull(artist);
+
+        List<Painting> paintings = ObjectSelect.query(Painting.class)
+                .where(Painting.TO_ARTIST.dot(Artist.ARTIST_ID_PK_PROPERTY).eq(artist.getObjectId()))
+                .select(context);
+        assertEquals(4, paintings.size());
+    }
+
+    @Test
+    public void testEvaluateObject() {
+        Expression exp = Painting.TO_ARTIST.dot(Artist.ARTIST_ID_PK_PROPERTY).getExpression();
+        Painting painting = ObjectSelect.query(Painting.class).selectFirst(context);
+        Object result = exp.evaluate(painting);
+        assertNotNull(result);
+        assertThat(result, instanceOf(Long.class));
+        assertEquals(painting.getToArtist().getObjectId().getIdSnapshot().get("ARTIST_ID"), result);
+    }
+
+    @Test
+    public void testEvaluateCollection() {
+        Expression exp = Painting.TO_ARTIST.dot(Artist.ARTIST_ID_PK_PROPERTY).getExpression();
+        List<Painting> paintings = ObjectSelect.query(Painting.class).select(context);
+        Object result = exp.evaluate(paintings);
+        assertNotNull(result);
+        assertThat(result, instanceOf(List.class));
+        @SuppressWarnings("unchecked")
+        List<Long> ids = (List<Long>)result;
+        assertEquals(paintings.size(), ids.size());
+        for(int i=0; i<paintings.size(); i++) {
+            assertEquals(paintings.get(i).getToArtist().getObjectId().getIdSnapshot().get("ARTIST_ID"), ids.get(i));
+        }
+    }
+
+    @Test
+    public void testEvaluateEntity() {
+        Expression exp = Painting.TO_ARTIST.dot(Artist.ARTIST_ID_PK_PROPERTY).getExpression();
+        ObjEntity painting = context.getEntityResolver().getObjEntity(Painting.class);
+        Object result = exp.evaluate(painting);
+        assertNotNull(result);
+        assertThat(result, instanceOf(DbAttribute.class));
+        DbAttribute pk = (DbAttribute)result;
+        assertEquals("ARTIST_ID", pk.getName());
+        assertTrue(pk.isPrimaryKey());
+    }
+}
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/exp/property/IdPropertyTest.java b/cayenne-server/src/test/java/org/apache/cayenne/exp/property/IdPropertyTest.java
new file mode 100644
index 0000000..161bf18
--- /dev/null
+++ b/cayenne-server/src/test/java/org/apache/cayenne/exp/property/IdPropertyTest.java
@@ -0,0 +1,81 @@
+/*****************************************************************
+ *   Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    https://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ ****************************************************************/
+
+package org.apache.cayenne.exp.property;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.cayenne.CayenneRuntimeException;
+import org.apache.cayenne.ObjectId;
+import org.junit.Test;
+
+import static org.apache.cayenne.exp.ExpressionFactory.*;
+import static org.apache.cayenne.exp.property.PropertyFactory.createNumericId;
+import static org.junit.Assert.*;
+
+/**
+ * @since 4.2
+ */
+public class IdPropertyTest {
+
+    @Test
+    public void expressionContentAttribute() {
+        assertEquals(dbIdPathExp("ARTIST_ID"),
+                createNumericId("ARTIST_ID", "Artist", Long.class).getExpression());
+    }
+
+    @Test
+    public void expressionContentPathAttribute() {
+        assertEquals(dbIdPathExp("path.ARTIST_ID"),
+                createNumericId("ARTIST_ID", "path", "Artist", Long.class).getExpression());
+    }
+
+    @Test
+    public void eqObjectIdAttribute() {
+        ObjectId id = ObjectId.of("Artist", "ARTIST_ID", 2);
+        assertEquals(matchDbIdExp("ARTIST_ID", 2),
+                createNumericId("ARTIST_ID", "Artist", Integer.class).eq(id));
+    }
+
+    @Test(expected = CayenneRuntimeException.class)
+    public void eqObjectIdWrongAttribute() {
+        ObjectId id = ObjectId.of("Artist", "ARTIST_ID", 2);
+        createNumericId("ARTIST_PK", "Artist", Integer.class).eq(id);
+    }
+
+    @Test
+    public void eqObjectIdCompound() {
+        Map<String, Object> key = new HashMap<>();
+        key.put("ARTIST_ID", 2);
+        key.put("SERIAL", 1);
+        ObjectId id = ObjectId.of("Artist", key);
+        assertEquals(matchDbIdExp("ARTIST_ID", 2),
+                createNumericId("ARTIST_ID", "Artist", Integer.class).eq(id));
+    }
+
+    @Test(expected = CayenneRuntimeException.class)
+    public void eqObjectIdWrongCompound() {
+        Map<String, Object> key = new HashMap<>();
+        key.put("ARTIST_ID", 2);
+        key.put("SERIAL", 1);
+        ObjectId id = ObjectId.of("Artist", key);
+        createNumericId("Artist", "ARTIST_ID", Integer.class).eq(id);
+    }
+}
\ No newline at end of file
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/testdo/testmap/auto/_ArtGroup.java b/cayenne-server/src/test/java/org/apache/cayenne/testdo/testmap/auto/_ArtGroup.java
index 8a19ed8..b112745 100644
--- a/cayenne-server/src/test/java/org/apache/cayenne/testdo/testmap/auto/_ArtGroup.java
+++ b/cayenne-server/src/test/java/org/apache/cayenne/testdo/testmap/auto/_ArtGroup.java
@@ -6,10 +6,9 @@ import java.io.ObjectOutputStream;
 import java.util.List;
 
 import org.apache.cayenne.BaseDataObject;
-import org.apache.cayenne.exp.ExpressionFactory;
 import org.apache.cayenne.exp.property.EntityProperty;
 import org.apache.cayenne.exp.property.ListProperty;
-import org.apache.cayenne.exp.property.NumericProperty;
+import org.apache.cayenne.exp.property.NumericIdProperty;
 import org.apache.cayenne.exp.property.PropertyFactory;
 import org.apache.cayenne.exp.property.StringProperty;
 import org.apache.cayenne.testdo.testmap.ArtGroup;
@@ -25,7 +24,7 @@ public abstract class _ArtGroup extends BaseDataObject {
 
     private static final long serialVersionUID = 1L; 
 
-    public static final NumericProperty<Integer> GROUP_ID_PK_PROPERTY = PropertyFactory.createNumeric(ExpressionFactory.dbPathExp("GROUP_ID"), Integer.class);
+    public static final NumericIdProperty<Integer> GROUP_ID_PK_PROPERTY = PropertyFactory.createNumericId("GROUP_ID", "ArtGroup", Integer.class);
     public static final String GROUP_ID_PK_COLUMN = "GROUP_ID";
 
     public static final StringProperty<String> NAME = PropertyFactory.createString("name", String.class);
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/testdo/testmap/auto/_Artist.java b/cayenne-server/src/test/java/org/apache/cayenne/testdo/testmap/auto/_Artist.java
index 7324407..fa1545d 100644
--- a/cayenne-server/src/test/java/org/apache/cayenne/testdo/testmap/auto/_Artist.java
+++ b/cayenne-server/src/test/java/org/apache/cayenne/testdo/testmap/auto/_Artist.java
@@ -7,10 +7,9 @@ import java.util.Date;
 import java.util.List;
 
 import org.apache.cayenne.BaseDataObject;
-import org.apache.cayenne.exp.ExpressionFactory;
 import org.apache.cayenne.exp.property.DateProperty;
 import org.apache.cayenne.exp.property.ListProperty;
-import org.apache.cayenne.exp.property.NumericProperty;
+import org.apache.cayenne.exp.property.NumericIdProperty;
 import org.apache.cayenne.exp.property.PropertyFactory;
 import org.apache.cayenne.exp.property.StringProperty;
 import org.apache.cayenne.testdo.testmap.ArtGroup;
@@ -27,7 +26,7 @@ public abstract class _Artist extends BaseDataObject {
 
     private static final long serialVersionUID = 1L; 
 
-    public static final NumericProperty<Long> ARTIST_ID_PK_PROPERTY = PropertyFactory.createNumeric(ExpressionFactory.dbPathExp("ARTIST_ID"), Long.class);
+    public static final NumericIdProperty<Long> ARTIST_ID_PK_PROPERTY = PropertyFactory.createNumericId("ARTIST_ID", "Artist", Long.class);
     public static final String ARTIST_ID_PK_COLUMN = "ARTIST_ID";
 
     public static final StringProperty<String> ARTIST_NAME = PropertyFactory.createString("artistName", String.class);
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/testdo/testmap/auto/_ArtistCallback.java b/cayenne-server/src/test/java/org/apache/cayenne/testdo/testmap/auto/_ArtistCallback.java
index 87e4238..ad2f1f6 100644
--- a/cayenne-server/src/test/java/org/apache/cayenne/testdo/testmap/auto/_ArtistCallback.java
+++ b/cayenne-server/src/test/java/org/apache/cayenne/testdo/testmap/auto/_ArtistCallback.java
@@ -6,9 +6,8 @@ import java.io.ObjectOutputStream;
 import java.util.Date;
 
 import org.apache.cayenne.BaseDataObject;
-import org.apache.cayenne.exp.ExpressionFactory;
 import org.apache.cayenne.exp.property.DateProperty;
-import org.apache.cayenne.exp.property.NumericProperty;
+import org.apache.cayenne.exp.property.NumericIdProperty;
 import org.apache.cayenne.exp.property.PropertyFactory;
 import org.apache.cayenne.exp.property.StringProperty;
 
@@ -22,7 +21,7 @@ public abstract class _ArtistCallback extends BaseDataObject {
 
     private static final long serialVersionUID = 1L; 
 
-    public static final NumericProperty<Integer> ARTIST_ID_PK_PROPERTY = PropertyFactory.createNumeric(ExpressionFactory.dbPathExp("ARTIST_ID"), Integer.class);
+    public static final NumericIdProperty<Integer> ARTIST_ID_PK_PROPERTY = PropertyFactory.createNumericId("ARTIST_ID", "ArtistCallback", Integer.class);
     public static final String ARTIST_ID_PK_COLUMN = "ARTIST_ID";
 
     public static final StringProperty<String> ARTIST_NAME = PropertyFactory.createString("artistName", String.class);
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/testdo/testmap/auto/_ArtistExhibit.java b/cayenne-server/src/test/java/org/apache/cayenne/testdo/testmap/auto/_ArtistExhibit.java
index eed7c9e..6b7cd0b 100644
--- a/cayenne-server/src/test/java/org/apache/cayenne/testdo/testmap/auto/_ArtistExhibit.java
+++ b/cayenne-server/src/test/java/org/apache/cayenne/testdo/testmap/auto/_ArtistExhibit.java
@@ -5,9 +5,8 @@ import java.io.ObjectInputStream;
 import java.io.ObjectOutputStream;
 
 import org.apache.cayenne.BaseDataObject;
-import org.apache.cayenne.exp.ExpressionFactory;
 import org.apache.cayenne.exp.property.EntityProperty;
-import org.apache.cayenne.exp.property.NumericProperty;
+import org.apache.cayenne.exp.property.NumericIdProperty;
 import org.apache.cayenne.exp.property.PropertyFactory;
 import org.apache.cayenne.testdo.testmap.Artist;
 import org.apache.cayenne.testdo.testmap.Exhibit;
@@ -22,9 +21,9 @@ public abstract class _ArtistExhibit extends BaseDataObject {
 
     private static final long serialVersionUID = 1L; 
 
-    public static final NumericProperty<Long> ARTIST_ID_PK_PROPERTY = PropertyFactory.createNumeric(ExpressionFactory.dbPathExp("ARTIST_ID"), Long.class);
+    public static final NumericIdProperty<Long> ARTIST_ID_PK_PROPERTY = PropertyFactory.createNumericId("ARTIST_ID", "ArtistExhibit", Long.class);
     public static final String ARTIST_ID_PK_COLUMN = "ARTIST_ID";
-    public static final NumericProperty<Integer> EXHIBIT_ID_PK_PROPERTY = PropertyFactory.createNumeric(ExpressionFactory.dbPathExp("EXHIBIT_ID"), Integer.class);
+    public static final NumericIdProperty<Integer> EXHIBIT_ID_PK_PROPERTY = PropertyFactory.createNumericId("EXHIBIT_ID", "ArtistExhibit", Integer.class);
     public static final String EXHIBIT_ID_PK_COLUMN = "EXHIBIT_ID";
 
     public static final EntityProperty<Artist> TO_ARTIST = PropertyFactory.createEntity("toArtist", Artist.class);
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/testdo/testmap/auto/_CompoundPainting.java b/cayenne-server/src/test/java/org/apache/cayenne/testdo/testmap/auto/_CompoundPainting.java
index 1d33435..56c8096 100644
--- a/cayenne-server/src/test/java/org/apache/cayenne/testdo/testmap/auto/_CompoundPainting.java
+++ b/cayenne-server/src/test/java/org/apache/cayenne/testdo/testmap/auto/_CompoundPainting.java
@@ -6,8 +6,8 @@ import java.io.ObjectOutputStream;
 import java.math.BigDecimal;
 
 import org.apache.cayenne.BaseDataObject;
-import org.apache.cayenne.exp.ExpressionFactory;
 import org.apache.cayenne.exp.property.EntityProperty;
+import org.apache.cayenne.exp.property.NumericIdProperty;
 import org.apache.cayenne.exp.property.NumericProperty;
 import org.apache.cayenne.exp.property.PropertyFactory;
 import org.apache.cayenne.exp.property.StringProperty;
@@ -25,7 +25,7 @@ public abstract class _CompoundPainting extends BaseDataObject {
 
     private static final long serialVersionUID = 1L; 
 
-    public static final NumericProperty<Integer> PAINTING_ID_PK_PROPERTY = PropertyFactory.createNumeric(ExpressionFactory.dbPathExp("PAINTING_ID"), Integer.class);
+    public static final NumericIdProperty<Integer> PAINTING_ID_PK_PROPERTY = PropertyFactory.createNumericId("PAINTING_ID", "CompoundPainting", Integer.class);
     public static final String PAINTING_ID_PK_COLUMN = "PAINTING_ID";
 
     public static final StringProperty<String> ARTIST_NAME = PropertyFactory.createString("artistName", String.class);
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/testdo/testmap/auto/_CompoundPaintingLongNames.java b/cayenne-server/src/test/java/org/apache/cayenne/testdo/testmap/auto/_CompoundPaintingLongNames.java
index e785fa8..6d4297a 100644
--- a/cayenne-server/src/test/java/org/apache/cayenne/testdo/testmap/auto/_CompoundPaintingLongNames.java
+++ b/cayenne-server/src/test/java/org/apache/cayenne/testdo/testmap/auto/_CompoundPaintingLongNames.java
@@ -6,8 +6,8 @@ import java.io.ObjectOutputStream;
 import java.math.BigDecimal;
 
 import org.apache.cayenne.BaseDataObject;
-import org.apache.cayenne.exp.ExpressionFactory;
 import org.apache.cayenne.exp.property.EntityProperty;
+import org.apache.cayenne.exp.property.NumericIdProperty;
 import org.apache.cayenne.exp.property.NumericProperty;
 import org.apache.cayenne.exp.property.PropertyFactory;
 import org.apache.cayenne.exp.property.StringProperty;
@@ -26,7 +26,7 @@ public abstract class _CompoundPaintingLongNames extends BaseDataObject {
 
     private static final long serialVersionUID = 1L; 
 
-    public static final NumericProperty<Integer> PAINTING_ID_PK_PROPERTY = PropertyFactory.createNumeric(ExpressionFactory.dbPathExp("PAINTING_ID"), Integer.class);
+    public static final NumericIdProperty<Integer> PAINTING_ID_PK_PROPERTY = PropertyFactory.createNumericId("PAINTING_ID", "CompoundPaintingLongNames", Integer.class);
     public static final String PAINTING_ID_PK_COLUMN = "PAINTING_ID";
 
     public static final StringProperty<String> ARTIST_LONG_NAME = PropertyFactory.createString("artistLongName", String.class);
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/testdo/testmap/auto/_Exhibit.java b/cayenne-server/src/test/java/org/apache/cayenne/testdo/testmap/auto/_Exhibit.java
index b05c657..2895455 100644
--- a/cayenne-server/src/test/java/org/apache/cayenne/testdo/testmap/auto/_Exhibit.java
+++ b/cayenne-server/src/test/java/org/apache/cayenne/testdo/testmap/auto/_Exhibit.java
@@ -7,11 +7,10 @@ import java.util.Date;
 import java.util.List;
 
 import org.apache.cayenne.BaseDataObject;
-import org.apache.cayenne.exp.ExpressionFactory;
 import org.apache.cayenne.exp.property.DateProperty;
 import org.apache.cayenne.exp.property.EntityProperty;
 import org.apache.cayenne.exp.property.ListProperty;
-import org.apache.cayenne.exp.property.NumericProperty;
+import org.apache.cayenne.exp.property.NumericIdProperty;
 import org.apache.cayenne.exp.property.PropertyFactory;
 import org.apache.cayenne.testdo.testmap.ArtistExhibit;
 import org.apache.cayenne.testdo.testmap.Gallery;
@@ -26,7 +25,7 @@ public abstract class _Exhibit extends BaseDataObject {
 
     private static final long serialVersionUID = 1L; 
 
-    public static final NumericProperty<Integer> EXHIBIT_ID_PK_PROPERTY = PropertyFactory.createNumeric(ExpressionFactory.dbPathExp("EXHIBIT_ID"), Integer.class);
+    public static final NumericIdProperty<Integer> EXHIBIT_ID_PK_PROPERTY = PropertyFactory.createNumericId("EXHIBIT_ID", "Exhibit", Integer.class);
     public static final String EXHIBIT_ID_PK_COLUMN = "EXHIBIT_ID";
 
     public static final DateProperty<Date> CLOSING_DATE = PropertyFactory.createDate("closingDate", Date.class);
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/testdo/testmap/auto/_Gallery.java b/cayenne-server/src/test/java/org/apache/cayenne/testdo/testmap/auto/_Gallery.java
index b1af774..85c01e0 100644
--- a/cayenne-server/src/test/java/org/apache/cayenne/testdo/testmap/auto/_Gallery.java
+++ b/cayenne-server/src/test/java/org/apache/cayenne/testdo/testmap/auto/_Gallery.java
@@ -6,9 +6,8 @@ import java.io.ObjectOutputStream;
 import java.util.List;
 
 import org.apache.cayenne.BaseDataObject;
-import org.apache.cayenne.exp.ExpressionFactory;
 import org.apache.cayenne.exp.property.ListProperty;
-import org.apache.cayenne.exp.property.NumericProperty;
+import org.apache.cayenne.exp.property.NumericIdProperty;
 import org.apache.cayenne.exp.property.PropertyFactory;
 import org.apache.cayenne.exp.property.StringProperty;
 import org.apache.cayenne.testdo.testmap.Exhibit;
@@ -24,7 +23,7 @@ public abstract class _Gallery extends BaseDataObject {
 
     private static final long serialVersionUID = 1L; 
 
-    public static final NumericProperty<Integer> GALLERY_ID_PK_PROPERTY = PropertyFactory.createNumeric(ExpressionFactory.dbPathExp("GALLERY_ID"), Integer.class);
+    public static final NumericIdProperty<Integer> GALLERY_ID_PK_PROPERTY = PropertyFactory.createNumericId("GALLERY_ID", "Gallery", Integer.class);
     public static final String GALLERY_ID_PK_COLUMN = "GALLERY_ID";
 
     public static final StringProperty<String> GALLERY_NAME = PropertyFactory.createString("galleryName", String.class);
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/testdo/testmap/auto/_MeaningfulGeneratedColumnTestEntity.java b/cayenne-server/src/test/java/org/apache/cayenne/testdo/testmap/auto/_MeaningfulGeneratedColumnTestEntity.java
index 8b62796..f71a023 100644
--- a/cayenne-server/src/test/java/org/apache/cayenne/testdo/testmap/auto/_MeaningfulGeneratedColumnTestEntity.java
+++ b/cayenne-server/src/test/java/org/apache/cayenne/testdo/testmap/auto/_MeaningfulGeneratedColumnTestEntity.java
@@ -5,7 +5,6 @@ import java.io.ObjectInputStream;
 import java.io.ObjectOutputStream;
 
 import org.apache.cayenne.BaseDataObject;
-import org.apache.cayenne.exp.ExpressionFactory;
 import org.apache.cayenne.exp.property.NumericProperty;
 import org.apache.cayenne.exp.property.PropertyFactory;
 import org.apache.cayenne.exp.property.StringProperty;
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/testdo/testmap/auto/_NullTestEntity.java b/cayenne-server/src/test/java/org/apache/cayenne/testdo/testmap/auto/_NullTestEntity.java
index cf6ac90..396758c 100644
--- a/cayenne-server/src/test/java/org/apache/cayenne/testdo/testmap/auto/_NullTestEntity.java
+++ b/cayenne-server/src/test/java/org/apache/cayenne/testdo/testmap/auto/_NullTestEntity.java
@@ -5,8 +5,7 @@ import java.io.ObjectInputStream;
 import java.io.ObjectOutputStream;
 
 import org.apache.cayenne.BaseDataObject;
-import org.apache.cayenne.exp.ExpressionFactory;
-import org.apache.cayenne.exp.property.NumericProperty;
+import org.apache.cayenne.exp.property.NumericIdProperty;
 import org.apache.cayenne.exp.property.PropertyFactory;
 import org.apache.cayenne.exp.property.StringProperty;
 
@@ -20,7 +19,7 @@ public abstract class _NullTestEntity extends BaseDataObject {
 
     private static final long serialVersionUID = 1L; 
 
-    public static final NumericProperty<Integer> ID_PK_PROPERTY = PropertyFactory.createNumeric(ExpressionFactory.dbPathExp("ID"), Integer.class);
+    public static final NumericIdProperty<Integer> ID_PK_PROPERTY = PropertyFactory.createNumericId("ID", "NullTestEntity", Integer.class);
     public static final String ID_PK_COLUMN = "ID";
 
     public static final StringProperty<String> NAME = PropertyFactory.createString("name", String.class);
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/testdo/testmap/auto/_Painting.java b/cayenne-server/src/test/java/org/apache/cayenne/testdo/testmap/auto/_Painting.java
index 7930027..3d42f0c 100644
--- a/cayenne-server/src/test/java/org/apache/cayenne/testdo/testmap/auto/_Painting.java
+++ b/cayenne-server/src/test/java/org/apache/cayenne/testdo/testmap/auto/_Painting.java
@@ -5,8 +5,8 @@ import java.io.ObjectInputStream;
 import java.io.ObjectOutputStream;
 import java.math.BigDecimal;
 
-import org.apache.cayenne.exp.ExpressionFactory;
 import org.apache.cayenne.exp.property.EntityProperty;
+import org.apache.cayenne.exp.property.NumericIdProperty;
 import org.apache.cayenne.exp.property.NumericProperty;
 import org.apache.cayenne.exp.property.PropertyFactory;
 import org.apache.cayenne.exp.property.StringProperty;
@@ -25,7 +25,7 @@ public abstract class _Painting extends ArtDataObject {
 
     private static final long serialVersionUID = 1L; 
 
-    public static final NumericProperty<Integer> PAINTING_ID_PK_PROPERTY = PropertyFactory.createNumeric(ExpressionFactory.dbPathExp("PAINTING_ID"), Integer.class);
+    public static final NumericIdProperty<Integer> PAINTING_ID_PK_PROPERTY = PropertyFactory.createNumericId("PAINTING_ID", "Painting", Integer.class);
     public static final String PAINTING_ID_PK_COLUMN = "PAINTING_ID";
 
     public static final NumericProperty<BigDecimal> ESTIMATED_PRICE = PropertyFactory.createNumeric("estimatedPrice", BigDecimal.class);
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/testdo/testmap/auto/_Painting1.java b/cayenne-server/src/test/java/org/apache/cayenne/testdo/testmap/auto/_Painting1.java
index 4fb23db..6d5125f 100644
--- a/cayenne-server/src/test/java/org/apache/cayenne/testdo/testmap/auto/_Painting1.java
+++ b/cayenne-server/src/test/java/org/apache/cayenne/testdo/testmap/auto/_Painting1.java
@@ -6,8 +6,8 @@ import java.io.ObjectOutputStream;
 import java.math.BigDecimal;
 
 import org.apache.cayenne.BaseDataObject;
-import org.apache.cayenne.exp.ExpressionFactory;
 import org.apache.cayenne.exp.property.EntityProperty;
+import org.apache.cayenne.exp.property.NumericIdProperty;
 import org.apache.cayenne.exp.property.NumericProperty;
 import org.apache.cayenne.exp.property.PropertyFactory;
 import org.apache.cayenne.exp.property.StringProperty;
@@ -23,7 +23,7 @@ public abstract class _Painting1 extends BaseDataObject {
 
     private static final long serialVersionUID = 1L; 
 
-    public static final NumericProperty<Integer> PAINTING_ID_PK_PROPERTY = PropertyFactory.createNumeric(ExpressionFactory.dbPathExp("PAINTING_ID"), Integer.class);
+    public static final NumericIdProperty<Integer> PAINTING_ID_PK_PROPERTY = PropertyFactory.createNumericId("PAINTING_ID", "Painting1", Integer.class);
     public static final String PAINTING_ID_PK_COLUMN = "PAINTING_ID";
 
     public static final NumericProperty<BigDecimal> ESTIMATED_PRICE = PropertyFactory.createNumeric("estimatedPrice", BigDecimal.class);
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/testdo/testmap/auto/_PaintingInfo.java b/cayenne-server/src/test/java/org/apache/cayenne/testdo/testmap/auto/_PaintingInfo.java
index b5bacbe..52451b1 100644
--- a/cayenne-server/src/test/java/org/apache/cayenne/testdo/testmap/auto/_PaintingInfo.java
+++ b/cayenne-server/src/test/java/org/apache/cayenne/testdo/testmap/auto/_PaintingInfo.java
@@ -5,10 +5,9 @@ import java.io.ObjectInputStream;
 import java.io.ObjectOutputStream;
 
 import org.apache.cayenne.BaseDataObject;
-import org.apache.cayenne.exp.ExpressionFactory;
 import org.apache.cayenne.exp.property.BaseProperty;
 import org.apache.cayenne.exp.property.EntityProperty;
-import org.apache.cayenne.exp.property.NumericProperty;
+import org.apache.cayenne.exp.property.NumericIdProperty;
 import org.apache.cayenne.exp.property.PropertyFactory;
 import org.apache.cayenne.exp.property.StringProperty;
 import org.apache.cayenne.testdo.testmap.Painting;
@@ -23,7 +22,7 @@ public abstract class _PaintingInfo extends BaseDataObject {
 
     private static final long serialVersionUID = 1L; 
 
-    public static final NumericProperty<Integer> PAINTING_ID_PK_PROPERTY = PropertyFactory.createNumeric(ExpressionFactory.dbPathExp("PAINTING_ID"), Integer.class);
+    public static final NumericIdProperty<Integer> PAINTING_ID_PK_PROPERTY = PropertyFactory.createNumericId("PAINTING_ID", "PaintingInfo", Integer.class);
     public static final String PAINTING_ID_PK_COLUMN = "PAINTING_ID";
 
     public static final BaseProperty<byte[]> IMAGE_BLOB = PropertyFactory.createBase("imageBlob", byte[].class);
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/testdo/testmap/auto/_ROArtist.java b/cayenne-server/src/test/java/org/apache/cayenne/testdo/testmap/auto/_ROArtist.java
index 140d384..4bbd30f 100644
--- a/cayenne-server/src/test/java/org/apache/cayenne/testdo/testmap/auto/_ROArtist.java
+++ b/cayenne-server/src/test/java/org/apache/cayenne/testdo/testmap/auto/_ROArtist.java
@@ -7,10 +7,9 @@ import java.sql.Date;
 import java.util.List;
 
 import org.apache.cayenne.BaseDataObject;
-import org.apache.cayenne.exp.ExpressionFactory;
 import org.apache.cayenne.exp.property.DateProperty;
 import org.apache.cayenne.exp.property.ListProperty;
-import org.apache.cayenne.exp.property.NumericProperty;
+import org.apache.cayenne.exp.property.NumericIdProperty;
 import org.apache.cayenne.exp.property.PropertyFactory;
 import org.apache.cayenne.exp.property.StringProperty;
 import org.apache.cayenne.testdo.testmap.Painting;
@@ -25,7 +24,7 @@ public abstract class _ROArtist extends BaseDataObject {
 
     private static final long serialVersionUID = 1L; 
 
-    public static final NumericProperty<Long> ARTIST_ID_PK_PROPERTY = PropertyFactory.createNumeric(ExpressionFactory.dbPathExp("ARTIST_ID"), Long.class);
+    public static final NumericIdProperty<Long> ARTIST_ID_PK_PROPERTY = PropertyFactory.createNumericId("ARTIST_ID", "ROArtist", Long.class);
     public static final String ARTIST_ID_PK_COLUMN = "ARTIST_ID";
 
     public static final StringProperty<String> ARTIST_NAME = PropertyFactory.createString("artistName", String.class);
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/testdo/testmap/auto/_ROPainting.java b/cayenne-server/src/test/java/org/apache/cayenne/testdo/testmap/auto/_ROPainting.java
index 5ac5555..f8f612e 100644
--- a/cayenne-server/src/test/java/org/apache/cayenne/testdo/testmap/auto/_ROPainting.java
+++ b/cayenne-server/src/test/java/org/apache/cayenne/testdo/testmap/auto/_ROPainting.java
@@ -6,8 +6,8 @@ import java.io.ObjectOutputStream;
 import java.math.BigDecimal;
 
 import org.apache.cayenne.BaseDataObject;
-import org.apache.cayenne.exp.ExpressionFactory;
 import org.apache.cayenne.exp.property.EntityProperty;
+import org.apache.cayenne.exp.property.NumericIdProperty;
 import org.apache.cayenne.exp.property.NumericProperty;
 import org.apache.cayenne.exp.property.PropertyFactory;
 import org.apache.cayenne.exp.property.StringProperty;
@@ -23,7 +23,7 @@ public abstract class _ROPainting extends BaseDataObject {
 
     private static final long serialVersionUID = 1L; 
 
-    public static final NumericProperty<Integer> PAINTING_ID_PK_PROPERTY = PropertyFactory.createNumeric(ExpressionFactory.dbPathExp("PAINTING_ID"), Integer.class);
+    public static final NumericIdProperty<Integer> PAINTING_ID_PK_PROPERTY = PropertyFactory.createNumericId("PAINTING_ID", "ROPainting", Integer.class);
     public static final String PAINTING_ID_PK_COLUMN = "PAINTING_ID";
 
     public static final NumericProperty<BigDecimal> ESTIMATED_PRICE = PropertyFactory.createNumeric("estimatedPrice", BigDecimal.class);
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/testdo/testmap/auto/_RWCompoundPainting.java b/cayenne-server/src/test/java/org/apache/cayenne/testdo/testmap/auto/_RWCompoundPainting.java
index 20745c9..fb172f3 100644
--- a/cayenne-server/src/test/java/org/apache/cayenne/testdo/testmap/auto/_RWCompoundPainting.java
+++ b/cayenne-server/src/test/java/org/apache/cayenne/testdo/testmap/auto/_RWCompoundPainting.java
@@ -6,7 +6,7 @@ import java.io.ObjectOutputStream;
 import java.math.BigDecimal;
 
 import org.apache.cayenne.BaseDataObject;
-import org.apache.cayenne.exp.ExpressionFactory;
+import org.apache.cayenne.exp.property.NumericIdProperty;
 import org.apache.cayenne.exp.property.NumericProperty;
 import org.apache.cayenne.exp.property.PropertyFactory;
 import org.apache.cayenne.exp.property.StringProperty;
@@ -21,7 +21,7 @@ public abstract class _RWCompoundPainting extends BaseDataObject {
 
     private static final long serialVersionUID = 1L; 
 
-    public static final NumericProperty<Integer> PAINTING_ID_PK_PROPERTY = PropertyFactory.createNumeric(ExpressionFactory.dbPathExp("PAINTING_ID"), Integer.class);
+    public static final NumericIdProperty<Integer> PAINTING_ID_PK_PROPERTY = PropertyFactory.createNumericId("PAINTING_ID", "RWCompoundPainting", Integer.class);
     public static final String PAINTING_ID_PK_COLUMN = "PAINTING_ID";
 
     public static final NumericProperty<BigDecimal> ESTIMATED_PRICE = PropertyFactory.createNumeric("estimatedPrice", BigDecimal.class);
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/testdo/testmap/auto/_SubPainting.java b/cayenne-server/src/test/java/org/apache/cayenne/testdo/testmap/auto/_SubPainting.java
index e2c6422..fe4f3c4 100644
--- a/cayenne-server/src/test/java/org/apache/cayenne/testdo/testmap/auto/_SubPainting.java
+++ b/cayenne-server/src/test/java/org/apache/cayenne/testdo/testmap/auto/_SubPainting.java
@@ -5,8 +5,7 @@ import java.io.ObjectInputStream;
 import java.io.ObjectOutputStream;
 
 import org.apache.cayenne.BaseDataObject;
-import org.apache.cayenne.exp.ExpressionFactory;
-import org.apache.cayenne.exp.property.NumericProperty;
+import org.apache.cayenne.exp.property.NumericIdProperty;
 import org.apache.cayenne.exp.property.PropertyFactory;
 import org.apache.cayenne.exp.property.StringProperty;
 
@@ -20,7 +19,7 @@ public abstract class _SubPainting extends BaseDataObject {
 
     private static final long serialVersionUID = 1L; 
 
-    public static final NumericProperty<Integer> PAINTING_ID_PK_PROPERTY = PropertyFactory.createNumeric(ExpressionFactory.dbPathExp("PAINTING_ID"), Integer.class);
+    public static final NumericIdProperty<Integer> PAINTING_ID_PK_PROPERTY = PropertyFactory.createNumericId("PAINTING_ID", "SubPainting", Integer.class);
     public static final String PAINTING_ID_PK_COLUMN = "PAINTING_ID";
 
     public static final StringProperty<String> PAINTING_TITLE = PropertyFactory.createString("paintingTitle", String.class);
diff --git a/cayenne-server/src/test/resources/testmap.map.xml b/cayenne-server/src/test/resources/testmap.map.xml
index 442ce65..5ec09e0 100644
--- a/cayenne-server/src/test/resources/testmap.map.xml
+++ b/cayenne-server/src/test/resources/testmap.map.xml
@@ -241,12 +241,21 @@
 	<obj-relationship name="painting" source="PaintingInfo" target="Painting" deleteRule="Nullify" db-relationship-path="painting"/>
 	<obj-relationship name="paintingArray" source="ROArtist" target="Painting" deleteRule="Deny" db-relationship-path="paintingArray"/>
 	<obj-relationship name="toArtist" source="ROPainting" target="Artist" deleteRule="Nullify" db-relationship-path="toArtist"/>
+	<query name="EjbqlQueryTest" type="EJBQLQuery">
+		<property name="cayenne.GenericSelectQuery.cacheStrategy" value="SHARED_CACHE"/>
+		<property name="cayenne.GenericSelectQuery.fetchingDataRows" value="true"/>
+		<ejbql><![CDATA[select a from Artist a]]></ejbql>
+	</query>
 	<query name="NonSelectingQuery" type="SQLTemplate" root="data-map" root-name="testmap">
 		<property name="cayenne.SQLTemplate.columnNameCapitalization" value="UPPER"/>
 		<sql><![CDATA[INSERT INTO PAINTING (PAINTING_ID, PAINTING_TITLE, ESTIMATED_PRICE)
 VALUES (512, 'No Painting Like This', 12.5)]]></sql>
 		<sql adapter-class="org.apache.cayenne.dba.db2.DB2Adapter"><![CDATA[INSERT INTO PAINTING (PAINTING_ID, PAINTING_TITLE, ESTIMATED_PRICE) VALUES (512, 'No Painting Like This', 12.5)]]></sql>
 	</query>
+	<query name="ObjectQuery" type="SelectQuery" root="obj-entity" root-name="Painting">
+		<qualifier><![CDATA[toArtist = $artist]]></qualifier>
+		<ordering><![CDATA[paintingTitle]]></ordering>
+	</query>
 	<query name="ParameterizedNonSelectingQuery" type="SQLTemplate" root="data-map" root-name="testmap">
 		<sql><![CDATA[INSERT INTO PAINTING (PAINTING_ID, PAINTING_TITLE, ESTIMATED_PRICE)
 VALUES (#bind($id), #bind($title), #bind($price))]]></sql>
@@ -256,32 +265,11 @@ VALUES (#bind($id), #bind($title), #bind($price))]]></sql>
 		<property name="cayenne.GenericSelectQuery.cacheStrategy" value="LOCAL_CACHE"/>
 		<qualifier><![CDATA[artistName like $name]]></qualifier>
 	</query>
-	<query name="ObjectQuery" type="SelectQuery" root="obj-entity" root-name="Painting">
-		<qualifier><![CDATA[toArtist = $artist]]></qualifier>
-		<ordering><![CDATA[paintingTitle]]></ordering>
-	</query>
-	<query name="QueryWithQualifier" type="SelectQuery" root="obj-entity" root-name="Artist">
-		<qualifier><![CDATA[artistName = $param1]]></qualifier>
-	</query>
-	<query name="SelectTestUpper" type="SQLTemplate" root="data-map" root-name="testmap">
-		<property name="cayenne.SQLTemplate.columnNameCapitalization" value="UPPER"/>
-		<property name="cayenne.GenericSelectQuery.fetchingDataRows" value="true"/>
-		<sql><![CDATA[select * from ARTIST]]></sql>
-	</query>
-	<query name="EjbqlQueryTest" type="EJBQLQuery">
-		<property name="cayenne.GenericSelectQuery.cacheStrategy" value="SHARED_CACHE"/>
-		<property name="cayenne.GenericSelectQuery.fetchingDataRows" value="true"/>
-		<ejbql><![CDATA[select a from Artist a]]></ejbql>
-	</query>
 	<query name="ParameterizedQueryWithSharedCache" type="SelectQuery" root="obj-entity" root-name="Artist">
 		<property name="cayenne.GenericSelectQuery.cacheStrategy" value="SHARED_CACHE"/>
 		<qualifier><![CDATA[artistName like $name]]></qualifier>
 	</query>
-	<query name="SelectTestLower" type="SQLTemplate" root="data-map" root-name="testmap">
-		<property name="cayenne.SQLTemplate.columnNameCapitalization" value="LOWER"/>
-		<property name="cayenne.GenericSelectQuery.fetchingDataRows" value="true"/>
-		<sql><![CDATA[select * from ARTIST]]></sql>
-	</query>
+	<query name="ProcedureQuery" type="ProcedureQuery" root="procedure" root-name="cayenne_tst_select_proc" result-entity="Artist"/>
 	<query name="QueryWithLocalCache" type="SelectQuery" root="obj-entity" root-name="Artist">
 		<property name="cayenne.GenericSelectQuery.cacheStrategy" value="LOCAL_CACHE"/>
 	</query>
@@ -289,13 +277,25 @@ VALUES (#bind($id), #bind($title), #bind($price))]]></sql>
 		<ordering descending="true" ignore-case="true"><![CDATA[artistName]]></ordering>
 		<ordering><![CDATA[dateOfBirth]]></ordering>
 	</query>
+	<query name="QueryWithPrefetch" type="SelectQuery" root="obj-entity" root-name="Gallery">
+		<prefetch><![CDATA[paintingArray]]></prefetch>
+	</query>
+	<query name="QueryWithQualifier" type="SelectQuery" root="obj-entity" root-name="Artist">
+		<qualifier><![CDATA[artistName = $param1]]></qualifier>
+	</query>
 	<query name="QueryWithSharedCache" type="SelectQuery" root="obj-entity" root-name="Artist">
 		<property name="cayenne.GenericSelectQuery.cacheStrategy" value="SHARED_CACHE"/>
 	</query>
-	<query name="QueryWithPrefetch" type="SelectQuery" root="obj-entity" root-name="Gallery">
-		<prefetch><![CDATA[paintingArray]]></prefetch>
+	<query name="SelectTestLower" type="SQLTemplate" root="data-map" root-name="testmap">
+		<property name="cayenne.SQLTemplate.columnNameCapitalization" value="LOWER"/>
+		<property name="cayenne.GenericSelectQuery.fetchingDataRows" value="true"/>
+		<sql><![CDATA[select * from ARTIST]]></sql>
+	</query>
+	<query name="SelectTestUpper" type="SQLTemplate" root="data-map" root-name="testmap">
+		<property name="cayenne.SQLTemplate.columnNameCapitalization" value="UPPER"/>
+		<property name="cayenne.GenericSelectQuery.fetchingDataRows" value="true"/>
+		<sql><![CDATA[select * from ARTIST]]></sql>
 	</query>
-	<query name="ProcedureQuery" type="ProcedureQuery" root="procedure" root-name="cayenne_tst_select_proc" result-entity="Artist"/>
 	<cgen xmlns="http://cayenne.apache.org/schema/10/cgen">
 		<destDir>../java</destDir>
 		<mode>entity</mode>


[cayenne] 04/04: fix tests

Posted by nt...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 93bcf442500dedda81bccfefb63f1ac8c63accd6
Author: Nikita Timofeev <st...@gmail.com>
AuthorDate: Mon Sep 2 13:32:31 2019 +0300

    fix tests
---
 .../test/java/org/apache/cayenne/event/JMSBridgeProviderTest.java   | 6 ++++++
 .../test/java/org/apache/cayenne/event/XMPPBridgeProviderTest.java  | 6 ++++++
 2 files changed, 12 insertions(+)

diff --git a/cayenne-jms/src/test/java/org/apache/cayenne/event/JMSBridgeProviderTest.java b/cayenne-jms/src/test/java/org/apache/cayenne/event/JMSBridgeProviderTest.java
index 5989a49..f8f9c5a 100644
--- a/cayenne-jms/src/test/java/org/apache/cayenne/event/JMSBridgeProviderTest.java
+++ b/cayenne-jms/src/test/java/org/apache/cayenne/event/JMSBridgeProviderTest.java
@@ -27,10 +27,14 @@ import org.apache.cayenne.access.flush.operation.DbRowOpSorter;
 import org.apache.cayenne.configuration.Constants;
 import org.apache.cayenne.configuration.DefaultRuntimeProperties;
 import org.apache.cayenne.configuration.RuntimeProperties;
+import org.apache.cayenne.di.AdhocObjectFactory;
 import org.apache.cayenne.di.Binder;
+import org.apache.cayenne.di.ClassLoaderManager;
 import org.apache.cayenne.di.DIBootstrap;
 import org.apache.cayenne.di.Injector;
 import org.apache.cayenne.di.Module;
+import org.apache.cayenne.di.spi.DefaultAdhocObjectFactory;
+import org.apache.cayenne.di.spi.DefaultClassLoaderManager;
 import org.apache.cayenne.log.Slf4jJdbcEventLogger;
 import org.apache.cayenne.log.JdbcEventLogger;
 import org.apache.cayenne.tx.DefaultTransactionFactory;
@@ -88,6 +92,8 @@ public class JMSBridgeProviderTest {
             binder.bind(RuntimeProperties.class).to(DefaultRuntimeProperties.class);
             binder.bind(DataDomainFlushActionFactory.class).to(DefaultDataDomainFlushActionFactory.class);
             binder.bind(DbRowOpSorter.class).to(DefaultDbRowOpSorter.class);
+            binder.bind(AdhocObjectFactory.class).to(DefaultAdhocObjectFactory.class);
+            binder.bind(ClassLoaderManager.class).to(DefaultClassLoaderManager.class);
         }
     }
 }
diff --git a/cayenne-xmpp/src/test/java/org/apache/cayenne/event/XMPPBridgeProviderTest.java b/cayenne-xmpp/src/test/java/org/apache/cayenne/event/XMPPBridgeProviderTest.java
index e697eba..9662f61 100644
--- a/cayenne-xmpp/src/test/java/org/apache/cayenne/event/XMPPBridgeProviderTest.java
+++ b/cayenne-xmpp/src/test/java/org/apache/cayenne/event/XMPPBridgeProviderTest.java
@@ -27,10 +27,14 @@ import org.apache.cayenne.access.flush.operation.DbRowOpSorter;
 import org.apache.cayenne.configuration.Constants;
 import org.apache.cayenne.configuration.DefaultRuntimeProperties;
 import org.apache.cayenne.configuration.RuntimeProperties;
+import org.apache.cayenne.di.AdhocObjectFactory;
 import org.apache.cayenne.di.Binder;
+import org.apache.cayenne.di.ClassLoaderManager;
 import org.apache.cayenne.di.DIBootstrap;
 import org.apache.cayenne.di.Injector;
 import org.apache.cayenne.di.Module;
+import org.apache.cayenne.di.spi.DefaultAdhocObjectFactory;
+import org.apache.cayenne.di.spi.DefaultClassLoaderManager;
 import org.apache.cayenne.log.Slf4jJdbcEventLogger;
 import org.apache.cayenne.log.JdbcEventLogger;
 import org.apache.cayenne.tx.DefaultTransactionFactory;
@@ -107,6 +111,8 @@ public class XMPPBridgeProviderTest {
             binder.bind(RuntimeProperties.class).to(DefaultRuntimeProperties.class);
             binder.bind(DataDomainFlushActionFactory.class).to(DefaultDataDomainFlushActionFactory.class);
             binder.bind(DbRowOpSorter.class).to(DefaultDbRowOpSorter.class);
+            binder.bind(AdhocObjectFactory.class).to(DefaultAdhocObjectFactory.class);
+            binder.bind(ClassLoaderManager.class).to(DefaultClassLoaderManager.class);
         }
     }
 }