Repository: cayenne
Updated Branches:
  refs/heads/master 30c2c6f12 -> fa605bf30


CAY-2103 cayenne-crypto: support for mapping non-String and non-binary types


Project: http://git-wip-us.apache.org/repos/asf/cayenne/repo
Commit: http://git-wip-us.apache.org/repos/asf/cayenne/commit/fa605bf3
Tree: http://git-wip-us.apache.org/repos/asf/cayenne/tree/fa605bf3
Diff: http://git-wip-us.apache.org/repos/asf/cayenne/diff/fa605bf3

Branch: refs/heads/master
Commit: fa605bf300f95f916e6339c3be3b0f514dca9341
Parents: 30c2c6f
Author: Andrus Adamchik <and...@objectstyle.com>
Authored: Thu Aug 18 12:25:04 2016 +0300
Committer: Andrus Adamchik <and...@objectstyle.com>
Committed: Thu Aug 18 15:44:04 2016 +0300

----------------------------------------------------------------------
 .../reader/CryptoRowReaderFactoryDecorator.java |  60 +++-
 .../value/Base64StringConverter.java            |   2 +-
 .../transformer/value/BooleanConverter.java     |  35 +++
 .../crypto/transformer/value/ByteConverter.java |  56 ++++
 .../transformer/value/BytesConverter.java       |   2 +-
 .../value/DefaultValueTransformerFactory.java   | 313 ++++++++++---------
 .../transformer/value/IntegerConverter.java     |  71 +++++
 .../crypto/transformer/value/LongConverter.java |  58 ++++
 .../transformer/value/ShortConverter.java       |  68 ++++
 .../transformer/value/Utf8StringConverter.java  |   4 +-
 .../transformer/value/UtilDateConverter.java    |  30 ++
 .../cayenne/crypto/Runtime_AES128_Base.java     |   9 +-
 .../cayenne/crypto/Runtime_AES128_IT.java       |  55 +++-
 .../apache/cayenne/crypto/db/auto/_Table1.java  |  18 ++
 .../transformer/value/BooleanConverterTest.java |  28 ++
 .../transformer/value/ByteConverterTest.java    |  45 +++
 .../transformer/value/IntegerConverterTest.java |  61 ++++
 .../transformer/value/LongConverterTest.java    |  49 +++
 .../transformer/value/ShortConverterTest.java   |  48 +++
 .../value/UtilDateConverterTest.java            |  31 ++
 .../cayenne/crypto/unit/CryptoUnitUtils.java    |  35 ++-
 .../src/test/resources/datamap.map.xml          |   4 +
 .../org/apache/cayenne/dba/TypesMapping.java    |  53 +++-
 docs/doc/src/main/resources/RELEASE-NOTES.txt   |   1 +
 24 files changed, 951 insertions(+), 185 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cayenne/blob/fa605bf3/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/reader/CryptoRowReaderFactoryDecorator.java
----------------------------------------------------------------------
diff --git 
a/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/reader/CryptoRowReaderFactoryDecorator.java
 
b/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/reader/CryptoRowReaderFactoryDecorator.java
index fc1f418..ab2c08d 100644
--- 
a/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/reader/CryptoRowReaderFactoryDecorator.java
+++ 
b/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/reader/CryptoRowReaderFactoryDecorator.java
@@ -18,36 +18,47 @@
  ****************************************************************/
 package org.apache.cayenne.crypto.reader;
 
-import java.sql.ResultSet;
-import java.util.Map;
-
 import org.apache.cayenne.access.jdbc.ColumnDescriptor;
 import org.apache.cayenne.access.jdbc.RowDescriptor;
 import org.apache.cayenne.access.jdbc.reader.RowReader;
 import org.apache.cayenne.access.jdbc.reader.RowReaderFactory;
-import org.apache.cayenne.crypto.transformer.TransformerFactory;
+import org.apache.cayenne.access.types.ExtendedType;
+import org.apache.cayenne.access.types.ExtendedTypeMap;
+import org.apache.cayenne.crypto.map.ColumnMapper;
 import org.apache.cayenne.crypto.transformer.MapTransformer;
+import org.apache.cayenne.crypto.transformer.TransformerFactory;
 import org.apache.cayenne.dba.DbAdapter;
+import org.apache.cayenne.dba.TypesMapping;
 import org.apache.cayenne.di.Inject;
+import org.apache.cayenne.map.DbAttribute;
 import org.apache.cayenne.map.ObjAttribute;
 import org.apache.cayenne.query.QueryMetadata;
 
