Repository: cayenne
Updated Branches:
  refs/heads/master 4911ad11d -> 5e9f0e0f6


http://git-wip-us.apache.org/repos/asf/cayenne/blob/5e9f0e0f/cayenne-server/src/main/java/org/apache/cayenne/configuration/server/ServerModule.java
----------------------------------------------------------------------
diff --git 
a/cayenne-server/src/main/java/org/apache/cayenne/configuration/server/ServerModule.java
 
b/cayenne-server/src/main/java/org/apache/cayenne/configuration/server/ServerModule.java
index 61ba17b..74b8109 100644
--- 
a/cayenne-server/src/main/java/org/apache/cayenne/configuration/server/ServerModule.java
+++ 
b/cayenne-server/src/main/java/org/apache/cayenne/configuration/server/ServerModule.java
@@ -35,13 +35,14 @@ import 
org.apache.cayenne.access.translator.batch.DefaultBatchTranslatorFactory;
 import 
org.apache.cayenne.access.translator.select.DefaultSelectTranslatorFactory;
 import org.apache.cayenne.access.translator.select.SelectTranslatorFactory;
 import org.apache.cayenne.access.types.BigDecimalType;
-import org.apache.cayenne.access.types.BigIntegerType;
+import org.apache.cayenne.access.types.BigIntegerValueType;
 import org.apache.cayenne.access.types.BooleanType;
 import org.apache.cayenne.access.types.ByteArrayType;
 import org.apache.cayenne.access.types.ByteType;
 import org.apache.cayenne.access.types.CalendarType;
 import org.apache.cayenne.access.types.CharType;
 import org.apache.cayenne.access.types.DateType;
+import org.apache.cayenne.access.types.DefaultValueObjectTypeRegistry;
 import org.apache.cayenne.access.types.DoubleType;
 import org.apache.cayenne.access.types.ExtendedType;
 import org.apache.cayenne.access.types.ExtendedTypeFactory;
@@ -51,8 +52,9 @@ import org.apache.cayenne.access.types.LongType;
 import org.apache.cayenne.access.types.ShortType;
 import org.apache.cayenne.access.types.TimeType;
 import org.apache.cayenne.access.types.TimestampType;
-import org.apache.cayenne.access.types.UUIDType;
+import org.apache.cayenne.access.types.UUIDValueType;
 import org.apache.cayenne.access.types.UtilDateType;
+import org.apache.cayenne.access.types.ValueObjectTypeRegistry;
 import org.apache.cayenne.access.types.VoidType;
 import org.apache.cayenne.ashwood.AshwoodEntitySorter;
 import org.apache.cayenne.cache.MapQueryCacheProvider;
@@ -101,6 +103,7 @@ import org.apache.cayenne.event.EventManager;
 import org.apache.cayenne.log.CommonsJdbcEventLogger;
 import org.apache.cayenne.log.JdbcEventLogger;
 import org.apache.cayenne.map.EntitySorter;
+import org.apache.cayenne.access.types.ValueObjectType;
 import org.apache.cayenne.resource.ClassLoaderResourceLocator;
 import org.apache.cayenne.resource.ResourceLocator;
 import org.apache.cayenne.tx.DefaultTransactionFactory;
@@ -245,6 +248,16 @@ public class ServerModule implements Module {
     }
 
     /**
+     *
+     * @param binder DI binder passed to module during injector startup
+     * @return ListBuilder for user-contributed ValueObjectTypes
+     * @since 4.0
+     */
+    public static ListBuilder<ValueObjectType> 
contributeValueObjectTypes(Binder binder) {
+        return binder.bindList(ValueObjectType.class);
+    }
+
+    /**
      * Creates a new {@link ServerModule}.
      *
      * @since 4.0
@@ -300,16 +313,24 @@ public class ServerModule implements Module {
         contributeDomainListeners(binder);
 
         // configure extended types
-        contributeDefaultTypes(binder).add(new VoidType()).add(new 
BigDecimalType())
-                .add(new BigIntegerType()).add(new BooleanType()).add(new 
ByteArrayType(false, true))
-                .add(new ByteType(false)).add(new CharType(false, 
true)).add(new DateType()).add(new DoubleType())
-                .add(new FloatType()).add(new IntegerType()).add(new 
LongType()).add(new ShortType(false))
-                .add(new TimeType()).add(new TimestampType()).add(new 
UtilDateType())
-                .add(new 
CalendarType<GregorianCalendar>(GregorianCalendar.class))
-                .add(new CalendarType<Calendar>(Calendar.class)).add(new 
UUIDType());
+        contributeDefaultTypes(binder)
+                .add(new VoidType())
+                .add(new BigDecimalType())
+                .add(new BooleanType()).add(new ByteType(false)).add(new 
CharType(false, true))
+                .add(new DoubleType()).add(new FloatType()).add(new 
IntegerType()).add(new LongType()).add(new ShortType(false))
+                .add(new ByteArrayType(false, true))
+                .add(new DateType()).add(new TimeType()).add(new 
TimestampType())
+                // should be converted from ExtendedType to ValueType
+                .add(new UtilDateType()).add(new 
CalendarType<>(GregorianCalendar.class)).add(new 
CalendarType<>(Calendar.class));
         contributeUserTypes(binder);
         contributeTypeFactories(binder);
 
+        // Custom ValueObjects types contribution
+        contributeValueObjectTypes(binder)
+                .add(BigIntegerValueType.class)
+                .add(UUIDValueType.class);
+        
binder.bind(ValueObjectTypeRegistry.class).to(DefaultValueObjectTypeRegistry.class);
+
         // configure explicit configurations
         ListBuilder<String> locationsListBuilder = 
contributeProjectLocations(binder);
         for (String location : configurationLocations) {

http://git-wip-us.apache.org/repos/asf/cayenne/blob/5e9f0e0f/cayenne-server/src/main/java/org/apache/cayenne/dba/JdbcAdapter.java
----------------------------------------------------------------------
diff --git 
a/cayenne-server/src/main/java/org/apache/cayenne/dba/JdbcAdapter.java 
b/cayenne-server/src/main/java/org/apache/cayenne/dba/JdbcAdapter.java
index 01e244e..b163a88 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/dba/JdbcAdapter.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/dba/JdbcAdapter.java
@@ -32,6 +32,8 @@ import 
org.apache.cayenne.access.translator.select.SelectTranslator;
 import org.apache.cayenne.access.types.ExtendedType;
 import org.apache.cayenne.access.types.ExtendedTypeFactory;
 import org.apache.cayenne.access.types.ExtendedTypeMap;
+import org.apache.cayenne.access.types.ValueObjectTypeFactory;
+import org.apache.cayenne.access.types.ValueObjectTypeRegistry;
 import org.apache.cayenne.configuration.Constants;
 import org.apache.cayenne.configuration.RuntimeProperties;
 import org.apache.cayenne.di.Inject;
@@ -78,8 +80,7 @@ public class JdbcAdapter implements DbAdapter {
 
        /**
         * @since 3.1
-        * @deprecated since 4.0 BatchQueryBuilderfactory is attached to the
-        * DataNode.
+        * @deprecated since 4.0 BatchQueryBuilderfactory is attached to the 
DataNode.
         */
        @Inject
        protected BatchTranslatorFactory batchQueryBuilderFactory;