+import java.sql.ResultSet;
+import java.util.Map;
+
 public class CryptoRowReaderFactoryDecorator implements RowReaderFactory {
 
     private RowReaderFactory delegate;
     private TransformerFactory transformerFactory;
+    private ColumnMapper columnMapper;
 
     public CryptoRowReaderFactoryDecorator(@Inject RowReaderFactory delegate,
-            @Inject TransformerFactory transformerFactory) {
+                                           @Inject TransformerFactory 
transformerFactory,
+                                           @Inject ColumnMapper columnMapper) {
         this.delegate = delegate;
         this.transformerFactory = transformerFactory;
+        this.columnMapper = columnMapper;
     }
 
     @Override
     public RowReader<?> rowReader(final RowDescriptor descriptor, 
QueryMetadata queryMetadata, DbAdapter adapter,
-            Map<ObjAttribute, ColumnDescriptor> attributeOverrides) {
+                                  Map<ObjAttribute, ColumnDescriptor> 
attributeOverrides) {
 
-        final RowReader<?> delegateReader = delegate.rowReader(descriptor, 
queryMetadata, adapter, attributeOverrides);
+        final RowReader<?> delegateReader = 
delegate.rowReader(encryptedRowDescriptor(descriptor, 
adapter.getExtendedTypes()),
+                queryMetadata,
+                adapter,
+                attributeOverrides);
 
         return new RowReader<Object>() {
 
@@ -69,7 +80,7 @@ public class CryptoRowReaderFactoryDecorator implements 
RowReaderFactory {
 
                 if (decryptor != null) {
 
-                    @SuppressWarnings({ "unchecked", "rawtypes" })
+                    @SuppressWarnings({"unchecked", "rawtypes"})
                     Map<String, Object> map = (Map) row;
 
                     decryptor.transform(map);
@@ -79,4 +90,37 @@ public class CryptoRowReaderFactoryDecorator implements 
RowReaderFactory {
             }
         };
     }
+
+    protected RowDescriptor encryptedRowDescriptor(RowDescriptor descriptor, 
ExtendedTypeMap typeMap) {
+
+        // need to tweak the original descriptor to ensure binary columns are 
read as binary, eben if the plain Java
+        // type is not a byte[]
+
+        ColumnDescriptor[] originalColumns = descriptor.getColumns();
+        int len = originalColumns.length;
+
+        ExtendedType[] originalConverters = descriptor.getConverters();
+        ExtendedType[] encryptedConverters = new ExtendedType[len];
+
+        for (int i = 0; i < len; i++) {
+            DbAttribute attribute = originalColumns[i].getAttribute();
+
+            ExtendedType t = originalConverters[i];
+
+            if (attribute != null && columnMapper.isEncrypted(attribute)) {
+
+                // only char or binary columns can store encrypted data
+                if (TypesMapping.isBinary(attribute.getType())) {
+                    t = typeMap.getRegisteredType(byte[].class);
+                } else if (TypesMapping.isCharacter(attribute.getType())) {
+                    t = typeMap.getRegisteredType(String.class);
+                }
+                // else - warning?
+            }
+
+            encryptedConverters[i] = t;
+        }
+
+        return new RowDescriptor(originalColumns, encryptedConverters);
+    }
 }

http://git-wip-us.apache.org/repos/asf/cayenne/blob/fa605bf3/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/value/Base64StringConverter.java
----------------------------------------------------------------------
diff --git 
a/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/value/Base64StringConverter.java
 
b/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/value/Base64StringConverter.java
index 3d36a3a..2f80d6e 100644
--- 
a/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/value/Base64StringConverter.java
+++ 
b/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/value/Base64StringConverter.java
@@ -23,7 +23,7 @@ import javax.xml.bind.DatatypeConverter;
 /**
  * @since 4.0
  */
-class Base64StringConverter implements BytesConverter {
+public class Base64StringConverter implements BytesConverter {
 
     static final BytesConverter INSTANCE = new Base64StringConverter();
 

http://git-wip-us.apache.org/repos/asf/cayenne/blob/fa605bf3/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/value/BooleanConverter.java
----------------------------------------------------------------------
diff --git 
a/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/value/BooleanConverter.java
 
b/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/value/BooleanConverter.java
new file mode 100644
index 0000000..cf7636f
--- /dev/null
+++ 
b/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/value/BooleanConverter.java
@@ -0,0 +1,35 @@
+package org.apache.cayenne.crypto.transformer.value;
+
+/**
+ * Converts between java.util.Date and byte[], based on the long timestamp 
encoding.
+ *
+ * @since 4.0
+ */
+public class BooleanConverter implements BytesConverter {
+
+    static final BytesConverter INSTANCE = new BooleanConverter();
+
+    @Override
+    public Object fromBytes(byte[] bytes) {
+
+        if (bytes.length != 1) {
+            throw new IllegalArgumentException("Unexpected number of bytes for 
boolean: " + bytes.length);
+        }
+
+        byte b = bytes[0];
+        if (b == 0) {
+            return Boolean.FALSE;
+        } else if (b == 1) {
+            return Boolean.TRUE;
+        } else {
+            throw new IllegalArgumentException("Unexpected byte value for 
boolean: " + b);
+        }
+    }
+
+    @Override
+    public byte[] toBytes(Object value) {
+        return new byte[]{
+                ((Boolean) value) ? (byte) 1 : 0
+        };
+    }
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/fa605bf3/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/value/ByteConverter.java
----------------------------------------------------------------------
diff --git 
a/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/value/ByteConverter.java
 
b/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/value/ByteConverter.java
new file mode 100644
index 0000000..77f97e0
--- /dev/null
+++ 
b/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/value/ByteConverter.java
@@ -0,0 +1,56 @@
+/*****************************************************************
+ *   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.crypto.transformer.value;
+
+/**
+ * @since 4.0
+ */
+public class ByteConverter implements BytesConverter {
+
+    public static final BytesConverter INSTANCE = new ByteConverter();
+    private static final int BYTES = 1;
+
+    static byte getByte(byte[] bytes) {
+        if (bytes.length != BYTES) {
+            throw new IllegalArgumentException("Unexpected number of bytes: " 
+ bytes.length);
+        }
+
+        return bytes[0];
+    }
+
+    static byte[] getBytes(byte b) {
+        return new byte[]{b};
+    }
+
+    @Override
+    public Object fromBytes(byte[] bytes) {
+        return getByte(bytes);
+    }
+
+    @Override
+    public byte[] toBytes(Object value) {
+
+        long number = ((Number) value).longValue();
+        if (number <= Byte.MAX_VALUE && number >= Byte.MIN_VALUE) {
+            return getBytes((byte) number);
+        }
+
+        throw new IllegalArgumentException("Byte is out of range: " + number);
+    }
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/fa605bf3/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/value/BytesConverter.java
----------------------------------------------------------------------
diff --git 
a/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/value/BytesConverter.java
 
b/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/value/BytesConverter.java
index d6b75f6..1caec01 100644
--- 
a/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/value/BytesConverter.java
+++ 
b/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/value/BytesConverter.java
@@ -21,7 +21,7 @@ package org.apache.cayenne.crypto.transformer.value;
 /**
  * @since 4.0
  */
-interface BytesConverter {
+public interface BytesConverter {
 
     Object fromBytes(byte[] bytes);
 

http://git-wip-us.apache.org/repos/asf/cayenne/blob/fa605bf3/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/value/DefaultValueTransformerFactory.java
----------------------------------------------------------------------
diff --git 
a/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/value/DefaultValueTransformerFactory.java
 
b/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/value/DefaultValueTransformerFactory.java
index eb6bf6a..26c4a36 100644
--- 
a/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/value/DefaultValueTransformerFactory.java
+++ 
b/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/value/DefaultValueTransformerFactory.java
@@ -18,15 +18,6 @@
  ****************************************************************/
 package org.apache.cayenne.crypto.transformer.value;
 
-import java.security.Key;
-import java.sql.Types;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
-
 import org.apache.cayenne.crypto.key.KeySource;
 import org.apache.cayenne.dba.TypesMapping;
 import org.apache.cayenne.di.Inject;
@@ -36,190 +27,230 @@ import org.apache.cayenne.map.DbEntity;
 import org.apache.cayenne.map.ObjAttribute;
 import org.apache.cayenne.map.ObjEntity;
 
+import java.security.Key;
+import java.sql.Types;
+import java.util.Collection;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
 /**
  * A {@link ValueTransformerFactory} that creates encryptors/decryptors that 
are
  * taking advantage of the JCE (Java Cryptography Extension) ciphers.
- * 
+ *
  * @since 4.0
  */
 public class DefaultValueTransformerFactory implements ValueTransformerFactory 
{
 
-       private final Key defaultKey;
+    private final Key defaultKey;
+
+    private final Map<String, BytesConverter> objectToBytes;
+    private final Map<Integer, BytesConverter> dbToBytes;
+
+    private final Map<String, BytesConverter> bytesToObject;
+    private final Map<Integer, BytesConverter> bytesToDb;
+
+    private final ConcurrentMap<DbAttribute, ValueEncryptor> encryptors;
+    private final ConcurrentMap<DbAttribute, ValueDecryptor> decryptors;
+
+    public DefaultValueTransformerFactory(@Inject KeySource keySource) {
+        this.defaultKey = keySource.getKey(keySource.getDefaultKeyAlias());
+
+        this.encryptors = new ConcurrentHashMap<DbAttribute, ValueEncryptor>();
+        this.decryptors = new ConcurrentHashMap<DbAttribute, ValueDecryptor>();
+
+        this.objectToBytes = createObjectToBytesConverters();
+        this.dbToBytes = createDbToBytesConverters();
+        this.bytesToObject = createBytesToObjectConverters();
+        this.bytesToDb = createBytesToDbConverters();
+    }
+
+    @Override
+    public ValueDecryptor decryptor(DbAttribute a) {
+        ValueDecryptor e = decryptors.get(a);
+
+        if (e == null) {
+
+            ValueDecryptor newTransformer = createDecryptor(a);
+            ValueDecryptor oldTransformer = decryptors.putIfAbsent(a, 
newTransformer);
+
+            e = oldTransformer != null ? oldTransformer : newTransformer;
+        }
+
+        return e;
+    }
 
-       private final Map<String, BytesConverter> objectToBytes;
-       private final Map<Integer, BytesConverter> dbToBytes;
+    @Override
+    public ValueEncryptor encryptor(DbAttribute a) {
+        ValueEncryptor e = encryptors.get(a);
 
-       private final Map<String, BytesConverter> bytesToObject;
-       private final Map<Integer, BytesConverter> bytesToDb;
+        if (e == null) {
 
-       private final ConcurrentMap<DbAttribute, ValueEncryptor> encryptors;
-       private final ConcurrentMap<DbAttribute, ValueDecryptor> decryptors;
+            ValueEncryptor newTransformer = createEncryptor(a);
+            ValueEncryptor oldTransformer = encryptors.putIfAbsent(a, 
newTransformer);
 
-       public DefaultValueTransformerFactory(@Inject KeySource keySource) {
-               this.defaultKey = 
keySource.getKey(keySource.getDefaultKeyAlias());
+            e = oldTransformer != null ? oldTransformer : newTransformer;
+        }
 
-               this.encryptors = new ConcurrentHashMap<DbAttribute, 
ValueEncryptor>();
-               this.decryptors = new ConcurrentHashMap<DbAttribute, 
ValueDecryptor>();
+        return e;
+    }
 
-               this.objectToBytes = createObjectToBytesConverters();
-               this.dbToBytes = createDbToBytesConverters();
-               this.bytesToObject = createBytesToObjectConverters();
-               this.bytesToDb = createBytesToDbConverters();
-       }
+    protected Map<Integer, BytesConverter> createDbToBytesConverters() {
+        Map<Integer, BytesConverter> map = new HashMap<Integer, 
BytesConverter>();
 
-       @Override
-       public ValueDecryptor decryptor(DbAttribute a) {
-               ValueDecryptor e = decryptors.get(a);
+        map.put(Types.BINARY, BytesToBytesConverter.INSTANCE);
+        map.put(Types.BLOB, BytesToBytesConverter.INSTANCE);
+        map.put(Types.VARBINARY, BytesToBytesConverter.INSTANCE);
+        map.put(Types.LONGVARBINARY, BytesToBytesConverter.INSTANCE);
 
-               if (e == null) {
+        map.put(Types.CHAR, Base64StringConverter.INSTANCE);
+        map.put(Types.NCHAR, Base64StringConverter.INSTANCE);
+        map.put(Types.CLOB, Base64StringConverter.INSTANCE);
+        map.put(Types.NCLOB, Base64StringConverter.INSTANCE);
+        map.put(Types.LONGVARCHAR, Base64StringConverter.INSTANCE);
+        map.put(Types.LONGNVARCHAR, Base64StringConverter.INSTANCE);
+        map.put(Types.VARCHAR, Base64StringConverter.INSTANCE);
+        map.put(Types.NVARCHAR, Base64StringConverter.INSTANCE);
 
-                       ValueDecryptor newTransformer = createDecryptor(a);
-                       ValueDecryptor oldTransformer = 
decryptors.putIfAbsent(a, newTransformer);
+        return map;
+    }
 
-                       e = oldTransformer != null ? oldTransformer : 
newTransformer;
-               }
+    protected Map<Integer, BytesConverter> createBytesToDbConverters() {
+        Map<Integer, BytesConverter> map = new HashMap<Integer, 
BytesConverter>();
 
-               return e;
-       }
+        map.put(Types.BINARY, BytesToBytesConverter.INSTANCE);
+        map.put(Types.BLOB, BytesToBytesConverter.INSTANCE);
+        map.put(Types.VARBINARY, BytesToBytesConverter.INSTANCE);
+        map.put(Types.LONGVARBINARY, BytesToBytesConverter.INSTANCE);
 
-       @Override
-       public ValueEncryptor encryptor(DbAttribute a) {
-               ValueEncryptor e = encryptors.get(a);
+        map.put(Types.CHAR, Base64StringConverter.INSTANCE);
+        map.put(Types.NCHAR, Base64StringConverter.INSTANCE);
+        map.put(Types.CLOB, Base64StringConverter.INSTANCE);
+        map.put(Types.NCLOB, Base64StringConverter.INSTANCE);
+        map.put(Types.LONGVARCHAR, Base64StringConverter.INSTANCE);
+        map.put(Types.LONGNVARCHAR, Base64StringConverter.INSTANCE);
+        map.put(Types.VARCHAR, Base64StringConverter.INSTANCE);
+        map.put(Types.NVARCHAR, Base64StringConverter.INSTANCE);
 
-               if (e == null) {
+        return map;
+    }
 
-                       ValueEncryptor newTransformer = createEncryptor(a);
-                       ValueEncryptor oldTransformer = 
encryptors.putIfAbsent(a, newTransformer);
+    protected Map<String, BytesConverter> createObjectToBytesConverters() {
+        Map<String, BytesConverter> map = new HashMap<>();
 
-                       e = oldTransformer != null ? oldTransformer : 
newTransformer;
-               }
+        map.put("byte[]", BytesToBytesConverter.INSTANCE);
+        map.put(String.class.getName(), Utf8StringConverter.INSTANCE);
 
-               return e;
-       }
+        map.put(Long.class.getName(), LongConverter.INSTANCE);
+        map.put(Long.TYPE.getName(), LongConverter.INSTANCE);
 
-       protected Map<Integer, BytesConverter> createDbToBytesConverters() {
-               Map<Integer, BytesConverter> map = new HashMap<Integer, 
BytesConverter>();
+        map.put(Integer.class.getName(), IntegerConverter.INSTANCE);
+        map.put(Integer.TYPE.getName(), IntegerConverter.INSTANCE);
 
-               map.put(Types.BINARY, BytesToBytesConverter.INSTANCE);
-               map.put(Types.BLOB, BytesToBytesConverter.INSTANCE);
-               map.put(Types.VARBINARY, BytesToBytesConverter.INSTANCE);
-               map.put(Types.LONGVARBINARY, BytesToBytesConverter.INSTANCE);
+        map.put(Short.class.getName(), ShortConverter.INSTANCE);
+        map.put(Short.TYPE.getName(), ShortConverter.INSTANCE);
 
-               map.put(Types.CHAR, Base64StringConverter.INSTANCE);
-               map.put(Types.NCHAR, Base64StringConverter.INSTANCE);
-               map.put(Types.CLOB, Base64StringConverter.INSTANCE);
-               map.put(Types.NCLOB, Base64StringConverter.INSTANCE);
-               map.put(Types.LONGVARCHAR, Base64StringConverter.INSTANCE);
-               map.put(Types.LONGNVARCHAR, Base64StringConverter.INSTANCE);
-               map.put(Types.VARCHAR, Base64StringConverter.INSTANCE);
-               map.put(Types.NVARCHAR, Base64StringConverter.INSTANCE);
+        map.put(Byte.class.getName(), ByteConverter.INSTANCE);
+        map.put(Byte.TYPE.getName(), ByteConverter.INSTANCE);
 
-               return map;
-       }
+        map.put(Boolean.class.getName(), BooleanConverter.INSTANCE);
+        map.put(Date.class.getName(), UtilDateConverter.INSTANCE);
 
-       protected Map<Integer, BytesConverter> createBytesToDbConverters() {
-               Map<Integer, BytesConverter> map = new HashMap<Integer, 
BytesConverter>();
+        return map;
+    }
 
-               map.put(Types.BINARY, BytesToBytesConverter.INSTANCE);
-               map.put(Types.BLOB, BytesToBytesConverter.INSTANCE);
-               map.put(Types.VARBINARY, BytesToBytesConverter.INSTANCE);
-               map.put(Types.LONGVARBINARY, BytesToBytesConverter.INSTANCE);
+    protected Map<String, BytesConverter> createBytesToObjectConverters() {
 
-               map.put(Types.CHAR, Base64StringConverter.INSTANCE);
-               map.put(Types.NCHAR, Base64StringConverter.INSTANCE);
-               map.put(Types.CLOB, Base64StringConverter.INSTANCE);
-               map.put(Types.NCLOB, Base64StringConverter.INSTANCE);
-               map.put(Types.LONGVARCHAR, Base64StringConverter.INSTANCE);
-               map.put(Types.LONGNVARCHAR, Base64StringConverter.INSTANCE);
-               map.put(Types.VARCHAR, Base64StringConverter.INSTANCE);
-               map.put(Types.NVARCHAR, Base64StringConverter.INSTANCE);
+        Map<String, BytesConverter> map = new HashMap<>();
 
-               return map;
-       }
+        map.put("byte[]", BytesToBytesConverter.INSTANCE);
+        map.put(String.class.getName(), Utf8StringConverter.INSTANCE);
 
-       protected Map<String, BytesConverter> createObjectToBytesConverters() {
-               Map<String, BytesConverter> map = new HashMap<>();
+        map.put(Long.class.getName(), LongConverter.INSTANCE);
+        map.put(Long.TYPE.getName(), LongConverter.INSTANCE);
 
-               map.put("byte[]", BytesToBytesConverter.INSTANCE);
-               map.put(String.class.getName(), Utf8StringConverter.INSTANCE);
+        map.put(Integer.class.getName(), IntegerConverter.INSTANCE);
+        map.put(Integer.TYPE.getName(), IntegerConverter.INSTANCE);
 
-               return map;
-       }
+        map.put(Short.class.getName(), ShortConverter.INSTANCE);
+        map.put(Short.TYPE.getName(), ShortConverter.INSTANCE);
 
-       protected Map<String, BytesConverter> createBytesToObjectConverters() {
+        map.put(Byte.class.getName(), ByteConverter.INSTANCE);
+        map.put(Byte.TYPE.getName(), ByteConverter.INSTANCE);
 
-               Map<String, BytesConverter> map = new HashMap<>();
+        map.put(Boolean.class.getName(), BooleanConverter.INSTANCE);
+        map.put(Date.class.getName(), UtilDateConverter.INSTANCE);
 
-               map.put("byte[]", BytesToBytesConverter.INSTANCE);
-               map.put(String.class.getName(), Utf8StringConverter.INSTANCE);
+        return map;
+    }
 
-               return map;
-       }
+    protected ValueEncryptor createEncryptor(DbAttribute a) {
 
-       protected ValueEncryptor createEncryptor(DbAttribute a) {
+        String type = getJavaType(a);
 
-               String type = getJavaType(a);
+        BytesConverter toBytes = objectToBytes.get(type);
+        if (toBytes == null) {
+            throw new IllegalArgumentException("The type " + type + " for 
attribute " + a
+                    + " has no object-to-bytes conversion");
+        }
 
-               BytesConverter toBytes = objectToBytes.get(type);
-               if (toBytes == null) {
-                       throw new IllegalArgumentException("The type " + type + 
" for attribute " + a
-                                       + " has no object-to-bytes conversion");
-               }
+        BytesConverter fromBytes = bytesToDb.get(a.getType());
+        if (fromBytes == null) {
+            throw new IllegalArgumentException("The type " + 
TypesMapping.getSqlNameByType(a.getType())
+                    + " for attribute " + a + " has no bytes-to-db 
conversion");
+        }
 
-               BytesConverter fromBytes = bytesToDb.get(a.getType());
-               if (fromBytes == null) {
-                       throw new IllegalArgumentException("The type " + 
TypesMapping.getSqlNameByType(a.getType())
-                                       + " for attribute " + a + " has no 
bytes-to-db conversion");
-               }
+        return new DefaultValueEncryptor(toBytes, fromBytes);
+    }
 
-               return new DefaultValueEncryptor(toBytes, fromBytes);
-       }
+    protected ValueDecryptor createDecryptor(DbAttribute a) {
 
-       protected ValueDecryptor createDecryptor(DbAttribute a) {
+        BytesConverter toBytes = dbToBytes.get(a.getType());
+        if (toBytes == null) {
+            throw new IllegalArgumentException("The type " + 
TypesMapping.getSqlNameByType(a.getType())
+                    + " for attribute " + a + " has no db-to-bytes 
conversion");
+        }
 
-               BytesConverter toBytes = dbToBytes.get(a.getType());
-               if (toBytes == null) {
-                       throw new IllegalArgumentException("The type " + 
TypesMapping.getSqlNameByType(a.getType())
-                                       + " for attribute " + a + " has no 
db-to-bytes conversion");
-               }
+        String type = getJavaType(a);
+        BytesConverter fromBytes = bytesToObject.get(type);
+        if (fromBytes == null) {
+            throw new IllegalArgumentException("The type " + type + " for 
attribute " + a
+                    + " has no bytes-to-object conversion");
+        }
 
-               String type = getJavaType(a);
-               BytesConverter fromBytes = bytesToObject.get(type);
-               if (fromBytes == null) {
-                       throw new IllegalArgumentException("The type " + type + 
" for attribute " + a
-                                       + " has no bytes-to-object conversion");
-               }
+        return new DefaultValueDecryptor(toBytes, fromBytes, defaultKey);
+    }
 
-               return new DefaultValueDecryptor(toBytes, fromBytes, 
defaultKey);
-       }
+    // TODO: calculating Java type of ObjAttribute may become unneeded per
+    // CAY-1752, as DbAttribute will have it.
+    protected String getJavaType(DbAttribute a) {
 
-       // TODO: calculating Java type of ObjAttribute may become unneeded per
-       // CAY-1752, as DbAttribute will have it.
-       protected String getJavaType(DbAttribute a) {
+        DbEntity dbEntity = a.getEntity();
+        DataMap dataMap = dbEntity.getDataMap();
+        Collection<ObjEntity> objEntities = 
dataMap.getMappedEntities(dbEntity);
 
-               DbEntity dbEntity = a.getEntity();
-               DataMap dataMap = dbEntity.getDataMap();
-               Collection<ObjEntity> objEntities = 
dataMap.getMappedEntities(dbEntity);
-
-               if (objEntities.size() != 1) {
-                       return TypesMapping.getJavaBySqlType(a.getType());
-               }
+        if (objEntities.size() != 1) {
+            return TypesMapping.getJavaBySqlType(a.getType());
+        }
 
-               Collection<String> javaTypes = new HashSet<String>();
-               ObjEntity objEntity = objEntities.iterator().next();
-               for (ObjAttribute oa : objEntity.getAttributes()) {
+        Collection<String> javaTypes = new HashSet<String>();
+        ObjEntity objEntity = objEntities.iterator().next();
+        for (ObjAttribute oa : objEntity.getAttributes()) {
 
-                       // TODO: this won't pick up flattened attributes
-                       if (a.getName().equals(oa.getDbAttributePath())) {
-                               javaTypes.add(oa.getType());
-                       }
-               }
+            // TODO: this won't pick up flattened attributes
+            if (a.getName().equals(oa.getDbAttributePath())) {
+                javaTypes.add(oa.getType());
+            }
+        }
 
-               if (javaTypes.size() != 1) {
-                       return TypesMapping.getJavaBySqlType(a.getType());
-               }
+        if (javaTypes.size() != 1) {
+            return TypesMapping.getJavaBySqlType(a.getType());
+        }
 
-               return javaTypes.iterator().next();
-       }
+        return javaTypes.iterator().next();
+    }
 
 }

http://git-wip-us.apache.org/repos/asf/cayenne/blob/fa605bf3/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/value/IntegerConverter.java
----------------------------------------------------------------------
diff --git 
a/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/value/IntegerConverter.java
 
b/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/value/IntegerConverter.java
new file mode 100644
index 0000000..e95a895
--- /dev/null
+++ 
b/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/value/IntegerConverter.java
@@ -0,0 +1,71 @@
+/*****************************************************************
+ *   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.crypto.transformer.value;
+
+/**
+ * Converts between integer and byte[] using big-endian encoding.
+ *
+ * @since 4.0
+ */
+public class IntegerConverter implements BytesConverter {
+
+    public static final BytesConverter INSTANCE = new IntegerConverter();
+    private static final int BYTES = 4;
+
+    static int getInt(byte[] bytes) {
+        if (bytes.length < BYTES) {
+            return ShortConverter.getShort(bytes);
+        }
+
+        if (bytes.length > BYTES) {
+            throw new IllegalArgumentException("byte[] is too large for a 
single int value: " + bytes.length);
+        }
+
+        return (bytes[0] & 0xFF) << 24 | (bytes[1] & 0xFF) << 16 | (bytes[2] & 
0xFF) << 8 | (bytes[3] & 0xFF);
+    }
+
+    static byte[] getBytes(int k) {
+
+        if (k <= Short.MAX_VALUE) {
+            return ShortConverter.getBytes((short) k);
+        }
+
+        return new byte[]{
+                (byte) (k >> 24),
+                (byte) (k >> 16),
+                (byte) (k >> 8),
+                (byte) k};
+    }
+
+    @Override
+    public Object fromBytes(byte[] bytes) {
+        return getInt(bytes);
+    }
+
+    @Override
+    public byte[] toBytes(Object value) {
+
+        long number = ((Number) value).longValue();
+        if (number <= Integer.MAX_VALUE && number >= Integer.MIN_VALUE) {
+            return getBytes((int) number);
+        }
+
+        throw new IllegalArgumentException("Integer is out of range: " + 
number);
+    }
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/fa605bf3/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/value/LongConverter.java
----------------------------------------------------------------------
diff --git 
a/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/value/LongConverter.java
 
b/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/value/LongConverter.java
new file mode 100644
index 0000000..ab2a770
--- /dev/null
+++ 
b/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/value/LongConverter.java
@@ -0,0 +1,58 @@
+package org.apache.cayenne.crypto.transformer.value;
+
+/**
+ * Converts between long and byte[] using big-endian encoding.
+ *
+ * @since 4.0
+ */
+public class LongConverter implements BytesConverter {
+
+    public static final BytesConverter INSTANCE = new LongConverter();
+    private static final int BYTES = 8;
+
+    static long getLong(byte[] bytes) {
+        if (bytes.length < BYTES) {
+            return IntegerConverter.getInt(bytes);
+        }
+
+        if (bytes.length > BYTES) {
+            throw new IllegalArgumentException("byte[] is too large for a 
single long value: " + bytes.length);
+        }
+
+        return (bytes[0] & 0xFFL) << 56
+                | (bytes[1] & 0xFFL) << 48
+                | (bytes[2] & 0xFFL) << 40
+                | (bytes[3] & 0xFFL) << 32
+                | (bytes[4] & 0xFFL) << 24
+                | (bytes[5] & 0xFFL) << 16
+                | (bytes[6] & 0xFFL) << 8
+                | (bytes[7] & 0xFFL);
+    }
+
+    static byte[] getBytes(long k) {
+
+        if (k <= Integer.MAX_VALUE) {
+            return IntegerConverter.getBytes((int) k);
+        }
+
+        return new byte[]{
+                (byte) (k >> 56),
+                (byte) (k >> 48),
+                (byte) (k >> 40),
+                (byte) (k >> 32),
+                (byte) (k >> 24),
+                (byte) (k >> 16),
+                (byte) (k >> 8),
+                (byte) k};
+    }
+
+    @Override
+    public Object fromBytes(byte[] bytes) {
+        return getLong(bytes);
+    }
+
+    @Override
+    public byte[] toBytes(Object value) {
+        return getBytes(((Number) value).longValue());
+    }
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/fa605bf3/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/value/ShortConverter.java
----------------------------------------------------------------------
diff --git 
a/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/value/ShortConverter.java
 
b/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/value/ShortConverter.java
new file mode 100644
index 0000000..7df6dcf
--- /dev/null
+++ 
b/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/value/ShortConverter.java
@@ -0,0 +1,68 @@
+/*****************************************************************
+ *   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.crypto.transformer.value;
+
+/**
+ * @since 4.0
+ */
+public class ShortConverter implements BytesConverter {
+
+    public static final BytesConverter INSTANCE = new ShortConverter();
+    private static final int BYTES = 2;
+
+    static short getShort(byte[] bytes) {
+
+        if (bytes.length < BYTES) {
+            return ByteConverter.getByte(bytes);
+        }
+
+        if (bytes.length > BYTES) {
+            throw new IllegalArgumentException("byte[] is too large for a 
single short value: " + bytes.length);
+        }
+
+        return (short) ((bytes[0] & 0xFF) << 8 | (bytes[1] & 0xFF));
+    }
+
+    static byte[] getBytes(short k) {
+
+        if (k <= Byte.MAX_VALUE) {
+            return ByteConverter.getBytes((byte) k);
+        }
+
+        return new byte[]{
+                (byte) (k >> 8),
+                (byte) k};
+    }
+
+    @Override
+    public Object fromBytes(byte[] bytes) {
+        return getShort(bytes);
+    }
+
+    @Override
+    public byte[] toBytes(Object value) {
+
+        long number = ((Number) value).longValue();
+        if (number <= Short.MAX_VALUE && number >= Short.MIN_VALUE) {
+            return getBytes((short) number);
+        }
+
+        throw new IllegalArgumentException("Short is out of range: " + number);
+    }
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/fa605bf3/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/value/Utf8StringConverter.java
----------------------------------------------------------------------
diff --git 
a/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/value/Utf8StringConverter.java
 
b/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/value/Utf8StringConverter.java
index b9cddb7..314d122 100644
--- 
a/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/value/Utf8StringConverter.java
+++ 
b/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/value/Utf8StringConverter.java
@@ -23,11 +23,11 @@ import java.nio.charset.Charset;
 /**
  * @since 4.0
  */
-final class Utf8StringConverter implements BytesConverter {
+public final class Utf8StringConverter implements BytesConverter {
 
     static final String DEFAULT_CHARSET = "UTF-8";
 
-    static final BytesConverter INSTANCE = new Utf8StringConverter();
+    public static final BytesConverter INSTANCE = new Utf8StringConverter();
 
     private Charset utf8;
 

http://git-wip-us.apache.org/repos/asf/cayenne/blob/fa605bf3/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/value/UtilDateConverter.java
----------------------------------------------------------------------
diff --git 
a/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/value/UtilDateConverter.java
 
b/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/value/UtilDateConverter.java
new file mode 100644
index 0000000..97458cc
--- /dev/null
+++ 
b/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/value/UtilDateConverter.java
@@ -0,0 +1,30 @@
+package org.apache.cayenne.crypto.transformer.value;
+
+import java.util.Date;
+import java.util.Objects;
+
+/**
+ * Converts between java.util.Date and byte[], based on the long timestamp 
encoding.
+ *
+ * @since 4.0
+ */
+public class UtilDateConverter implements BytesConverter {
+
+    public static final BytesConverter INSTANCE = new 
UtilDateConverter(LongConverter.INSTANCE);
+
+    private BytesConverter longConverter;
+
+    public UtilDateConverter(BytesConverter longConverter) {
+        this.longConverter = Objects.requireNonNull(longConverter);
+    }
+
+    @Override
+    public Object fromBytes(byte[] bytes) {
+        return new Date((long) longConverter.fromBytes(bytes));
+    }
+
+    @Override
+    public byte[] toBytes(Object value) {
+        return longConverter.toBytes(((Date) value).getTime());
+    }
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/fa605bf3/cayenne-crypto/src/test/java/org/apache/cayenne/crypto/Runtime_AES128_Base.java
----------------------------------------------------------------------
diff --git 
a/cayenne-crypto/src/test/java/org/apache/cayenne/crypto/Runtime_AES128_Base.java
 
b/cayenne-crypto/src/test/java/org/apache/cayenne/crypto/Runtime_AES128_Base.java
index d1f3834..8703c4a 100644
--- 
a/cayenne-crypto/src/test/java/org/apache/cayenne/crypto/Runtime_AES128_Base.java
+++ 
b/cayenne-crypto/src/test/java/org/apache/cayenne/crypto/Runtime_AES128_Base.java
@@ -18,17 +18,18 @@
  ****************************************************************/
 package org.apache.cayenne.crypto;
 
-import java.net.URL;
-
 import org.apache.cayenne.configuration.server.ServerRuntime;
 import org.apache.cayenne.crypto.key.JceksKeySourceTest;
 import org.apache.cayenne.di.Module;
 import org.apache.cayenne.test.jdbc.DBHelper;
 import org.apache.cayenne.test.jdbc.TableHelper;
 
+import java.net.URL;
+
 public class Runtime_AES128_Base {
 
     protected ServerRuntime runtime;
+    protected TableHelper table1;
     protected TableHelper table2;
 
     protected void setUp(boolean compress) throws Exception {
@@ -50,6 +51,10 @@ public class Runtime_AES128_Base {
 
         this.table2 = new TableHelper(dbHelper, "TABLE2").setColumns("ID", 
"PLAIN_BYTES", "CRYPTO_BYTES");
         table2.deleteAll();
+
+        this.table1 = new TableHelper(dbHelper, "TABLE1").setColumns("ID", 
"PLAIN_STRING", "CRYPTO_STRING",
+                "PLAIN_INT", "CRYPTO_INT");
+        table1.deleteAll();
     }
 
 }

http://git-wip-us.apache.org/repos/asf/cayenne/blob/fa605bf3/cayenne-crypto/src/test/java/org/apache/cayenne/crypto/Runtime_AES128_IT.java
----------------------------------------------------------------------
diff --git 
a/cayenne-crypto/src/test/java/org/apache/cayenne/crypto/Runtime_AES128_IT.java 
b/cayenne-crypto/src/test/java/org/apache/cayenne/crypto/Runtime_AES128_IT.java
index e337d65..2589243 100644
--- 
a/cayenne-crypto/src/test/java/org/apache/cayenne/crypto/Runtime_AES128_IT.java
+++ 
b/cayenne-crypto/src/test/java/org/apache/cayenne/crypto/Runtime_AES128_IT.java
@@ -18,22 +18,24 @@
  ****************************************************************/
 package org.apache.cayenne.crypto;
 
-import static org.junit.Assert.assertArrayEquals;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNull;
-
-import java.sql.SQLException;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
 import org.apache.cayenne.ObjectContext;
+import org.apache.cayenne.crypto.db.Table1;
 import org.apache.cayenne.crypto.db.Table2;
+import org.apache.cayenne.crypto.transformer.value.IntegerConverter;
 import org.apache.cayenne.crypto.unit.CryptoUnitUtils;
 import org.apache.cayenne.query.SelectQuery;
 import org.junit.Before;
 import org.junit.Test;
 
+import java.sql.SQLException;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
 public class Runtime_AES128_IT extends Runtime_AES128_Base {
 
     @Before
@@ -58,6 +60,22 @@ public class Runtime_AES128_IT extends Runtime_AES128_Base {
     }
 
     @Test
+    public void testInsert_Numeric() throws SQLException {
+
+        ObjectContext context = runtime.newContext();
+
+        Table1 t1 = context.newObject(Table1.class);
+        t1.setPlainInt(59);
+        t1.setCryptoInt(61);
+
+        context.commitChanges();
+
+        Object[] data = table1.select();
+        assertEquals(59, data[3]);
+        assertEquals(61, 
IntegerConverter.INSTANCE.fromBytes(CryptoUnitUtils.decrypt_AES_CBC((byte[]) 
data[4], runtime)));
+    }
+
+    @Test
     public void testInsert_MultipleObjects() throws SQLException {
 
         ObjectContext context = runtime.newContext();
@@ -119,4 +137,23 @@ public class Runtime_AES128_IT extends Runtime_AES128_Base 
{
         assertArrayEquals(null, result.get(2).getCryptoBytes());
     }
 
+
+    @Test
+    public void test_SelectNumeric() throws SQLException {
+
+        ObjectContext context = runtime.newContext();
+
+        Table1 t1 = context.newObject(Table1.class);
+        t1.setPlainInt(59);
+        t1.setCryptoInt(61);
+
+        context.commitChanges();
+
+        List<Table1> result = 
SelectQuery.query(Table1.class).select(runtime.newContext());
+
+        assertEquals(1, result.size());
+        assertEquals(59, result.get(0).getPlainInt());
+        assertEquals(61, result.get(0).getCryptoInt());
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/cayenne/blob/fa605bf3/cayenne-crypto/src/test/java/org/apache/cayenne/crypto/db/auto/_Table1.java
----------------------------------------------------------------------
diff --git 
a/cayenne-crypto/src/test/java/org/apache/cayenne/crypto/db/auto/_Table1.java 
b/cayenne-crypto/src/test/java/org/apache/cayenne/crypto/db/auto/_Table1.java
index 85c5aca..97116c4 100644
--- 
a/cayenne-crypto/src/test/java/org/apache/cayenne/crypto/db/auto/_Table1.java
+++ 
b/cayenne-crypto/src/test/java/org/apache/cayenne/crypto/db/auto/_Table1.java
@@ -15,9 +15,19 @@ public abstract class _Table1 extends CayenneDataObject {
 
     public static final String ID_PK_COLUMN = "ID";
 
+    public static final Property<Integer> CRYPTO_INT = new 
Property<Integer>("cryptoInt");
     public static final Property<String> CRYPTO_STRING = new 
Property<String>("cryptoString");
+    public static final Property<Integer> PLAIN_INT = new 
Property<Integer>("plainInt");
     public static final Property<String> PLAIN_STRING = new 
Property<String>("plainString");
 
+    public void setCryptoInt(int cryptoInt) {
+        writeProperty("cryptoInt", cryptoInt);
+    }
+    public int getCryptoInt() {
+        Object value = readProperty("cryptoInt");
+        return (value != null) ? (Integer) value : 0;
+    }
+
     public void setCryptoString(String cryptoString) {
         writeProperty("cryptoString", cryptoString);
     }
@@ -25,6 +35,14 @@ public abstract class _Table1 extends CayenneDataObject {
         return (String)readProperty("cryptoString");
     }
 
+    public void setPlainInt(int plainInt) {
+        writeProperty("plainInt", plainInt);
+    }
+    public int getPlainInt() {
+        Object value = readProperty("plainInt");
+        return (value != null) ? (Integer) value : 0;
+    }
+
     public void setPlainString(String plainString) {
         writeProperty("plainString", plainString);
     }

http://git-wip-us.apache.org/repos/asf/cayenne/blob/fa605bf3/cayenne-crypto/src/test/java/org/apache/cayenne/crypto/transformer/value/BooleanConverterTest.java
----------------------------------------------------------------------
diff --git 
a/cayenne-crypto/src/test/java/org/apache/cayenne/crypto/transformer/value/BooleanConverterTest.java
 
b/cayenne-crypto/src/test/java/org/apache/cayenne/crypto/transformer/value/BooleanConverterTest.java
new file mode 100644
index 0000000..ff554cb
--- /dev/null
+++ 
b/cayenne-crypto/src/test/java/org/apache/cayenne/crypto/transformer/value/BooleanConverterTest.java
@@ -0,0 +1,28 @@
+package org.apache.cayenne.crypto.transformer.value;
+
+import org.junit.Test;
+
+import java.text.ParseException;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+
+public class BooleanConverterTest {
+
+    @Test
+    public void testFromBytes() {
+        assertEquals(true, BooleanConverter.INSTANCE.fromBytes(new byte[]{1}));
+        assertEquals(false, BooleanConverter.INSTANCE.fromBytes(new 
byte[]{0}));
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testFromBytes_InvalidArray() {
+        BooleanConverter.INSTANCE.fromBytes(new byte[]{1, 0});
+    }
+
+    @Test
+    public void testToBytes() throws ParseException {
+        assertArrayEquals(new byte[]{0}, 
BooleanConverter.INSTANCE.toBytes(false));
+        assertArrayEquals(new byte[]{1}, 
BooleanConverter.INSTANCE.toBytes(true));
+    }
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/fa605bf3/cayenne-crypto/src/test/java/org/apache/cayenne/crypto/transformer/value/ByteConverterTest.java
----------------------------------------------------------------------
diff --git 
a/cayenne-crypto/src/test/java/org/apache/cayenne/crypto/transformer/value/ByteConverterTest.java
 
b/cayenne-crypto/src/test/java/org/apache/cayenne/crypto/transformer/value/ByteConverterTest.java
new file mode 100644
index 0000000..ae1ebc7
--- /dev/null
+++ 
b/cayenne-crypto/src/test/java/org/apache/cayenne/crypto/transformer/value/ByteConverterTest.java
@@ -0,0 +1,45 @@
+/*****************************************************************
+ *   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.crypto.transformer.value;
+
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+
+public class ByteConverterTest {
+
+    @Test
+    public void testFromBytes() {
+        assertEquals(new Byte((byte) 6), ByteConverter.INSTANCE.fromBytes(new 
byte[]{6}));
+        assertEquals(new Byte((byte) -7), ByteConverter.INSTANCE.fromBytes(new 
byte[]{-7}));
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testFromBytes_TooLong() {
+        ByteConverter.INSTANCE.fromBytes(new byte[]{6, 5, 4});
+    }
+
+    @Test
+    public void testToBytes() {
+        assertArrayEquals(new byte[]{6}, ByteConverter.INSTANCE.toBytes((byte) 
6));
+        assertArrayEquals(new byte[]{-7}, 
ByteConverter.INSTANCE.toBytes((byte) -7));
+    }
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/fa605bf3/cayenne-crypto/src/test/java/org/apache/cayenne/crypto/transformer/value/IntegerConverterTest.java
----------------------------------------------------------------------
diff --git 
a/cayenne-crypto/src/test/java/org/apache/cayenne/crypto/transformer/value/IntegerConverterTest.java
 
b/cayenne-crypto/src/test/java/org/apache/cayenne/crypto/transformer/value/IntegerConverterTest.java
new file mode 100644
index 0000000..ddc1472
--- /dev/null
+++ 
b/cayenne-crypto/src/test/java/org/apache/cayenne/crypto/transformer/value/IntegerConverterTest.java
@@ -0,0 +1,61 @@
+/*****************************************************************
+ *   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.crypto.transformer.value;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+
+
+public class IntegerConverterTest {
+
+    @Test
+    public void testFromBytes_InByteRange() {
+        assertEquals(new Integer(6), new IntegerConverter().fromBytes(new 
byte[]{6}));
+    }
+
+    @Test
+    public void testFromBytes_InShortRange() {
+        assertEquals(new Integer(1542), new IntegerConverter().fromBytes(new 
byte[]{6, 6}));
+    }
+
+    @Test
+    public void testFromBytes_InIntRange() {
+        // 16 + 256*7 + 256*256* 6
+        assertEquals(new Integer(395024), new IntegerConverter().fromBytes(new 
byte[]{0, 6, 7, 16}));
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testFromBytes_TooLong() {
+        IntegerConverter.INSTANCE.fromBytes(new byte[]{6, 5, 4});
+    }
+
+    @Test
+    public void testToBytes() {
+        assertArrayEquals(new byte[]{127, -1, -1, -2}, new 
IntegerConverter().toBytes(Integer.MAX_VALUE - 1));
+        assertArrayEquals(new byte[]{127, -2}, new 
IntegerConverter().toBytes(Short.MAX_VALUE - 1));
+        assertArrayEquals(new byte[]{-7}, new IntegerConverter().toBytes(-7));
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testToBytesOverflow() {
+        new IntegerConverter().toBytes(new Long(Integer.MAX_VALUE + 55l));
+    }
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/fa605bf3/cayenne-crypto/src/test/java/org/apache/cayenne/crypto/transformer/value/LongConverterTest.java
----------------------------------------------------------------------
diff --git 
a/cayenne-crypto/src/test/java/org/apache/cayenne/crypto/transformer/value/LongConverterTest.java
 
b/cayenne-crypto/src/test/java/org/apache/cayenne/crypto/transformer/value/LongConverterTest.java
new file mode 100644
index 0000000..f35b5bf
--- /dev/null
+++ 
b/cayenne-crypto/src/test/java/org/apache/cayenne/crypto/transformer/value/LongConverterTest.java
@@ -0,0 +1,49 @@
+/*****************************************************************
+ *   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.crypto.transformer.value;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+
+
+public class LongConverterTest {
+
+
+    @Test
+    public void testFromBytes_InIntRange() {
+        // 256*256* 6 + 256*7 + 16
+        assertEquals(new Long(395024), new LongConverter().fromBytes(new 
byte[]{0, 6, 7, 16}));
+    }
+
+    @Test
+    public void testFromBytes_InLongRange() {
+        // 6*256*256*256*256 + 7*256*256*256 + 16*256*256 + 17*256 + 99
+        assertEquals(new Long(25888297315l), new LongConverter().fromBytes(new 
byte[]{0, 0, 0, 6, 7, 16, 17, 99}));
+    }
+
+    @Test
+    public void testToBytes() {
+        assertArrayEquals(new byte[]{127, -1, -1, -1, -1, -1, -1, -2}, new 
LongConverter().toBytes((long) (Long.MAX_VALUE - 1l)));
+        assertArrayEquals(new byte[]{127, -1, -1, -2}, new 
LongConverter().toBytes((long) (Integer.MAX_VALUE - 1)));
+        assertArrayEquals(new byte[]{127, -2}, new 
LongConverter().toBytes(Short.MAX_VALUE - 1));
+        assertArrayEquals(new byte[]{-7}, new LongConverter().toBytes(-7));
+    }
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/fa605bf3/cayenne-crypto/src/test/java/org/apache/cayenne/crypto/transformer/value/ShortConverterTest.java
----------------------------------------------------------------------
diff --git 
a/cayenne-crypto/src/test/java/org/apache/cayenne/crypto/transformer/value/ShortConverterTest.java
 
b/cayenne-crypto/src/test/java/org/apache/cayenne/crypto/transformer/value/ShortConverterTest.java
new file mode 100644
index 0000000..50088c9
--- /dev/null
+++ 
b/cayenne-crypto/src/test/java/org/apache/cayenne/crypto/transformer/value/ShortConverterTest.java
@@ -0,0 +1,48 @@
+/*****************************************************************
+ *   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.crypto.transformer.value;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+
+public class ShortConverterTest {
+
+    @Test
+    public void testFromBytes_InByteRange() {
+        assertEquals(new Short((short) 6), 
ShortConverter.INSTANCE.fromBytes(new byte[]{6}));
+    }
+
+    @Test
+    public void testFromBytes_InShortRange() {
+        assertEquals(new Short((short) 1287), 
ShortConverter.INSTANCE.fromBytes(new byte[]{5, 7}));
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testFromBytes_TooLong() {
+        ShortConverter.INSTANCE.fromBytes(new byte[]{6, 5, 4});
+    }
+
+    @Test
+    public void testToBytes() {
+        assertArrayEquals(new byte[]{127, -2}, 
ShortConverter.INSTANCE.toBytes((short) (Short.MAX_VALUE - 1)));
+        assertArrayEquals(new byte[]{-7}, 
ShortConverter.INSTANCE.toBytes((short) -7));
+    }
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/fa605bf3/cayenne-crypto/src/test/java/org/apache/cayenne/crypto/transformer/value/UtilDateConverterTest.java
----------------------------------------------------------------------
diff --git 
a/cayenne-crypto/src/test/java/org/apache/cayenne/crypto/transformer/value/UtilDateConverterTest.java
 
b/cayenne-crypto/src/test/java/org/apache/cayenne/crypto/transformer/value/UtilDateConverterTest.java
new file mode 100644
index 0000000..a2691ae
--- /dev/null
+++ 
b/cayenne-crypto/src/test/java/org/apache/cayenne/crypto/transformer/value/UtilDateConverterTest.java
@@ -0,0 +1,31 @@
+package org.apache.cayenne.crypto.transformer.value;
+
+import org.junit.Test;
+
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.TimeZone;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+
+public class UtilDateConverterTest {
+
+    private Date date(String dateString) throws ParseException {
+        SimpleDateFormat f = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+        f.setTimeZone(TimeZone.getTimeZone("GMT"));
+        return f.parse(dateString);
+    }
+
+    @Test
+    public void testFromBytes() throws ParseException {
+        assertEquals(date("2015-01-07 11:00:02"), 
UtilDateConverter.INSTANCE.fromBytes(new byte[]{0, 0, 1, 74, -60, 13, 31, 80}));
+    }
+
+    @Test
+    public void testToBytes() throws ParseException {
+        assertArrayEquals(new byte[]{0, 0, 1, 74, -60, 13, 31, 80},
+                UtilDateConverter.INSTANCE.toBytes(date("2015-01-07 
11:00:02")));
+    }
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/fa605bf3/cayenne-crypto/src/test/java/org/apache/cayenne/crypto/unit/CryptoUnitUtils.java
----------------------------------------------------------------------
diff --git 
a/cayenne-crypto/src/test/java/org/apache/cayenne/crypto/unit/CryptoUnitUtils.java
 
b/cayenne-crypto/src/test/java/org/apache/cayenne/crypto/unit/CryptoUnitUtils.java
index 0695042..0f3e4d0 100644
--- 
a/cayenne-crypto/src/test/java/org/apache/cayenne/crypto/unit/CryptoUnitUtils.java
+++ 
b/cayenne-crypto/src/test/java/org/apache/cayenne/crypto/unit/CryptoUnitUtils.java
@@ -18,22 +18,22 @@
  ****************************************************************/
 package org.apache.cayenne.crypto.unit;
 
+import org.apache.cayenne.configuration.server.ServerRuntime;
+import org.apache.cayenne.crypto.key.KeySource;
+import org.apache.cayenne.crypto.transformer.bytes.Header;
+
+import javax.crypto.Cipher;
+import javax.crypto.spec.IvParameterSpec;
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.math.BigInteger;
+import java.nio.ByteBuffer;
 import java.security.Key;
 import java.util.Arrays;
 import java.util.Random;
 import java.util.zip.GZIPInputStream;
 
-import javax.crypto.Cipher;
-import javax.crypto.spec.IvParameterSpec;
-
-import org.apache.cayenne.configuration.server.ServerRuntime;
-import org.apache.cayenne.crypto.key.KeySource;
-import org.apache.cayenne.crypto.transformer.bytes.Header;
-
 public class CryptoUnitUtils {
 
     public static byte[] bytesOfSize(int len) {
@@ -97,4 +97,25 @@ public class CryptoUnitUtils {
         }
     }
 
+    public static byte[] toByteArray(int integer) {
+
+        if (integer <= Short.MAX_VALUE) {
+            return toByteArray((short) integer);
+        }
+
+        return ByteBuffer.allocate(4).putInt(integer).array();
+    }
+
+    public static byte[] toByteArray(short shortInt) {
+        if (shortInt <= Byte.MAX_VALUE) {
+            return toByteArray((byte) shortInt);
+        }
+
+        return ByteBuffer.allocate(2).putShort(shortInt).array();
+    }
+
+    public static byte[] toByteArray(byte byteInt) {
+        return new byte[]{byteInt};
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/cayenne/blob/fa605bf3/cayenne-crypto/src/test/resources/datamap.map.xml
----------------------------------------------------------------------
diff --git a/cayenne-crypto/src/test/resources/datamap.map.xml 
b/cayenne-crypto/src/test/resources/datamap.map.xml
index 2a20be2..2ea6a80 100644
--- a/cayenne-crypto/src/test/resources/datamap.map.xml
+++ b/cayenne-crypto/src/test/resources/datamap.map.xml
@@ -5,8 +5,10 @@
         project-version="8">
        <property name="defaultPackage" value="org.apache.cayenne.crypto.db"/>
        <db-entity name="TABLE1">
+               <db-attribute name="CRYPTO_INT" type="BLOB"/>
                <db-attribute name="CRYPTO_STRING" type="CLOB"/>
                <db-attribute name="ID" type="INTEGER" isPrimaryKey="true" 
isMandatory="true"/>
+               <db-attribute name="PLAIN_INT" type="INTEGER"/>
                <db-attribute name="PLAIN_STRING" type="VARCHAR" length="200"/>
        </db-entity>
        <db-entity name="TABLE2">
@@ -19,7 +21,9 @@
                <db-attribute name="ID" type="INTEGER" isPrimaryKey="true" 
isMandatory="true"/>
        </db-entity>
        <obj-entity name="Table1" 
className="org.apache.cayenne.crypto.db.Table1" dbEntityName="TABLE1">
+               <obj-attribute name="cryptoInt" type="int" 
db-attribute-path="CRYPTO_INT"/>
                <obj-attribute name="cryptoString" type="java.lang.String" 
db-attribute-path="CRYPTO_STRING"/>
+               <obj-attribute name="plainInt" type="int" 
db-attribute-path="PLAIN_INT"/>
                <obj-attribute name="plainString" type="java.lang.String" 
db-attribute-path="PLAIN_STRING"/>
        </obj-entity>
        <obj-entity name="Table2" 
className="org.apache.cayenne.crypto.db.Table2" dbEntityName="TABLE2">

http://git-wip-us.apache.org/repos/asf/cayenne/blob/fa605bf3/cayenne-server/src/main/java/org/apache/cayenne/dba/TypesMapping.java
----------------------------------------------------------------------
diff --git 
a/cayenne-server/src/main/java/org/apache/cayenne/dba/TypesMapping.java 
b/cayenne-server/src/main/java/org/apache/cayenne/dba/TypesMapping.java
index 161d2c1..1eed22a 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/dba/TypesMapping.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/dba/TypesMapping.java
@@ -19,6 +19,21 @@
 
 package org.apache.cayenne.dba;
 
+import org.apache.cayenne.util.Util;
+
+import java.io.Serializable;
+import java.math.BigInteger;
+import java.sql.DatabaseMetaData;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Types;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
 import static java.sql.Types.ARRAY;
 import static java.sql.Types.BIGINT;
 import static java.sql.Types.BINARY;
@@ -50,20 +65,6 @@ import static java.sql.Types.TINYINT;
 import static java.sql.Types.VARBINARY;
 import static java.sql.Types.VARCHAR;
 
-import java.io.Serializable;
-import java.math.BigInteger;
-import java.sql.DatabaseMetaData;
-import java.sql.ResultSet;
-import java.sql.SQLException;
-import java.util.ArrayList;
-import java.util.Calendar;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import org.apache.cayenne.util.Util;
-
 /**
  * A utility class that handles mappings of JDBC data types to the database
  * types and Java types. Also contains methods that provide information about
@@ -280,6 +281,30 @@ public class TypesMapping {
                return JdbcAdapter.supportsLength(type);
        }
 
+
+       // TODO: redo all isXyz as an internal enum over types, where each enum 
object knows whether it is this or that kind
+
+       /**
+        * Returns true if supplied type is a character type.
+        * @since 4.0
+        * @param type JDBC type
+        * @return true if supplied type is a character type.
+        */
+       public static boolean isCharacter(int type) {
+               return type == Types.CHAR || type == Types.NCHAR || type == 
Types.VARCHAR || type == Types.NVARCHAR
+                               || type == Types.CLOB || type == Types.NCLOB || 
type == Types.LONGVARCHAR || type == Types.LONGNVARCHAR;
+       }
+
+       /**
+        * Returns true if supplied type is a binary type.
+        * @since 4.0
+        * @param type JDBC type
+        * @return true if supplied type is a binary type.
+        */
+       public static boolean isBinary(int type) {
+               return type == Types.BINARY || type == Types.BLOB || type == 
Types.VARBINARY || type == Types.LONGVARBINARY;
+       }
+
        /**
         * Returns true if supplied type is a numeric type.
         */

http://git-wip-us.apache.org/repos/asf/cayenne/blob/fa605bf3/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 8889bbb..b8054b3 100644
--- a/docs/doc/src/main/resources/RELEASE-NOTES.txt
+++ b/docs/doc/src/main/resources/RELEASE-NOTES.txt
@@ -26,6 +26,7 @@ CAY-2083 Implement Protostuff as serialization service for 
Cayenne ROP
 CAY-2090 Untangle HttpRemoteService from ServiceContext thread local setup
 CAY-2100 Add supporting generated keys for PostgreSQL
 CAY-2102 EJBQL: db: path not supported in select columns
+CAY-2103 cayenne-crypto: support for mapping non-String and non-binary types
 
 Bug Fixes:
 

Reply via email to