@@ -94,7 +95,8 @@ public class JdbcAdapter implements DbAdapter {
                           @Inject(Constants.SERVER_DEFAULT_TYPES_LIST) 
List<ExtendedType> defaultExtendedTypes,
                           @Inject(Constants.SERVER_USER_TYPES_LIST) 
List<ExtendedType> userExtendedTypes,
                           @Inject(Constants.SERVER_TYPE_FACTORIES_LIST) 
List<ExtendedTypeFactory> extendedTypeFactories,
-                          @Inject(Constants.SERVER_RESOURCE_LOCATOR) 
ResourceLocator resourceLocator) {
+                          @Inject(Constants.SERVER_RESOURCE_LOCATOR) 
ResourceLocator resourceLocator,
+                                          @Inject ValueObjectTypeRegistry 
valueObjectTypeRegistry) {
 
                // init defaults
                this.setSupportsBatchUpdates(false);
@@ -108,7 +110,7 @@ public class JdbcAdapter implements DbAdapter {
                this.ejbqlTranslatorFactory = createEJBQLTranslatorFactory();
                this.typesHandler = 
TypesHandler.getHandler(findResource("/types.xml"));
                this.extendedTypes = new ExtendedTypeMap();
-               initExtendedTypes(defaultExtendedTypes, userExtendedTypes, 
extendedTypeFactories);
+               initExtendedTypes(defaultExtendedTypes, userExtendedTypes, 
extendedTypeFactories, valueObjectTypeRegistry);
        }
 
        /**
@@ -159,7 +161,7 @@ public class JdbcAdapter implements DbAdapter {
        }
 
        /**
-        * Called from {@link #initExtendedTypes(List, List, List)} to load
+        * Called from {@link #initExtendedTypes(List, List, List, 
ValueObjectTypeRegistry)} to load
         * adapter-specific types into the ExtendedTypeMap right after the 
default
         * types are loaded, but before the DI overrides are. This method has
         * specific implementations in JdbcAdapter subclasses.
@@ -172,7 +174,8 @@ public class JdbcAdapter implements DbAdapter {
         * @since 3.1
         */
        protected void initExtendedTypes(List<ExtendedType> 
defaultExtendedTypes, List<ExtendedType> userExtendedTypes,
-                                        List<ExtendedTypeFactory> 
extendedTypeFactories) {
+                                        List<ExtendedTypeFactory> 
extendedTypeFactories,
+                                                                        
ValueObjectTypeRegistry valueObjectTypeRegistry) {
                for (ExtendedType type : defaultExtendedTypes) {
                        extendedTypes.registerType(type);
                }
@@ -186,6 +189,7 @@ public class JdbcAdapter implements DbAdapter {
                for (ExtendedTypeFactory typeFactory : extendedTypeFactories) {
                        extendedTypes.addFactory(typeFactory);
                }
+               extendedTypes.addFactory(new 
ValueObjectTypeFactory(extendedTypes, valueObjectTypeRegistry));
        }
 
        /**
@@ -285,11 +289,7 @@ public class JdbcAdapter implements DbAdapter {
         */
        @Override
        public Collection<String> dropTableStatements(DbEntity table) {
-
-               StringBuilder buf = new StringBuilder("DROP TABLE ");
-               buf.append(quotingStrategy.quotedFullyQualifiedName(table));
-
-               return Collections.singleton(buf.toString());
+               return Collections.singleton("DROP TABLE " + 
quotingStrategy.quotedFullyQualifiedName(table));
        }
 
        /**

http://git-wip-us.apache.org/repos/asf/cayenne/blob/5e9f0e0f/cayenne-server/src/main/java/org/apache/cayenne/dba/db2/DB2Adapter.java
----------------------------------------------------------------------
diff --git 
a/cayenne-server/src/main/java/org/apache/cayenne/dba/db2/DB2Adapter.java 
b/cayenne-server/src/main/java/org/apache/cayenne/dba/db2/DB2Adapter.java
index d1415bf..7ac6dec 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/dba/db2/DB2Adapter.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/dba/db2/DB2Adapter.java
@@ -36,6 +36,7 @@ import org.apache.cayenne.access.types.CharType;
 import org.apache.cayenne.access.types.ExtendedType;
 import org.apache.cayenne.access.types.ExtendedTypeFactory;
 import org.apache.cayenne.access.types.ExtendedTypeMap;
+import org.apache.cayenne.access.types.ValueObjectTypeRegistry;
 import org.apache.cayenne.configuration.Constants;
 import org.apache.cayenne.configuration.RuntimeProperties;
 import org.apache.cayenne.dba.JdbcAdapter;
@@ -67,8 +68,9 @@ public class DB2Adapter extends JdbcAdapter {
             @Inject(Constants.SERVER_DEFAULT_TYPES_LIST) List<ExtendedType> 
defaultExtendedTypes,
             @Inject(Constants.SERVER_USER_TYPES_LIST) List<ExtendedType> 
userExtendedTypes,
             @Inject(Constants.SERVER_TYPE_FACTORIES_LIST) 
List<ExtendedTypeFactory> extendedTypeFactories,
-            @Inject(Constants.SERVER_RESOURCE_LOCATOR) ResourceLocator 
resourceLocator) {
-        super(runtimeProperties, defaultExtendedTypes, userExtendedTypes, 
extendedTypeFactories, resourceLocator);
+            @Inject(Constants.SERVER_RESOURCE_LOCATOR) ResourceLocator 
resourceLocator,
+            @Inject ValueObjectTypeRegistry valueObjectTypeRegistry) {
+        super(runtimeProperties, defaultExtendedTypes, userExtendedTypes, 
extendedTypeFactories, resourceLocator, valueObjectTypeRegistry);
         setSupportsGeneratedKeys(true);
     }
 

http://git-wip-us.apache.org/repos/asf/cayenne/blob/5e9f0e0f/cayenne-server/src/main/java/org/apache/cayenne/dba/derby/DerbyAdapter.java
----------------------------------------------------------------------
diff --git 
a/cayenne-server/src/main/java/org/apache/cayenne/dba/derby/DerbyAdapter.java 
b/cayenne-server/src/main/java/org/apache/cayenne/dba/derby/DerbyAdapter.java
index bca04a1..d2bb67e 100644
--- 
a/cayenne-server/src/main/java/org/apache/cayenne/dba/derby/DerbyAdapter.java
+++ 
b/cayenne-server/src/main/java/org/apache/cayenne/dba/derby/DerbyAdapter.java
@@ -31,6 +31,7 @@ import org.apache.cayenne.access.types.ExtendedType;
 import org.apache.cayenne.access.types.ExtendedTypeFactory;
 import org.apache.cayenne.access.types.ExtendedTypeMap;
 import org.apache.cayenne.access.types.ShortType;
+import org.apache.cayenne.access.types.ValueObjectTypeRegistry;
 import org.apache.cayenne.configuration.Constants;
 import org.apache.cayenne.configuration.RuntimeProperties;
 import org.apache.cayenne.dba.JdbcAdapter;
@@ -72,13 +73,15 @@ public class DerbyAdapter extends JdbcAdapter {
             @Inject(Constants.SERVER_DEFAULT_TYPES_LIST) List<ExtendedType> 
defaultExtendedTypes,
             @Inject(Constants.SERVER_USER_TYPES_LIST) List<ExtendedType> 
userExtendedTypes,
             @Inject(Constants.SERVER_TYPE_FACTORIES_LIST) 
List<ExtendedTypeFactory> extendedTypeFactories,
-            @Inject(Constants.SERVER_RESOURCE_LOCATOR) ResourceLocator 
resourceLocator) {
+            @Inject(Constants.SERVER_RESOURCE_LOCATOR) ResourceLocator 
resourceLocator,
+            @Inject ValueObjectTypeRegistry valueObjectTypeRegistry) {
         super(
                 runtimeProperties,
                 defaultExtendedTypes,
                 userExtendedTypes,
                 extendedTypeFactories,
-                resourceLocator);
+                resourceLocator,
+                valueObjectTypeRegistry);
         setSupportsGeneratedKeys(true);
         setSupportsBatchUpdates(true);
     }

http://git-wip-us.apache.org/repos/asf/cayenne/blob/5e9f0e0f/cayenne-server/src/main/java/org/apache/cayenne/dba/firebird/FirebirdAdapter.java
----------------------------------------------------------------------
diff --git 
a/cayenne-server/src/main/java/org/apache/cayenne/dba/firebird/FirebirdAdapter.java
 
b/cayenne-server/src/main/java/org/apache/cayenne/dba/firebird/FirebirdAdapter.java
index ae754d5..a878be2 100644
--- 
a/cayenne-server/src/main/java/org/apache/cayenne/dba/firebird/FirebirdAdapter.java
+++ 
b/cayenne-server/src/main/java/org/apache/cayenne/dba/firebird/FirebirdAdapter.java
@@ -28,6 +28,7 @@ import org.apache.cayenne.access.types.CharType;
 import org.apache.cayenne.access.types.ExtendedType;
 import org.apache.cayenne.access.types.ExtendedTypeFactory;
 import org.apache.cayenne.access.types.ExtendedTypeMap;
+import org.apache.cayenne.access.types.ValueObjectTypeRegistry;
 import org.apache.cayenne.configuration.Constants;
 import org.apache.cayenne.configuration.RuntimeProperties;
 import org.apache.cayenne.dba.JdbcAdapter;
@@ -58,13 +59,15 @@ public class FirebirdAdapter extends JdbcAdapter {
             @Inject(Constants.SERVER_DEFAULT_TYPES_LIST) List<ExtendedType> 
defaultExtendedTypes,
             @Inject(Constants.SERVER_USER_TYPES_LIST) List<ExtendedType> 
userExtendedTypes,
             @Inject(Constants.SERVER_TYPE_FACTORIES_LIST) 
List<ExtendedTypeFactory> extendedTypeFactories,
-            @Inject(Constants.SERVER_RESOURCE_LOCATOR) ResourceLocator 
resourceLocator) {
+            @Inject(Constants.SERVER_RESOURCE_LOCATOR) ResourceLocator 
resourceLocator,
+            @Inject ValueObjectTypeRegistry valueObjectTypeRegistry) {
         super(
                 runtimeProperties,
                 defaultExtendedTypes,
                 userExtendedTypes,
                 extendedTypeFactories,
-                resourceLocator);
+                resourceLocator,
+                valueObjectTypeRegistry);
            setSupportsBatchUpdates(true);
     }
     

http://git-wip-us.apache.org/repos/asf/cayenne/blob/5e9f0e0f/cayenne-server/src/main/java/org/apache/cayenne/dba/frontbase/FrontBaseAdapter.java
----------------------------------------------------------------------
diff --git 
a/cayenne-server/src/main/java/org/apache/cayenne/dba/frontbase/FrontBaseAdapter.java
 
b/cayenne-server/src/main/java/org/apache/cayenne/dba/frontbase/FrontBaseAdapter.java
index 564e0ed..69b9876 100644
--- 
a/cayenne-server/src/main/java/org/apache/cayenne/dba/frontbase/FrontBaseAdapter.java
+++ 
b/cayenne-server/src/main/java/org/apache/cayenne/dba/frontbase/FrontBaseAdapter.java
@@ -26,6 +26,7 @@ import 
org.apache.cayenne.access.translator.select.SelectTranslator;
 import org.apache.cayenne.access.types.ExtendedType;
 import org.apache.cayenne.access.types.ExtendedTypeFactory;
 import org.apache.cayenne.access.types.ExtendedTypeMap;
+import org.apache.cayenne.access.types.ValueObjectTypeRegistry;
 import org.apache.cayenne.configuration.Constants;
 import org.apache.cayenne.configuration.RuntimeProperties;
 import org.apache.cayenne.dba.JdbcAdapter;
@@ -72,8 +73,9 @@ public class FrontBaseAdapter extends JdbcAdapter {
                        @Inject(Constants.SERVER_DEFAULT_TYPES_LIST) 
List<ExtendedType> defaultExtendedTypes,
                        @Inject(Constants.SERVER_USER_TYPES_LIST) 
List<ExtendedType> userExtendedTypes,
                        @Inject(Constants.SERVER_TYPE_FACTORIES_LIST) 
List<ExtendedTypeFactory> extendedTypeFactories,
-                       @Inject(Constants.SERVER_RESOURCE_LOCATOR) 
ResourceLocator resourceLocator) {
-               super(runtimeProperties, defaultExtendedTypes, 
userExtendedTypes, extendedTypeFactories, resourceLocator);
+                       @Inject(Constants.SERVER_RESOURCE_LOCATOR) 
ResourceLocator resourceLocator,
+                       @Inject ValueObjectTypeRegistry 
valueObjectTypeRegistry) {
+               super(runtimeProperties, defaultExtendedTypes, 
userExtendedTypes, extendedTypeFactories, resourceLocator, 
valueObjectTypeRegistry);
                setSupportsBatchUpdates(true);
        }
 

http://git-wip-us.apache.org/repos/asf/cayenne/blob/5e9f0e0f/cayenne-server/src/main/java/org/apache/cayenne/dba/h2/H2Adapter.java
----------------------------------------------------------------------
diff --git 
a/cayenne-server/src/main/java/org/apache/cayenne/dba/h2/H2Adapter.java 
b/cayenne-server/src/main/java/org/apache/cayenne/dba/h2/H2Adapter.java
index 0d99869..6b47636 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/dba/h2/H2Adapter.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/dba/h2/H2Adapter.java
@@ -21,6 +21,7 @@ package org.apache.cayenne.dba.h2;
 
 import org.apache.cayenne.access.types.ExtendedType;
 import org.apache.cayenne.access.types.ExtendedTypeFactory;
+import org.apache.cayenne.access.types.ValueObjectTypeRegistry;
 import org.apache.cayenne.configuration.Constants;
 import org.apache.cayenne.configuration.RuntimeProperties;
 import org.apache.cayenne.dba.JdbcAdapter;
@@ -50,8 +51,9 @@ public class H2Adapter extends JdbcAdapter {
             @Inject(Constants.SERVER_DEFAULT_TYPES_LIST) List<ExtendedType> 
defaultExtendedTypes,
             @Inject(Constants.SERVER_USER_TYPES_LIST) List<ExtendedType> 
userExtendedTypes,
             @Inject(Constants.SERVER_TYPE_FACTORIES_LIST) 
List<ExtendedTypeFactory> extendedTypeFactories,
-            @Inject(Constants.SERVER_RESOURCE_LOCATOR) ResourceLocator 
resourceLocator) {
-        super(runtimeProperties, defaultExtendedTypes, userExtendedTypes, 
extendedTypeFactories, resourceLocator);
+            @Inject(Constants.SERVER_RESOURCE_LOCATOR) ResourceLocator 
resourceLocator,
+            @Inject ValueObjectTypeRegistry valueObjectTypeRegistry) {
+        super(runtimeProperties, defaultExtendedTypes, userExtendedTypes, 
extendedTypeFactories, resourceLocator, valueObjectTypeRegistry);
         setSupportsGeneratedKeys(true);
     }
 

http://git-wip-us.apache.org/repos/asf/cayenne/blob/5e9f0e0f/cayenne-server/src/main/java/org/apache/cayenne/dba/hsqldb/HSQLDBAdapter.java
----------------------------------------------------------------------
diff --git 
a/cayenne-server/src/main/java/org/apache/cayenne/dba/hsqldb/HSQLDBAdapter.java 
b/cayenne-server/src/main/java/org/apache/cayenne/dba/hsqldb/HSQLDBAdapter.java
index 314de60..64c59b3 100644
--- 
a/cayenne-server/src/main/java/org/apache/cayenne/dba/hsqldb/HSQLDBAdapter.java
+++ 
b/cayenne-server/src/main/java/org/apache/cayenne/dba/hsqldb/HSQLDBAdapter.java
@@ -31,6 +31,7 @@ import org.apache.cayenne.access.types.CharType;
 import org.apache.cayenne.access.types.ExtendedType;
 import org.apache.cayenne.access.types.ExtendedTypeFactory;
 import org.apache.cayenne.access.types.ExtendedTypeMap;
+import org.apache.cayenne.access.types.ValueObjectTypeRegistry;
 import org.apache.cayenne.configuration.Constants;
 import org.apache.cayenne.configuration.RuntimeProperties;
 import org.apache.cayenne.dba.JdbcAdapter;
@@ -72,8 +73,9 @@ public class HSQLDBAdapter extends JdbcAdapter {
                        @Inject(Constants.SERVER_DEFAULT_TYPES_LIST) 
List<ExtendedType> defaultExtendedTypes,
                        @Inject(Constants.SERVER_USER_TYPES_LIST) 
List<ExtendedType> userExtendedTypes,
                        @Inject(Constants.SERVER_TYPE_FACTORIES_LIST) 
List<ExtendedTypeFactory> extendedTypeFactories,
-                       @Inject(Constants.SERVER_RESOURCE_LOCATOR) 
ResourceLocator resourceLocator) {
-               super(runtimeProperties, defaultExtendedTypes, 
userExtendedTypes, extendedTypeFactories, resourceLocator);
+                       @Inject(Constants.SERVER_RESOURCE_LOCATOR) 
ResourceLocator resourceLocator,
+                   @Inject ValueObjectTypeRegistry valueObjectTypeRegistry) {
+               super(runtimeProperties, defaultExtendedTypes, 
userExtendedTypes, extendedTypeFactories, resourceLocator, 
valueObjectTypeRegistry);
                setSupportsGeneratedKeys(true);
        }
 

http://git-wip-us.apache.org/repos/asf/cayenne/blob/5e9f0e0f/cayenne-server/src/main/java/org/apache/cayenne/dba/hsqldb/HSQLDBNoSchemaAdapter.java
----------------------------------------------------------------------
diff --git 
a/cayenne-server/src/main/java/org/apache/cayenne/dba/hsqldb/HSQLDBNoSchemaAdapter.java
 
b/cayenne-server/src/main/java/org/apache/cayenne/dba/hsqldb/HSQLDBNoSchemaAdapter.java
index d963f27..e8b6641 100644
--- 
a/cayenne-server/src/main/java/org/apache/cayenne/dba/hsqldb/HSQLDBNoSchemaAdapter.java
+++ 
b/cayenne-server/src/main/java/org/apache/cayenne/dba/hsqldb/HSQLDBNoSchemaAdapter.java
@@ -21,6 +21,7 @@ package org.apache.cayenne.dba.hsqldb;
 
 import org.apache.cayenne.access.types.ExtendedType;
 import org.apache.cayenne.access.types.ExtendedTypeFactory;
+import org.apache.cayenne.access.types.ValueObjectTypeRegistry;
 import org.apache.cayenne.configuration.Constants;
 import org.apache.cayenne.configuration.RuntimeProperties;
 import org.apache.cayenne.di.Inject;
@@ -42,8 +43,9 @@ public class HSQLDBNoSchemaAdapter extends HSQLDBAdapter {
             @Inject(Constants.SERVER_DEFAULT_TYPES_LIST) List<ExtendedType> 
defaultExtendedTypes,
             @Inject(Constants.SERVER_USER_TYPES_LIST) List<ExtendedType> 
userExtendedTypes,
             @Inject(Constants.SERVER_TYPE_FACTORIES_LIST) 
List<ExtendedTypeFactory> extendedTypeFactories,
-            @Inject(Constants.SERVER_RESOURCE_LOCATOR) ResourceLocator 
resourceLocator) {
-        super(runtimeProperties, defaultExtendedTypes, userExtendedTypes, 
extendedTypeFactories, resourceLocator);
+            @Inject(Constants.SERVER_RESOURCE_LOCATOR) ResourceLocator 
resourceLocator,
+            @Inject ValueObjectTypeRegistry valueObjectTypeRegistry) {
+        super(runtimeProperties, defaultExtendedTypes, userExtendedTypes, 
extendedTypeFactories, resourceLocator, valueObjectTypeRegistry);
     }
 
     /**

http://git-wip-us.apache.org/repos/asf/cayenne/blob/5e9f0e0f/cayenne-server/src/main/java/org/apache/cayenne/dba/ingres/IngresAdapter.java
----------------------------------------------------------------------
diff --git 
a/cayenne-server/src/main/java/org/apache/cayenne/dba/ingres/IngresAdapter.java 
b/cayenne-server/src/main/java/org/apache/cayenne/dba/ingres/IngresAdapter.java
index 3fc8f07..2c76b2a 100644
--- 
a/cayenne-server/src/main/java/org/apache/cayenne/dba/ingres/IngresAdapter.java
+++ 
b/cayenne-server/src/main/java/org/apache/cayenne/dba/ingres/IngresAdapter.java
@@ -29,6 +29,7 @@ import 
org.apache.cayenne.access.translator.select.TrimmingQualifierTranslator;
 import org.apache.cayenne.access.types.ExtendedType;
 import org.apache.cayenne.access.types.ExtendedTypeFactory;
 import org.apache.cayenne.access.types.ExtendedTypeMap;
+import org.apache.cayenne.access.types.ValueObjectTypeRegistry;
 import org.apache.cayenne.configuration.Constants;
 import org.apache.cayenne.configuration.RuntimeProperties;
 import org.apache.cayenne.dba.JdbcAdapter;
@@ -67,8 +68,9 @@ public class IngresAdapter extends JdbcAdapter {
                             @Inject(Constants.SERVER_DEFAULT_TYPES_LIST) 
List<ExtendedType> defaultExtendedTypes,
                             @Inject(Constants.SERVER_USER_TYPES_LIST) 
List<ExtendedType> userExtendedTypes,
                             @Inject(Constants.SERVER_TYPE_FACTORIES_LIST) 
List<ExtendedTypeFactory> extendedTypeFactories,
-                            @Inject(Constants.SERVER_RESOURCE_LOCATOR) 
ResourceLocator resourceLocator) {
-               super(runtimeProperties, defaultExtendedTypes, 
userExtendedTypes, extendedTypeFactories, resourceLocator);
+                            @Inject(Constants.SERVER_RESOURCE_LOCATOR) 
ResourceLocator resourceLocator,
+                                                @Inject 
ValueObjectTypeRegistry valueObjectTypeRegistry) {
+               super(runtimeProperties, defaultExtendedTypes, 
userExtendedTypes, extendedTypeFactories, resourceLocator, 
valueObjectTypeRegistry);
                setSupportsUniqueConstraints(true);
                setSupportsGeneratedKeys(true);
        }

http://git-wip-us.apache.org/repos/asf/cayenne/blob/5e9f0e0f/cayenne-server/src/main/java/org/apache/cayenne/dba/mysql/MySQLAdapter.java
----------------------------------------------------------------------
diff --git 
a/cayenne-server/src/main/java/org/apache/cayenne/dba/mysql/MySQLAdapter.java 
b/cayenne-server/src/main/java/org/apache/cayenne/dba/mysql/MySQLAdapter.java
index 1981c2a..6e9529a 100644
--- 
a/cayenne-server/src/main/java/org/apache/cayenne/dba/mysql/MySQLAdapter.java
+++ 
b/cayenne-server/src/main/java/org/apache/cayenne/dba/mysql/MySQLAdapter.java
@@ -32,6 +32,7 @@ import org.apache.cayenne.access.types.CharType;
 import org.apache.cayenne.access.types.ExtendedType;
 import org.apache.cayenne.access.types.ExtendedTypeFactory;
 import org.apache.cayenne.access.types.ExtendedTypeMap;
+import org.apache.cayenne.access.types.ValueObjectTypeRegistry;
 import org.apache.cayenne.configuration.Constants;
 import org.apache.cayenne.configuration.RuntimeProperties;
 import org.apache.cayenne.dba.DefaultQuotingStrategy;
@@ -88,11 +89,12 @@ public class MySQLAdapter extends JdbcAdapter {
        protected String storageEngine;
 
        public MySQLAdapter(@Inject RuntimeProperties runtimeProperties,
-                       @Inject(Constants.SERVER_DEFAULT_TYPES_LIST) 
List<ExtendedType> defaultExtendedTypes,
-                       @Inject(Constants.SERVER_USER_TYPES_LIST) 
List<ExtendedType> userExtendedTypes,
-                       @Inject(Constants.SERVER_TYPE_FACTORIES_LIST) 
List<ExtendedTypeFactory> extendedTypeFactories,
-                       @Inject(Constants.SERVER_RESOURCE_LOCATOR) 
ResourceLocator resourceLocator) {
-               super(runtimeProperties, defaultExtendedTypes, 
userExtendedTypes, extendedTypeFactories, resourceLocator);
+                                               
@Inject(Constants.SERVER_DEFAULT_TYPES_LIST) List<ExtendedType> 
defaultExtendedTypes,
+                                               
@Inject(Constants.SERVER_USER_TYPES_LIST) List<ExtendedType> userExtendedTypes,
+                                               
@Inject(Constants.SERVER_TYPE_FACTORIES_LIST) List<ExtendedTypeFactory> 
extendedTypeFactories,
+                                               
@Inject(Constants.SERVER_RESOURCE_LOCATOR) ResourceLocator resourceLocator,
+                                               @Inject ValueObjectTypeRegistry 
valueObjectTypeRegistry) {
+               super(runtimeProperties, defaultExtendedTypes, 
userExtendedTypes, extendedTypeFactories, resourceLocator, 
valueObjectTypeRegistry);
 
                // init defaults
                this.storageEngine = DEFAULT_STORAGE_ENGINE;

http://git-wip-us.apache.org/repos/asf/cayenne/blob/5e9f0e0f/cayenne-server/src/main/java/org/apache/cayenne/dba/openbase/OpenBaseAdapter.java
----------------------------------------------------------------------
diff --git 
a/cayenne-server/src/main/java/org/apache/cayenne/dba/openbase/OpenBaseAdapter.java
 
b/cayenne-server/src/main/java/org/apache/cayenne/dba/openbase/OpenBaseAdapter.java
index 04d54cc..1a30779 100644
--- 
a/cayenne-server/src/main/java/org/apache/cayenne/dba/openbase/OpenBaseAdapter.java
+++ 
b/cayenne-server/src/main/java/org/apache/cayenne/dba/openbase/OpenBaseAdapter.java
@@ -35,6 +35,7 @@ import org.apache.cayenne.access.types.CharType;
 import org.apache.cayenne.access.types.ExtendedType;
 import org.apache.cayenne.access.types.ExtendedTypeFactory;
 import org.apache.cayenne.access.types.ExtendedTypeMap;
+import org.apache.cayenne.access.types.ValueObjectTypeRegistry;
 import org.apache.cayenne.configuration.Constants;
 import org.apache.cayenne.configuration.RuntimeProperties;
 import org.apache.cayenne.dba.JdbcAdapter;
@@ -68,8 +69,9 @@ public class OpenBaseAdapter extends JdbcAdapter {
                            @Inject(Constants.SERVER_DEFAULT_TYPES_LIST) 
List<ExtendedType> defaultExtendedTypes,
                            @Inject(Constants.SERVER_USER_TYPES_LIST) 
List<ExtendedType> userExtendedTypes,
                            @Inject(Constants.SERVER_TYPE_FACTORIES_LIST) 
List<ExtendedTypeFactory> extendedTypeFactories,
-                           @Inject(Constants.SERVER_RESOURCE_LOCATOR) 
ResourceLocator resourceLocator) {
-        super(runtimeProperties, defaultExtendedTypes, userExtendedTypes, 
extendedTypeFactories, resourceLocator);
+                           @Inject(Constants.SERVER_RESOURCE_LOCATOR) 
ResourceLocator resourceLocator,
+                           @Inject ValueObjectTypeRegistry 
valueObjectTypeRegistry) {
+        super(runtimeProperties, defaultExtendedTypes, userExtendedTypes, 
extendedTypeFactories, resourceLocator, valueObjectTypeRegistry);
 
         // init defaults
         this.setSupportsUniqueConstraints(false);

http://git-wip-us.apache.org/repos/asf/cayenne/blob/5e9f0e0f/cayenne-server/src/main/java/org/apache/cayenne/dba/oracle/Oracle8Adapter.java
----------------------------------------------------------------------
diff --git 
a/cayenne-server/src/main/java/org/apache/cayenne/dba/oracle/Oracle8Adapter.java
 
b/cayenne-server/src/main/java/org/apache/cayenne/dba/oracle/Oracle8Adapter.java
index 13ef0f7..60f820e 100644
--- 
a/cayenne-server/src/main/java/org/apache/cayenne/dba/oracle/Oracle8Adapter.java
+++ 
b/cayenne-server/src/main/java/org/apache/cayenne/dba/oracle/Oracle8Adapter.java
@@ -25,6 +25,7 @@ import 
org.apache.cayenne.access.translator.select.QueryAssembler;
 import org.apache.cayenne.access.translator.select.SelectTranslator;
 import org.apache.cayenne.access.types.ExtendedType;
 import org.apache.cayenne.access.types.ExtendedTypeFactory;
+import org.apache.cayenne.access.types.ValueObjectTypeRegistry;
 import org.apache.cayenne.configuration.Constants;
 import org.apache.cayenne.configuration.RuntimeProperties;
 import org.apache.cayenne.di.Inject;
@@ -54,11 +55,12 @@ public class Oracle8Adapter extends OracleAdapter {
        }
 
        public Oracle8Adapter(@Inject RuntimeProperties runtimeProperties,
-                       @Inject(Constants.SERVER_DEFAULT_TYPES_LIST) 
List<ExtendedType> defaultExtendedTypes,
-                       @Inject(Constants.SERVER_USER_TYPES_LIST) 
List<ExtendedType> userExtendedTypes,
-                       @Inject(Constants.SERVER_TYPE_FACTORIES_LIST) 
List<ExtendedTypeFactory> extendedTypeFactories,
-                       @Inject(Constants.SERVER_RESOURCE_LOCATOR) 
ResourceLocator resourceLocator) {
-               super(runtimeProperties, defaultExtendedTypes, 
userExtendedTypes, extendedTypeFactories, resourceLocator);
+                                                 
@Inject(Constants.SERVER_DEFAULT_TYPES_LIST) List<ExtendedType> 
defaultExtendedTypes,
+                                                 
@Inject(Constants.SERVER_USER_TYPES_LIST) List<ExtendedType> userExtendedTypes,
+                                                 
@Inject(Constants.SERVER_TYPE_FACTORIES_LIST) List<ExtendedTypeFactory> 
extendedTypeFactories,
+                                                 
@Inject(Constants.SERVER_RESOURCE_LOCATOR) ResourceLocator resourceLocator,
+                                                 @Inject 
ValueObjectTypeRegistry valueObjectTypeRegistry) {
+               super(runtimeProperties, defaultExtendedTypes, 
userExtendedTypes, extendedTypeFactories, resourceLocator, 
valueObjectTypeRegistry);
        }
 
        private static void initOracle8DriverInformation() {

http://git-wip-us.apache.org/repos/asf/cayenne/blob/5e9f0e0f/cayenne-server/src/main/java/org/apache/cayenne/dba/oracle/OracleAdapter.java
----------------------------------------------------------------------
diff --git 
a/cayenne-server/src/main/java/org/apache/cayenne/dba/oracle/OracleAdapter.java 
b/cayenne-server/src/main/java/org/apache/cayenne/dba/oracle/OracleAdapter.java
index 00ff222..f6528c9 100644
--- 
a/cayenne-server/src/main/java/org/apache/cayenne/dba/oracle/OracleAdapter.java
+++ 
b/cayenne-server/src/main/java/org/apache/cayenne/dba/oracle/OracleAdapter.java
@@ -31,6 +31,7 @@ import org.apache.cayenne.access.types.ExtendedType;
 import org.apache.cayenne.access.types.ExtendedTypeFactory;
 import org.apache.cayenne.access.types.ExtendedTypeMap;
 import org.apache.cayenne.access.types.ShortType;
+import org.apache.cayenne.access.types.ValueObjectTypeRegistry;
 import org.apache.cayenne.configuration.Constants;
 import org.apache.cayenne.configuration.RuntimeProperties;
 import org.apache.cayenne.dba.JdbcAdapter;
@@ -158,11 +159,12 @@ public class OracleAdapter extends JdbcAdapter {
        }
 
        public OracleAdapter(@Inject RuntimeProperties runtimeProperties,
-                       @Inject(Constants.SERVER_DEFAULT_TYPES_LIST) 
List<ExtendedType> defaultExtendedTypes,
-                       @Inject(Constants.SERVER_USER_TYPES_LIST) 
List<ExtendedType> userExtendedTypes,
-                       @Inject(Constants.SERVER_TYPE_FACTORIES_LIST) 
List<ExtendedTypeFactory> extendedTypeFactories,
-                       @Inject(Constants.SERVER_RESOURCE_LOCATOR) 
ResourceLocator resourceLocator) {
-               super(runtimeProperties, defaultExtendedTypes, 
userExtendedTypes, extendedTypeFactories, resourceLocator);
+                                                
@Inject(Constants.SERVER_DEFAULT_TYPES_LIST) List<ExtendedType> 
defaultExtendedTypes,
+                                                
@Inject(Constants.SERVER_USER_TYPES_LIST) List<ExtendedType> userExtendedTypes,
+                                                
@Inject(Constants.SERVER_TYPE_FACTORIES_LIST) List<ExtendedTypeFactory> 
extendedTypeFactories,
+                                                
@Inject(Constants.SERVER_RESOURCE_LOCATOR) ResourceLocator resourceLocator,
+                                                @Inject 
ValueObjectTypeRegistry valueObjectTypeRegistry) {
+               super(runtimeProperties, defaultExtendedTypes, 
userExtendedTypes, extendedTypeFactories, resourceLocator, 
valueObjectTypeRegistry);
 
                // enable batch updates by default
                setSupportsBatchUpdates(true);

http://git-wip-us.apache.org/repos/asf/cayenne/blob/5e9f0e0f/cayenne-server/src/main/java/org/apache/cayenne/dba/postgres/PostgresAdapter.java
----------------------------------------------------------------------
diff --git 
a/cayenne-server/src/main/java/org/apache/cayenne/dba/postgres/PostgresAdapter.java
 
b/cayenne-server/src/main/java/org/apache/cayenne/dba/postgres/PostgresAdapter.java
index b43eb7e..bf3f6b9 100644
--- 
a/cayenne-server/src/main/java/org/apache/cayenne/dba/postgres/PostgresAdapter.java
+++ 
b/cayenne-server/src/main/java/org/apache/cayenne/dba/postgres/PostgresAdapter.java
@@ -29,6 +29,7 @@ import org.apache.cayenne.access.types.CharType;
 import org.apache.cayenne.access.types.ExtendedType;
 import org.apache.cayenne.access.types.ExtendedTypeFactory;
 import org.apache.cayenne.access.types.ExtendedTypeMap;
+import org.apache.cayenne.access.types.ValueObjectTypeRegistry;
 import org.apache.cayenne.configuration.Constants;
 import org.apache.cayenne.configuration.RuntimeProperties;
 import org.apache.cayenne.dba.JdbcAdapter;
@@ -69,11 +70,12 @@ public class PostgresAdapter extends JdbcAdapter {
        public static final String BYTEA = "bytea";
 
        public PostgresAdapter(@Inject RuntimeProperties runtimeProperties,
-                       @Inject(Constants.SERVER_DEFAULT_TYPES_LIST) 
List<ExtendedType> defaultExtendedTypes,
-                       @Inject(Constants.SERVER_USER_TYPES_LIST) 
List<ExtendedType> userExtendedTypes,
-                       @Inject(Constants.SERVER_TYPE_FACTORIES_LIST) 
List<ExtendedTypeFactory> extendedTypeFactories,
-                       @Inject(Constants.SERVER_RESOURCE_LOCATOR) 
ResourceLocator resourceLocator) {
-               super(runtimeProperties, defaultExtendedTypes, 
userExtendedTypes, extendedTypeFactories, resourceLocator);
+                                                  
@Inject(Constants.SERVER_DEFAULT_TYPES_LIST) List<ExtendedType> 
defaultExtendedTypes,
+                                                  
@Inject(Constants.SERVER_USER_TYPES_LIST) List<ExtendedType> userExtendedTypes,
+                                                  
@Inject(Constants.SERVER_TYPE_FACTORIES_LIST) List<ExtendedTypeFactory> 
extendedTypeFactories,
+                                                  
@Inject(Constants.SERVER_RESOURCE_LOCATOR) ResourceLocator resourceLocator,
+                                                  @Inject 
ValueObjectTypeRegistry valueObjectTypeRegistry) {
+               super(runtimeProperties, defaultExtendedTypes, 
userExtendedTypes, extendedTypeFactories, resourceLocator, 
valueObjectTypeRegistry);
                setSupportsBatchUpdates(true);
                setSupportsGeneratedKeys(true);
        }

http://git-wip-us.apache.org/repos/asf/cayenne/blob/5e9f0e0f/cayenne-server/src/main/java/org/apache/cayenne/dba/sqlite/SQLiteAdapter.java
----------------------------------------------------------------------
diff --git 
a/cayenne-server/src/main/java/org/apache/cayenne/dba/sqlite/SQLiteAdapter.java 
b/cayenne-server/src/main/java/org/apache/cayenne/dba/sqlite/SQLiteAdapter.java
index 1796700..2542282 100644
--- 
a/cayenne-server/src/main/java/org/apache/cayenne/dba/sqlite/SQLiteAdapter.java
+++ 
b/cayenne-server/src/main/java/org/apache/cayenne/dba/sqlite/SQLiteAdapter.java
@@ -24,6 +24,7 @@ import 
org.apache.cayenne.access.translator.select.QueryAssembler;
 import org.apache.cayenne.access.types.ExtendedType;
 import org.apache.cayenne.access.types.ExtendedTypeFactory;
 import org.apache.cayenne.access.types.ExtendedTypeMap;
+import org.apache.cayenne.access.types.ValueObjectTypeRegistry;
 import org.apache.cayenne.configuration.Constants;
 import org.apache.cayenne.configuration.RuntimeProperties;
 import org.apache.cayenne.dba.JdbcAdapter;
@@ -61,13 +62,15 @@ public class SQLiteAdapter extends JdbcAdapter {
             @Inject(Constants.SERVER_DEFAULT_TYPES_LIST) List<ExtendedType> 
defaultExtendedTypes,
             @Inject(Constants.SERVER_USER_TYPES_LIST) List<ExtendedType> 
userExtendedTypes,
             @Inject(Constants.SERVER_TYPE_FACTORIES_LIST) 
List<ExtendedTypeFactory> extendedTypeFactories,
-            @Inject(Constants.SERVER_RESOURCE_LOCATOR) ResourceLocator 
resourceLocator) {
+            @Inject(Constants.SERVER_RESOURCE_LOCATOR) ResourceLocator 
resourceLocator,
+            @Inject ValueObjectTypeRegistry valueObjectTypeRegistry) {
         super(
                 runtimeProperties,
                 defaultExtendedTypes,
                 userExtendedTypes,
                 extendedTypeFactories,
-                resourceLocator);
+                resourceLocator,
+                valueObjectTypeRegistry);
         this.setSupportsUniqueConstraints(false);
         this.setSupportsGeneratedKeys(true);
     }

http://git-wip-us.apache.org/repos/asf/cayenne/blob/5e9f0e0f/cayenne-server/src/main/java/org/apache/cayenne/dba/sqlserver/SQLServerAdapter.java
----------------------------------------------------------------------
diff --git 
a/cayenne-server/src/main/java/org/apache/cayenne/dba/sqlserver/SQLServerAdapter.java
 
b/cayenne-server/src/main/java/org/apache/cayenne/dba/sqlserver/SQLServerAdapter.java
index 3faec45..1a32cf4 100644
--- 
a/cayenne-server/src/main/java/org/apache/cayenne/dba/sqlserver/SQLServerAdapter.java
+++ 
b/cayenne-server/src/main/java/org/apache/cayenne/dba/sqlserver/SQLServerAdapter.java
@@ -25,6 +25,7 @@ import 
org.apache.cayenne.access.translator.select.QueryAssembler;
 import org.apache.cayenne.access.translator.select.SelectTranslator;
 import org.apache.cayenne.access.types.ExtendedType;
 import org.apache.cayenne.access.types.ExtendedTypeFactory;
+import org.apache.cayenne.access.types.ValueObjectTypeRegistry;
 import org.apache.cayenne.configuration.Constants;
 import org.apache.cayenne.configuration.RuntimeProperties;
 import org.apache.cayenne.dba.sybase.SybaseAdapter;
@@ -80,11 +81,12 @@ public class SQLServerAdapter extends SybaseAdapter {
        public static final String TRIM_FUNCTION = "RTRIM";
 
        public SQLServerAdapter(@Inject RuntimeProperties runtimeProperties,
-                       @Inject(Constants.SERVER_DEFAULT_TYPES_LIST) 
List<ExtendedType> defaultExtendedTypes,
-                       @Inject(Constants.SERVER_USER_TYPES_LIST) 
List<ExtendedType> userExtendedTypes,
-                       @Inject(Constants.SERVER_TYPE_FACTORIES_LIST) 
List<ExtendedTypeFactory> extendedTypeFactories,
-                       @Inject(Constants.SERVER_RESOURCE_LOCATOR) 
ResourceLocator resourceLocator) {
-               super(runtimeProperties, defaultExtendedTypes, 
userExtendedTypes, extendedTypeFactories, resourceLocator);
+                                                       
@Inject(Constants.SERVER_DEFAULT_TYPES_LIST) List<ExtendedType> 
defaultExtendedTypes,
+                                                       
@Inject(Constants.SERVER_USER_TYPES_LIST) List<ExtendedType> userExtendedTypes,
+                                                       
@Inject(Constants.SERVER_TYPE_FACTORIES_LIST) List<ExtendedTypeFactory> 
extendedTypeFactories,
+                                                       
@Inject(Constants.SERVER_RESOURCE_LOCATOR) ResourceLocator resourceLocator,
+                                                       @Inject 
ValueObjectTypeRegistry valueObjectTypeRegistry) {
+               super(runtimeProperties, defaultExtendedTypes, 
userExtendedTypes, extendedTypeFactories, resourceLocator, 
valueObjectTypeRegistry);
 
                // TODO: i wonder if Sybase supports generated keys...
                // in this case we need to move this to the super.

http://git-wip-us.apache.org/repos/asf/cayenne/blob/5e9f0e0f/cayenne-server/src/main/java/org/apache/cayenne/dba/sybase/SybaseAdapter.java
----------------------------------------------------------------------
diff --git 
a/cayenne-server/src/main/java/org/apache/cayenne/dba/sybase/SybaseAdapter.java 
b/cayenne-server/src/main/java/org/apache/cayenne/dba/sybase/SybaseAdapter.java
index 28e948c..886308a 100644
--- 
a/cayenne-server/src/main/java/org/apache/cayenne/dba/sybase/SybaseAdapter.java
+++ 
b/cayenne-server/src/main/java/org/apache/cayenne/dba/sybase/SybaseAdapter.java
@@ -36,6 +36,7 @@ import org.apache.cayenne.access.types.ExtendedType;
 import org.apache.cayenne.access.types.ExtendedTypeFactory;
 import org.apache.cayenne.access.types.ExtendedTypeMap;
 import org.apache.cayenne.access.types.ShortType;
+import org.apache.cayenne.access.types.ValueObjectTypeRegistry;
 import org.apache.cayenne.configuration.Constants;
 import org.apache.cayenne.configuration.RuntimeProperties;
 import org.apache.cayenne.dba.DefaultQuotingStrategy;
@@ -54,11 +55,12 @@ import org.apache.cayenne.resource.ResourceLocator;
 public class SybaseAdapter extends JdbcAdapter {
 
     public SybaseAdapter(@Inject RuntimeProperties runtimeProperties,
-            @Inject(Constants.SERVER_DEFAULT_TYPES_LIST) List<ExtendedType> 
defaultExtendedTypes,
-            @Inject(Constants.SERVER_USER_TYPES_LIST) List<ExtendedType> 
userExtendedTypes,
-            @Inject(Constants.SERVER_TYPE_FACTORIES_LIST) 
List<ExtendedTypeFactory> extendedTypeFactories,
-            @Inject(Constants.SERVER_RESOURCE_LOCATOR) ResourceLocator 
resourceLocator) {
-        super(runtimeProperties, defaultExtendedTypes, userExtendedTypes, 
extendedTypeFactories, resourceLocator);
+                         @Inject(Constants.SERVER_DEFAULT_TYPES_LIST) 
List<ExtendedType> defaultExtendedTypes,
+                         @Inject(Constants.SERVER_USER_TYPES_LIST) 
List<ExtendedType> userExtendedTypes,
+                         @Inject(Constants.SERVER_TYPE_FACTORIES_LIST) 
List<ExtendedTypeFactory> extendedTypeFactories,
+                         @Inject(Constants.SERVER_RESOURCE_LOCATOR) 
ResourceLocator resourceLocator,
+                         @Inject ValueObjectTypeRegistry 
valueObjectTypeRegistry) {
+        super(runtimeProperties, defaultExtendedTypes, userExtendedTypes, 
extendedTypeFactories, resourceLocator, valueObjectTypeRegistry);
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/cayenne/blob/5e9f0e0f/cayenne-server/src/main/java/org/apache/cayenne/map/EntityResolver.java
----------------------------------------------------------------------
diff --git 
a/cayenne-server/src/main/java/org/apache/cayenne/map/EntityResolver.java 
b/cayenne-server/src/main/java/org/apache/cayenne/map/EntityResolver.java
index e7f93d8..2458361 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/map/EntityResolver.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/map/EntityResolver.java
@@ -21,6 +21,7 @@ package org.apache.cayenne.map;
 
 import org.apache.cayenne.ObjectId;
 import org.apache.cayenne.Persistent;
+import org.apache.cayenne.access.types.ValueObjectTypeRegistry;
 import org.apache.cayenne.query.Query;
 import org.apache.cayenne.reflect.ClassDescriptor;
 import org.apache.cayenne.reflect.ClassDescriptorMap;
@@ -69,6 +70,8 @@ public class EntityResolver implements MappingNamespace, 
Serializable {
     // callbacks are not serializable
     protected transient LifecycleCallbackRegistry callbackRegistry;
 
+    protected transient ValueObjectTypeRegistry valueObjectTypeRegistry;
+
     /**
      * Creates new empty EntityResolver.
      */
@@ -661,4 +664,12 @@ public class EntityResolver implements MappingNamespace, 
Serializable {
         in.defaultReadObject();
         refreshMappingCache();
     }
+
+    public ValueObjectTypeRegistry getValueObjectTypeRegistry() {
+        return valueObjectTypeRegistry;
+    }
+
+    public void setValueObjectTypeRegistry(ValueObjectTypeRegistry 
valueObjectTypeRegistry) {
+        this.valueObjectTypeRegistry = valueObjectTypeRegistry;
+    }
 }

http://git-wip-us.apache.org/repos/asf/cayenne/blob/5e9f0e0f/cayenne-server/src/main/java/org/apache/cayenne/query/SelectQueryMetadata.java
----------------------------------------------------------------------
diff --git 
a/cayenne-server/src/main/java/org/apache/cayenne/query/SelectQueryMetadata.java
 
b/cayenne-server/src/main/java/org/apache/cayenne/query/SelectQueryMetadata.java
index 85de9a8..1c5b04e 100644
--- 
a/cayenne-server/src/main/java/org/apache/cayenne/query/SelectQueryMetadata.java
+++ 
b/cayenne-server/src/main/java/org/apache/cayenne/query/SelectQueryMetadata.java
@@ -18,7 +18,6 @@
  ****************************************************************/
 package org.apache.cayenne.query;
 
-import java.io.IOException;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -28,10 +27,17 @@ import java.util.Map;
 import java.util.Set;
 
 import org.apache.cayenne.CayenneRuntimeException;
+import org.apache.cayenne.ObjectId;
+import org.apache.cayenne.Persistent;
+import org.apache.cayenne.access.types.ValueObjectType;
+import org.apache.cayenne.access.types.ValueObjectTypeRegistry;
 import org.apache.cayenne.exp.Expression;
 import org.apache.cayenne.exp.ExpressionFactory;
 import org.apache.cayenne.exp.Property;
+import org.apache.cayenne.exp.TraversalHandler;
 import org.apache.cayenne.exp.parser.ASTDbPath;
+import org.apache.cayenne.exp.parser.ASTFunctionCall;
+import org.apache.cayenne.exp.parser.ASTScalar;
 import org.apache.cayenne.map.DbAttribute;
 import org.apache.cayenne.map.DbEntity;
 import org.apache.cayenne.map.DbJoin;
@@ -67,14 +73,12 @@ class SelectQueryMetadata extends BaseQueryMetadata {
                this.pathSplitAliases = new 
HashMap<>(info.getPathSplitAliases());
        }
 
-       <T> boolean resolve(Object root, EntityResolver resolver, 
SelectQuery<T> query) {
+       boolean resolve(Object root, EntityResolver resolver, SelectQuery<?> 
query) {
 
                if (super.resolve(root, resolver, null)) {
-
                        // generate unique cache key, but only if we are 
caching..
-
                        if (cacheStrategy != null && cacheStrategy != 
QueryCacheStrategy.NO_CACHE) {
-                               this.cacheKey = makeCacheKey(query);
+                               this.cacheKey = makeCacheKey(query, resolver);
                        }
 
                        resolveAutoAliases(query);
@@ -87,12 +91,14 @@ class SelectQueryMetadata extends BaseQueryMetadata {
                return false;
        }
 
-       private String makeCacheKey(SelectQuery<?> query) {
+       private String makeCacheKey(SelectQuery<?> query, EntityResolver 
resolver) {
 
-               // create a unique key based on entity, qualifier, ordering and
-               // fetch offset and limit
+               // create a unique key based on entity or columns, qualifier, 
ordering, fetch offset and limit
 
                StringBuilder key = new StringBuilder();
+               // handler to create string out of expressions, created lazily
+               TraversalHandler traversalHandler = null;
+
                ObjEntity entity = getObjEntity();
                if (entity != null) {
                        key.append(entity.getName());
@@ -102,23 +108,19 @@ class SelectQueryMetadata extends BaseQueryMetadata {
 
                if(query.getColumns() != null && !query.getColumns().isEmpty()) 
{
                        key.append("/");
+                       traversalHandler = new 
ToCacheKeyTraversalHandler(resolver.getValueObjectTypeRegistry(), key);
                        for(Property<?> property : query.getColumns()) {
                                key.append("c:");
-                               try {
-                                       
property.getExpression().appendAsString(key);
-                               } catch (IOException e) {
-                                       throw new 
CayenneRuntimeException("Unexpected IO Exception appending to StringBuilder", 
e);
-                               }
+                               
property.getExpression().traverse(traversalHandler);
                        }
                }
 
                if (query.getQualifier() != null) {
                        key.append('/');
-                       try {
-                               query.getQualifier().appendAsString(key);
-                       } catch (IOException e) {
-                               throw new CayenneRuntimeException("Unexpected 
IO Exception appending to StringBuilder", e);
+                       if(traversalHandler == null) {
+                               traversalHandler = new 
ToCacheKeyTraversalHandler(resolver.getValueObjectTypeRegistry(), key);
                        }
+                       query.getQualifier().traverse(traversalHandler);
                }
 
                if (!query.getOrderings().isEmpty()) {
@@ -145,10 +147,9 @@ class SelectQueryMetadata extends BaseQueryMetadata {
                }
 
                return key.toString();
-
        }
 
-       private <T> void resolveAutoAliases(SelectQuery<T> query) {
+       private void resolveAutoAliases(SelectQuery<?> query) {
                Expression qualifier = query.getQualifier();
                if (qualifier != null) {
                        resolveAutoAliases(qualifier);
@@ -399,4 +400,79 @@ class SelectQueryMetadata extends BaseQueryMetadata {
        public void setSuppressingDistinct(boolean suppressingDistinct) {
                this.suppressingDistinct = suppressingDistinct;
        }
+
+       /**
+        * Expression traverse handler to create cache key string out of 
Expression.
+        * {@link Expression#appendAsString(Appendable)} where previously used 
for that,
+        * but it can't handle custom value objects properly (see CAY-2210).
+        *
+        * @see ValueObjectTypeRegistry
+        *
+        * @since 4.0
+        */
+       static class ToCacheKeyTraversalHandler implements TraversalHandler {
+
+               private ValueObjectTypeRegistry registry;
+               private StringBuilder out;
+
+               ToCacheKeyTraversalHandler(ValueObjectTypeRegistry registry, 
StringBuilder out) {
+                       this.registry = registry;
+                       this.out = out;
+               }
+
+               @Override
+               public void finishedChild(Expression node, int childIndex, 
boolean hasMoreChildren) {
+                       out.append(',');
+               }
+
+               @Override
+               public void startNode(Expression node, Expression parentNode) {
+                       if(node.getType() == Expression.FUNCTION_CALL) {
+                               
out.append(((ASTFunctionCall)node).getFunctionName()).append('(');
+                       } else {
+                               out.append(node.getType()).append('(');
+                       }
+               }
+
+               @Override
+               public void endNode(Expression node, Expression parentNode) {
+                       out.append(')');
+               }
+
+               @Override
+               public void objectNode(Object leaf, Expression parentNode) {
+                       if(leaf == null) {
+                               out.append("null");
+                               return;
+                       }
+
+                       if(leaf instanceof ASTScalar) {
+                               leaf = ((ASTScalar) leaf).getValue();
+                       } else if(leaf instanceof Object[]) {
+                               for(Object value : (Object[])leaf) {
+                                       objectNode(value, parentNode);
+                                       out.append(',');
+                               }
+                               return;
+                       }
+
+                       if (leaf instanceof Persistent) {
+                               ObjectId id = ((Persistent) leaf).getObjectId();
+                               Object encode = (id != null) ? id : leaf;
+                               out.append(encode);
+                       } else if (leaf instanceof Enum<?>) {
+                               Enum<?> e = (Enum<?>) leaf;
+                               
out.append("e:").append(leaf.getClass().getName()).append(':').append(e.ordinal());
+                       } else {
+                               ValueObjectType<Object, ?> valueObjectType;
+                               if (registry == null || (valueObjectType = 
registry.getValueType(leaf.getClass())) == null) {
+                                       // Registry will be null in 
cayenne-client context.
+                                       // Maybe we shouldn't create cache key 
at all in that case...
+                                       out.append(leaf);
+                               } else {
+                                       
out.append(valueObjectType.toCacheKey(leaf));
+                               }
+                       }
+               }
+       };
 }

http://git-wip-us.apache.org/repos/asf/cayenne/blob/5e9f0e0f/cayenne-server/src/test/java/org/apache/cayenne/access/types/DefaultValueObjectTypeRegistryTest.java
----------------------------------------------------------------------
diff --git 
a/cayenne-server/src/test/java/org/apache/cayenne/access/types/DefaultValueObjectTypeRegistryTest.java
 
b/cayenne-server/src/test/java/org/apache/cayenne/access/types/DefaultValueObjectTypeRegistryTest.java
new file mode 100644
index 0000000..28c7467
--- /dev/null
+++ 
b/cayenne-server/src/test/java/org/apache/cayenne/access/types/DefaultValueObjectTypeRegistryTest.java
@@ -0,0 +1,82 @@
+/*****************************************************************
+ *   Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ ****************************************************************/
+
+package org.apache.cayenne.access.types;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+/**
+ * @since 4.0
+ */
+public class DefaultValueObjectTypeRegistryTest {
+
+    DefaultValueObjectTypeRegistry registry;
+    ValueObjectType valueObjectType1, valueObjectType2;
+
+    @Before
+    public void setUpRegistry() {
+        valueObjectType1 = mock(ValueObjectType.class);
+        when(valueObjectType1.getValueType()).thenReturn(Integer.class);
+        when(valueObjectType1.getTargetType()).thenReturn(Integer.class);
+
+        valueObjectType2 = mock(ValueObjectType.class);
+        when(valueObjectType2.getValueType()).thenReturn(Number.class);
+        when(valueObjectType2.getTargetType()).thenReturn(Integer.class);
+
+        List<ValueObjectType<?, ?>> list = new ArrayList<>();
+        list.add(valueObjectType1);
+        list.add(valueObjectType2);
+
+        registry = new DefaultValueObjectTypeRegistry(list);
+    }
+
+    @Test
+    public void testInitialState() {
+        assertEquals(2, registry.typeCache.size());
+        assertTrue(registry.typeCache.containsKey(Integer.class.getName()));
+        assertTrue(registry.typeCache.containsKey(Number.class.getName()));
+        assertFalse(registry.typeCache.containsKey(String.class.getName()));
+        assertFalse(registry.typeCache.containsKey(Float.class.getName()));
+    }
+
+    @Test
+    public void getValueType() throws Exception {
+        ValueObjectType<?,?> valueObjectType = 
registry.getValueType(Integer.class);
+        assertSame(valueObjectType1, valueObjectType);
+
+        valueObjectType = registry.getValueType(Float.class);
+        assertSame(valueObjectType2, valueObjectType);
+
+        valueObjectType = registry.getValueType(String.class);
+        assertNull(valueObjectType);
+
+        assertEquals(4, registry.typeCache.size());
+        assertTrue(registry.typeCache.containsKey(String.class.getName()));
+        assertTrue(registry.typeCache.containsKey(Float.class.getName()));
+    }
+
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cayenne/blob/5e9f0e0f/cayenne-server/src/test/java/org/apache/cayenne/configuration/server/DataDomainProviderTest.java
----------------------------------------------------------------------
diff --git 
a/cayenne-server/src/test/java/org/apache/cayenne/configuration/server/DataDomainProviderTest.java
 
b/cayenne-server/src/test/java/org/apache/cayenne/configuration/server/DataDomainProviderTest.java
index af236eb..8a30ae8 100644
--- 
a/cayenne-server/src/test/java/org/apache/cayenne/configuration/server/DataDomainProviderTest.java
+++ 
b/cayenne-server/src/test/java/org/apache/cayenne/configuration/server/DataDomainProviderTest.java
@@ -36,6 +36,8 @@ import 
org.apache.cayenne.access.translator.batch.BatchTranslatorFactory;
 import 
org.apache.cayenne.access.translator.batch.DefaultBatchTranslatorFactory;
 import 
org.apache.cayenne.access.translator.select.DefaultSelectTranslatorFactory;
 import org.apache.cayenne.access.translator.select.SelectTranslatorFactory;
+import org.apache.cayenne.access.types.DefaultValueObjectTypeRegistry;
+import org.apache.cayenne.access.types.ValueObjectTypeRegistry;
 import org.apache.cayenne.annotation.PostLoad;
 import org.apache.cayenne.ashwood.AshwoodEntitySorter;
 import org.apache.cayenne.cache.QueryCache;
@@ -205,6 +207,9 @@ public class DataDomainProviderTest {
 
                 
binder.bind(EventBridge.class).toProvider(NoopEventBridgeProvider.class);
                 
binder.bind(DataRowStoreFactory.class).to(DefaultDataRowStoreFactory.class);
+
+                               ServerModule.contributeValueObjectTypes(binder);
+                               
binder.bind(ValueObjectTypeRegistry.class).to(DefaultValueObjectTypeRegistry.class);
                        }
                };
 

http://git-wip-us.apache.org/repos/asf/cayenne/blob/5e9f0e0f/cayenne-server/src/test/java/org/apache/cayenne/configuration/server/DefaultDbAdapterFactoryTest.java
----------------------------------------------------------------------
diff --git 
a/cayenne-server/src/test/java/org/apache/cayenne/configuration/server/DefaultDbAdapterFactoryTest.java
 
b/cayenne-server/src/test/java/org/apache/cayenne/configuration/server/DefaultDbAdapterFactoryTest.java
index d03e2e3..a4709c1 100644
--- 
a/cayenne-server/src/test/java/org/apache/cayenne/configuration/server/DefaultDbAdapterFactoryTest.java
+++ 
b/cayenne-server/src/test/java/org/apache/cayenne/configuration/server/DefaultDbAdapterFactoryTest.java
@@ -21,6 +21,8 @@ package org.apache.cayenne.configuration.server;
 import com.mockrunner.mock.jdbc.MockConnection;
 import com.mockrunner.mock.jdbc.MockDataSource;
 import org.apache.cayenne.access.translator.batch.BatchTranslatorFactory;
+import org.apache.cayenne.access.types.DefaultValueObjectTypeRegistry;
+import org.apache.cayenne.access.types.ValueObjectTypeRegistry;
 import org.apache.cayenne.configuration.Constants;
 import org.apache.cayenne.configuration.DataNodeDescriptor;
 import org.apache.cayenne.configuration.DefaultRuntimeProperties;
@@ -119,6 +121,9 @@ public class DefaultDbAdapterFactoryTest {
                 binder.bind(Key.get(ResourceLocator.class, 
Constants.SERVER_RESOURCE_LOCATOR)).to(ClassLoaderResourceLocator.class);
                 
binder.bind(RuntimeProperties.class).to(DefaultRuntimeProperties.class);
                 
binder.bind(BatchTranslatorFactory.class).toInstance(mock(BatchTranslatorFactory.class));
+
+                ServerModule.contributeValueObjectTypes(binder);
+                
binder.bind(ValueObjectTypeRegistry.class).to(DefaultValueObjectTypeRegistry.class);
             }
         };
 
@@ -156,6 +161,9 @@ public class DefaultDbAdapterFactoryTest {
                 binder.bind(Key.get(ResourceLocator.class, 
Constants.SERVER_RESOURCE_LOCATOR)).to(ClassLoaderResourceLocator.class);
                 
binder.bind(RuntimeProperties.class).to(DefaultRuntimeProperties.class);
                 
binder.bind(BatchTranslatorFactory.class).toInstance(mock(BatchTranslatorFactory.class));
+
+                ServerModule.contributeValueObjectTypes(binder);
+                
binder.bind(ValueObjectTypeRegistry.class).to(DefaultValueObjectTypeRegistry.class);
             }
         };
 

http://git-wip-us.apache.org/repos/asf/cayenne/blob/5e9f0e0f/cayenne-server/src/test/java/org/apache/cayenne/dba/PerAdapterProviderTest.java
----------------------------------------------------------------------
diff --git 
a/cayenne-server/src/test/java/org/apache/cayenne/dba/PerAdapterProviderTest.java
 
b/cayenne-server/src/test/java/org/apache/cayenne/dba/PerAdapterProviderTest.java
index fb2ebcb..505aefb 100644
--- 
a/cayenne-server/src/test/java/org/apache/cayenne/dba/PerAdapterProviderTest.java
+++ 
b/cayenne-server/src/test/java/org/apache/cayenne/dba/PerAdapterProviderTest.java
@@ -20,6 +20,7 @@ package org.apache.cayenne.dba;
 
 import org.apache.cayenne.access.types.ExtendedType;
 import org.apache.cayenne.access.types.ExtendedTypeFactory;
+import org.apache.cayenne.access.types.ValueObjectTypeRegistry;
 import org.apache.cayenne.configuration.RuntimeProperties;
 import org.apache.cayenne.dba.derby.DerbyAdapter;
 import org.apache.cayenne.dba.oracle.OracleAdapter;
@@ -49,18 +50,19 @@ public class PerAdapterProviderTest {
 
         ResourceLocator locator = new ClassLoaderResourceLocator(new 
DefaultClassLoaderManager());
         RuntimeProperties runtimeProperties = mock(RuntimeProperties.class);
+        ValueObjectTypeRegistry valueObjectTypeRegistry = 
mock(ValueObjectTypeRegistry.class);
 
         this.oracleAdapter = new OracleAdapter(runtimeProperties,
                 Collections.<ExtendedType>emptyList(),
                 Collections.<ExtendedType>emptyList(),
                 Collections.<ExtendedTypeFactory>emptyList(),
-                locator);
+                locator, valueObjectTypeRegistry);
 
         this.derbyAdapter = new DerbyAdapter(runtimeProperties,
                 Collections.<ExtendedType>emptyList(),
                 Collections.<ExtendedType>emptyList(),
                 Collections.<ExtendedTypeFactory>emptyList(),
-                locator);
+                locator, valueObjectTypeRegistry);
 
         this.autoDerbyAdapter = new AutoAdapter(new Provider<DbAdapter>() {
             @Override

http://git-wip-us.apache.org/repos/asf/cayenne/blob/5e9f0e0f/cayenne-server/src/test/java/org/apache/cayenne/query/SelectQueryCacheKeyIT.java
----------------------------------------------------------------------
diff --git 
a/cayenne-server/src/test/java/org/apache/cayenne/query/SelectQueryCacheKeyIT.java
 
b/cayenne-server/src/test/java/org/apache/cayenne/query/SelectQueryCacheKeyIT.java
index 6f6a4d1..701ca7b 100644
--- 
a/cayenne-server/src/test/java/org/apache/cayenne/query/SelectQueryCacheKeyIT.java
+++ 
b/cayenne-server/src/test/java/org/apache/cayenne/query/SelectQueryCacheKeyIT.java
@@ -30,6 +30,7 @@ import org.junit.Test;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 
@@ -170,7 +171,7 @@ public class SelectQueryCacheKeyIT extends ServerCase {
         assertNotNull(q1.getMetaData(resolver).getCacheKey());
         assertEquals(q1.getMetaData(resolver).getCacheKey(), 
q2.getMetaData(resolver).getCacheKey());
 
-        
assertFalse(q1.getMetaData(resolver).getCacheKey().equals(q3.getMetaData(resolver).getCacheKey()));
+        assertNotEquals(q1.getMetaData(resolver).getCacheKey(), 
q3.getMetaData(resolver).getCacheKey());
     }
 
     @Test
@@ -191,7 +192,7 @@ public class SelectQueryCacheKeyIT extends ServerCase {
         assertNotNull(q1.getMetaData(resolver).getCacheKey());
         assertEquals(q1.getMetaData(resolver).getCacheKey(), 
q2.getMetaData(resolver).getCacheKey());
 
-        
assertFalse(q1.getMetaData(resolver).getCacheKey().equals(q3.getMetaData(resolver).getCacheKey()));
+        assertNotEquals(q1.getMetaData(resolver).getCacheKey(), 
q3.getMetaData(resolver).getCacheKey());
     }
 
     @Test
@@ -215,7 +216,7 @@ public class SelectQueryCacheKeyIT extends ServerCase {
         assertNotNull(q1.getMetaData(resolver).getCacheKey());
         assertEquals(q1.getMetaData(resolver).getCacheKey(), 
q2.getMetaData(resolver).getCacheKey());
 
-        
assertFalse(q1.getMetaData(resolver).getCacheKey().equals(q3.getMetaData(resolver).getCacheKey()));
-        
assertFalse(q1.getMetaData(resolver).getCacheKey().equals(q4.getMetaData(resolver).getCacheKey()));
+        assertNotEquals(q1.getMetaData(resolver).getCacheKey(), 
q3.getMetaData(resolver).getCacheKey());
+        assertNotEquals(q1.getMetaData(resolver).getCacheKey(), 
q4.getMetaData(resolver).getCacheKey());
     }
 }

http://git-wip-us.apache.org/repos/asf/cayenne/blob/5e9f0e0f/cayenne-server/src/test/java/org/apache/cayenne/query/SelectQueryMetadataCacheKeyTest.java
----------------------------------------------------------------------
diff --git 
a/cayenne-server/src/test/java/org/apache/cayenne/query/SelectQueryMetadataCacheKeyTest.java
 
b/cayenne-server/src/test/java/org/apache/cayenne/query/SelectQueryMetadataCacheKeyTest.java
new file mode 100644
index 0000000..efd03c4
--- /dev/null
+++ 
b/cayenne-server/src/test/java/org/apache/cayenne/query/SelectQueryMetadataCacheKeyTest.java
@@ -0,0 +1,262 @@
+/*****************************************************************
+ *   Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ ****************************************************************/
+
+package org.apache.cayenne.query;
+
+import org.apache.cayenne.ObjectId;
+import org.apache.cayenne.Persistent;
+import org.apache.cayenne.access.types.ValueObjectType;
+import org.apache.cayenne.access.types.ValueObjectTypeRegistry;
+import org.apache.cayenne.exp.ExpressionFactory;
+import org.apache.cayenne.exp.TraversalHandler;
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+/**
+ * This class is testing converting Expressions to cache key part.
+ *
+ * @since 4.0
+ */
+public class SelectQueryMetadataCacheKeyTest {
+
+    private ValueObjectTypeRegistry registry;
+    private StringBuilder cacheKey;
+
+    @SuppressWarnings("unchecked")
+    @Before
+    public void createObjects() {
+        registry = mock(ValueObjectTypeRegistry.class);
+
+        // mock value type for Double class
+        ValueObjectType mockType = mock(ValueObjectType.class);
+        when(mockType.getValueType()).thenReturn(Double.class);
+        when(mockType.toCacheKey(any())).thenReturn("<value placeholder>");
+        when(registry.getValueType(eq(Double.class))).thenReturn(mockType);
+
+        // value type for TestValue class
+        ValueObjectType testType = new TestValueType();
+        when(registry.getValueType(eq(TestValue.class))).thenReturn(testType);
+    }
+
+    /**
+     * Simple expressions
+     */
+    @Test
+    public void cacheKeySimple() {
+        ExpressionFactory.exp("field = 1").traverse(newHandler());
+        String s1 = cacheKey.toString();
+
+        ExpressionFactory.exp("field = 1").traverse(newHandler());
+        String s2 = cacheKey.toString();
+
+        ExpressionFactory.exp("field = 2").traverse(newHandler());
+        String s3 = cacheKey.toString();
+
+        assertEquals(s1, s2);
+        assertNotEquals(s2, s3);
+    }
+
+    /**
+     * Expressions with list of simple values
+     */
+    @Test
+    public void cacheKeyWithList() {
+        ExpressionFactory.exp("field in (1,2,3)").traverse(newHandler());
+        String s1 = cacheKey.toString();
+
+        ExpressionFactory.exp("field in (1,2,3)").traverse(newHandler());
+        String s2 = cacheKey.toString();
+
+        ExpressionFactory.exp("field in (2,3,4)").traverse(newHandler());
+        String s3 = cacheKey.toString();
+
+        assertEquals(s1, s2);
+        assertNotEquals(s2, s3);
+    }
+
+    /**
+     * Simple test for custom value object, Double.class is marked as a custom 
value object.
+     */
+    @Test
+    public void cacheKeyWithValueObjectSimple() {
+        ExpressionFactory.exp("field = 1.0").traverse(newHandler());
+        String s1 = cacheKey.toString();
+
+        assertTrue(s1.contains("<value placeholder>"));
+    }
+
+    /**
+     * List of value objects, Double.class is marked as a custom value object.
+     */
+    @Test
+    public void cacheKeyWithValueObjectList() {
+        ExpressionFactory.exp("field in (1.0,2.0,3.0)").traverse(newHandler());
+        String s1 = cacheKey.toString();
+
+        assertTrue(s1.contains("<value placeholder>"));
+    }
+
+    @Test
+    public void cacheKeyWithEnumValue() {
+        ExpressionFactory.greaterOrEqualExp("testPath", 
TestEnum.VALUE_1).traverse(newHandler());
+        String s1 = cacheKey.toString();
+
+        ExpressionFactory.greaterOrEqualExp("testPath", 
TestEnum.VALUE_1).traverse(newHandler());
+        String s2 = cacheKey.toString();
+
+        ExpressionFactory.greaterOrEqualExp("testPath", 
TestEnum.VALUE_2).traverse(newHandler());
+        String s3 = cacheKey.toString();
+
+        assertEquals(s1, s2);
+        assertNotEquals(s2, s3);
+    }
+
+    @Test
+    public void cacheKeyWithValueObject() {
+        ExpressionFactory.greaterOrEqualExp("testPath", new 
TestValue(1)).traverse(newHandler());
+        String s1 = cacheKey.toString();
+
+        ExpressionFactory.greaterOrEqualExp("testPath", new 
TestValue(1)).traverse(newHandler());
+        String s2 = cacheKey.toString();
+
+        ExpressionFactory.greaterOrEqualExp("testPath", new 
TestValue(2)).traverse(newHandler());
+        String s3 = cacheKey.toString();
+
+        assertEquals(s1, s2);
+        assertNotEquals(s2, s3);
+    }
+
+    /**
+     * Persistent objects should be converted to their ObjectIds.
+     */
+    @Test
+    public void cacheKeyWithPersistentObject() {
+        Persistent persistent1 = mock(Persistent.class);
+        ObjectId objectId1 = mock(ObjectId.class);
+        when(objectId1.toString()).thenReturn("objId1");
+        when(persistent1.getObjectId()).thenReturn(objectId1);
+
+        Persistent persistent2 = mock(Persistent.class);
+        ObjectId objectId2 = mock(ObjectId.class);
+        when(objectId2.toString()).thenReturn("objId2");
+        when(persistent2.getObjectId()).thenReturn(objectId2);
+
+        ExpressionFactory.greaterOrEqualExp("testPath", 
persistent1).traverse(newHandler());
+        String s1 = cacheKey.toString();
+
+        ExpressionFactory.greaterOrEqualExp("testPath", 
persistent1).traverse(newHandler());
+        String s2 = cacheKey.toString();
+
+        ExpressionFactory.greaterOrEqualExp("testPath", 
persistent2).traverse(newHandler());
+        String s3 = cacheKey.toString();
+
+        assertTrue(s1.contains("objId1"));
+        assertTrue(s3.contains("objId2"));
+        assertEquals(s1, s2);
+        assertNotEquals(s2, s3);
+    }
+
+    @Test
+    public void cacheKeyWithFunctionCall() {
+        ExpressionFactory.exp("length(testPath)").traverse(newHandler());
+        String s1 = cacheKey.toString();
+
+        ExpressionFactory.exp("length(testPath)").traverse(newHandler());
+        String s2 = cacheKey.toString();
+
+        ExpressionFactory.exp("count(testPath)").traverse(newHandler());
+        String s3 = cacheKey.toString();
+
+        assertEquals(s1, s2);
+        assertNotEquals(s2, s3);
+
+        ExpressionFactory.exp("substring(path, 
testPath)").traverse(newHandler());
+        String s4 = cacheKey.toString();
+
+        ExpressionFactory.exp("substring(path2, 
testPath)").traverse(newHandler());
+        String s5 = cacheKey.toString();
+
+        assertNotEquals(s4, s5);
+
+        ExpressionFactory.exp("year(path)").traverse(newHandler());
+        String s6 = cacheKey.toString();
+
+        ExpressionFactory.exp("hour(path)").traverse(newHandler());
+        String s7 = cacheKey.toString();
+
+        assertNotEquals(s6, s7);
+    }
+
+    private TraversalHandler newHandler() {
+        return new SelectQueryMetadata.ToCacheKeyTraversalHandler(registry, 
cacheKey = new StringBuilder());
+    }
+
+    /* ************* Test types *************** */
+
+    /**
+     * Test enum
+     */
+    enum TestEnum { VALUE_1, VALUE_2 }
+
+    /**
+     * Test value object
+     */
+    static class TestValue {
+        int v = 0;
+        TestValue(int v) {
+            this.v = v;
+        }
+    }
+
+    /**
+     * Test value object descriptor, we need only toCacheKey() method
+     */
+    static class TestValueType implements ValueObjectType<TestValue, Integer> {
+        @Override
+        public Class<Integer> getTargetType() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public Class<TestValue> getValueType() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public TestValue toJavaObject(Integer value) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public Integer fromJavaObject(TestValue object) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public String toCacheKey(TestValue object) {
+            return Integer.toString(object.v);
+        }
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cayenne/blob/5e9f0e0f/cayenne-server/src/test/java/org/apache/cayenne/unit/di/server/ServerCaseModule.java
----------------------------------------------------------------------
diff --git 
a/cayenne-server/src/test/java/org/apache/cayenne/unit/di/server/ServerCaseModule.java
 
b/cayenne-server/src/test/java/org/apache/cayenne/unit/di/server/ServerCaseModule.java
index 553807f..7e1f2dd 100644
--- 
a/cayenne-server/src/test/java/org/apache/cayenne/unit/di/server/ServerCaseModule.java
+++ 
b/cayenne-server/src/test/java/org/apache/cayenne/unit/di/server/ServerCaseModule.java
@@ -25,13 +25,14 @@ import 
org.apache.cayenne.access.DefaultObjectMapRetainStrategy;
 import org.apache.cayenne.access.ObjectMapRetainStrategy;
 import org.apache.cayenne.access.translator.batch.BatchTranslatorFactory;
 import org.apache.cayenne.access.types.BigDecimalType;
-import org.apache.cayenne.access.types.BigIntegerType;
+import org.apache.cayenne.access.types.BigIntegerValueType;
 import org.apache.cayenne.access.types.BooleanType;
 import org.apache.cayenne.access.types.ByteArrayType;
 import org.apache.cayenne.access.types.ByteType;
 import org.apache.cayenne.access.types.CalendarType;
 import org.apache.cayenne.access.types.CharType;
 import org.apache.cayenne.access.types.DateType;
+import org.apache.cayenne.access.types.DefaultValueObjectTypeRegistry;
 import org.apache.cayenne.access.types.DoubleType;
 import org.apache.cayenne.access.types.FloatType;
 import org.apache.cayenne.access.types.IntegerType;
@@ -39,8 +40,9 @@ import org.apache.cayenne.access.types.LongType;
 import org.apache.cayenne.access.types.ShortType;
 import org.apache.cayenne.access.types.TimeType;
 import org.apache.cayenne.access.types.TimestampType;
-import org.apache.cayenne.access.types.UUIDType;
+import org.apache.cayenne.access.types.UUIDValueType;
 import org.apache.cayenne.access.types.UtilDateType;
+import org.apache.cayenne.access.types.ValueObjectTypeRegistry;
 import org.apache.cayenne.access.types.VoidType;
 import org.apache.cayenne.configuration.ConfigurationNameMapper;
 import org.apache.cayenne.configuration.Constants;
@@ -124,44 +126,28 @@ public class ServerCaseModule implements Module {
         // unit test injector. ServerRuntime injector contents are customized
         // inside ServerRuntimeProvider.
 
-        binder.bindMap(String.class, 
UnitDbAdapterProvider.TEST_ADAPTERS_MAP).put(
-                FirebirdAdapter.class.getName(),
-                FirebirdUnitDbAdapter.class.getName()).put(
-                OracleAdapter.class.getName(),
-                OracleUnitDbAdapter.class.getName()).put(
-                DerbyAdapter.class.getName(),
-                DerbyUnitDbAdapter.class.getName()).put(
-                Oracle8Adapter.class.getName(),
-                OracleUnitDbAdapter.class.getName()).put(
-                SybaseAdapter.class.getName(),
-                SybaseUnitDbAdapter.class.getName()).put(
-                MySQLAdapter.class.getName(),
-                MySQLUnitDbAdapter.class.getName()).put(
-                PostgresAdapter.class.getName(),
-                PostgresUnitDbAdapter.class.getName()).put(
-                OpenBaseAdapter.class.getName(),
-                OpenBaseUnitDbAdapter.class.getName()).put(
-                SQLServerAdapter.class.getName(),
-                SQLServerUnitDbAdapter.class.getName()).put(
-                DB2Adapter.class.getName(),
-                DB2UnitDbAdapter.class.getName()).put(
-                HSQLDBAdapter.class.getName(),
-                HSQLDBUnitDbAdapter.class.getName()).put(
-                H2Adapter.class.getName(),
-                H2UnitDbAdapter.class.getName()).put(
-                FrontBaseAdapter.class.getName(),
-                FrontBaseUnitDbAdapter.class.getName()).put(
-                IngresAdapter.class.getName(),
-                IngresUnitDbAdapter.class.getName()).put(
-                SQLiteAdapter.class.getName(),
-                SQLiteUnitDbAdapter.class.getName());
+        binder.bindMap(String.class, UnitDbAdapterProvider.TEST_ADAPTERS_MAP)
+                .put(FirebirdAdapter.class.getName(), 
FirebirdUnitDbAdapter.class.getName())
+                .put(OracleAdapter.class.getName(), 
OracleUnitDbAdapter.class.getName())
+                .put(DerbyAdapter.class.getName(), 
DerbyUnitDbAdapter.class.getName())
+                .put(Oracle8Adapter.class.getName(), 
OracleUnitDbAdapter.class.getName())
+                .put(SybaseAdapter.class.getName(), 
SybaseUnitDbAdapter.class.getName())
+                .put(MySQLAdapter.class.getName(), 
MySQLUnitDbAdapter.class.getName())
+                .put(PostgresAdapter.class.getName(), 
PostgresUnitDbAdapter.class.getName())
+                .put(OpenBaseAdapter.class.getName(), 
OpenBaseUnitDbAdapter.class.getName())
+                .put(SQLServerAdapter.class.getName(), 
SQLServerUnitDbAdapter.class.getName())
+                .put(DB2Adapter.class.getName(), 
DB2UnitDbAdapter.class.getName())
+                .put(HSQLDBAdapter.class.getName(), 
HSQLDBUnitDbAdapter.class.getName())
+                .put(H2Adapter.class.getName(), 
H2UnitDbAdapter.class.getName())
+                .put(FrontBaseAdapter.class.getName(), 
FrontBaseUnitDbAdapter.class.getName())
+                .put(IngresAdapter.class.getName(), 
IngresUnitDbAdapter.class.getName())
+                .put(SQLiteAdapter.class.getName(), 
SQLiteUnitDbAdapter.class.getName());
         ServerModule.contributeProperties(binder);
         
         // configure extended types
         ServerModule.contributeDefaultTypes(binder)
                 .add(new VoidType())
                 .add(new BigDecimalType())
-                .add(new BigIntegerType())
                 .add(new BooleanType())
                 .add(new ByteArrayType(false, true))
                 .add(new ByteType(false))
@@ -175,24 +161,24 @@ public class ServerCaseModule implements Module {
                 .add(new TimeType())
                 .add(new TimestampType())
                 .add(new UtilDateType())
-                .add(new 
CalendarType<GregorianCalendar>(GregorianCalendar.class))
-                .add(new CalendarType<Calendar>(Calendar.class))
-                .add(new UUIDType());
+                .add(new CalendarType<>(GregorianCalendar.class))
+                .add(new CalendarType<>(Calendar.class));
         ServerModule.contributeUserTypes(binder);
         ServerModule.contributeTypeFactories(binder);
+        ServerModule.contributeValueObjectTypes(binder)
+                .add(BigIntegerValueType.class)
+                .add(UUIDValueType.class);
+        
binder.bind(ValueObjectTypeRegistry.class).to(DefaultValueObjectTypeRegistry.class);
 
         binder.bind(SchemaBuilder.class).to(SchemaBuilder.class);
         binder.bind(JdbcEventLogger.class).to(CommonsJdbcEventLogger.class);
         
binder.bind(RuntimeProperties.class).to(DefaultRuntimeProperties.class);
-        binder.bind(ObjectMapRetainStrategy.class).to(
-                DefaultObjectMapRetainStrategy.class);
+        
binder.bind(ObjectMapRetainStrategy.class).to(DefaultObjectMapRetainStrategy.class);
 
         // singleton objects
-        binder.bind(UnitTestLifecycleManager.class).toInstance(
-                new ServerCaseLifecycleManager(testScope));
+        binder.bind(UnitTestLifecycleManager.class).toInstance(new 
ServerCaseLifecycleManager(testScope));
 
-        binder.bind(DataSourceInfo.class).toProvider(
-                ServerCaseDataSourceInfoProvider.class);
+        
binder.bind(DataSourceInfo.class).toProvider(ServerCaseDataSourceInfoProvider.class);
         
binder.bind(DataSourceFactory.class).to(ServerCaseSharedDataSourceFactory.class);
         
binder.bind(DbAdapter.class).toProvider(ServerCaseDbAdapterProvider.class);
         
binder.bind(JdbcAdapter.class).toProvider(ServerCaseDbAdapterProvider.class);
@@ -201,14 +187,10 @@ public class ServerCaseModule implements Module {
         // this factory is a hack that allows to inject to DbAdapters loaded 
outside of
         // server runtime... BatchQueryBuilderFactory is hardcoded and 
whatever is placed
         // in the ServerModule is ignored
-        binder.bind(BatchTranslatorFactory.class).toProvider(
-                ServerCaseBatchQueryBuilderFactoryProvider.class);
-        binder.bind(DataChannelInterceptor.class).to(
-                ServerCaseDataChannelInterceptor.class);
-        binder.bind(SQLTemplateCustomizer.class).toProvider(
-                SQLTemplateCustomizerProvider.class);
-        binder.bind(ServerCaseDataSourceFactory.class).to(
-                ServerCaseDataSourceFactory.class);
+        
binder.bind(BatchTranslatorFactory.class).toProvider(ServerCaseBatchQueryBuilderFactoryProvider.class);
+        
binder.bind(DataChannelInterceptor.class).to(ServerCaseDataChannelInterceptor.class);
+        
binder.bind(SQLTemplateCustomizer.class).toProvider(SQLTemplateCustomizerProvider.class);
+        
binder.bind(ServerCaseDataSourceFactory.class).to(ServerCaseDataSourceFactory.class);
         
binder.bind(ClassLoaderManager.class).to(DefaultClassLoaderManager.class);
         
binder.bind(AdhocObjectFactory.class).to(DefaultAdhocObjectFactory.class);
         
binder.bind(ResourceLocator.class).to(ClassLoaderResourceLocator.class);
@@ -218,26 +200,13 @@ public class ServerCaseModule implements Module {
         
binder.bind(ConfigurationNameMapper.class).to(DefaultConfigurationNameMapper.class);
 
         // test-scoped objects
-        binder.bind(EntityResolver.class).toProvider(
-                ServerCaseEntityResolverProvider.class).in(testScope);
-        
binder.bind(DataNode.class).toProvider(ServerCaseDataNodeProvider.class).in(
-                testScope);
-        
binder.bind(ServerCaseProperties.class).to(ServerCaseProperties.class).in(
-                testScope);
-        
binder.bind(ServerRuntime.class).toProvider(ServerRuntimeProvider.class).in(
-                testScope);
-        binder
-                .bind(ObjectContext.class)
-                .toProvider(ServerCaseObjectContextProvider.class)
-                .withoutScope();
-        binder
-                .bind(DataContext.class)
-                .toProvider(ServerCaseDataContextProvider.class)
-                .withoutScope();
-
-        
binder.bind(DBHelper.class).toProvider(FlavoredDBHelperProvider.class).in(
-                testScope);
-        binder.bind(DBCleaner.class).toProvider(DBCleanerProvider.class).in(
-                testScope);
+        
binder.bind(EntityResolver.class).toProvider(ServerCaseEntityResolverProvider.class).in(testScope);
+        
binder.bind(DataNode.class).toProvider(ServerCaseDataNodeProvider.class).in(testScope);
+        
binder.bind(ServerCaseProperties.class).to(ServerCaseProperties.class).in(testScope);
+        
binder.bind(ServerRuntime.class).toProvider(ServerRuntimeProvider.class).in(testScope);
+        
binder.bind(ObjectContext.class).toProvider(ServerCaseObjectContextProvider.class).withoutScope();
+        
binder.bind(DataContext.class).toProvider(ServerCaseDataContextProvider.class).withoutScope();
+        
binder.bind(DBHelper.class).toProvider(FlavoredDBHelperProvider.class).in(testScope);
+        
binder.bind(DBCleaner.class).toProvider(DBCleanerProvider.class).in(testScope);
     }
 }

http://git-wip-us.apache.org/repos/asf/cayenne/blob/5e9f0e0f/docs/doc/src/main/resources/RELEASE-NOTES.txt
----------------------------------------------------------------------
diff --git a/docs/doc/src/main/resources/RELEASE-NOTES.txt 
b/docs/doc/src/main/resources/RELEASE-NOTES.txt
index b07f2d0..20f6164 100644
--- a/docs/doc/src/main/resources/RELEASE-NOTES.txt
+++ b/docs/doc/src/main/resources/RELEASE-NOTES.txt
@@ -15,6 +15,7 @@ Changes/New Features:
 
 CAY-1873 Move DataDomain cache configuration from the Modeler and into DI
 CAY-2109 cayenne-crypto: add value authentication (HMAC)
+CAY-2210 Query cache: incorrect cache key for queries with custom value objects
 CAY-2255 ObjectSelect improvement: columns as full entities
 CAY-2258 DI: type-safe binding of List and Map
 CAY-2266 Move EventBridge implementations into autoloadable modules

Reply via email to