You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cayenne.apache.org by sk...@apache.org on 2015/05/06 15:35:30 UTC

cayenne git commit: CAY-1626 | Add JodaTime DateTime support

Repository: cayenne
Updated Branches:
  refs/heads/master bd38bad4a -> 6d7bc02dc


CAY-1626 | Add JodaTime DateTime support


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

Branch: refs/heads/master
Commit: 6d7bc02dc06b33c91c1ee769b1e7344141df72a4
Parents: bd38bad
Author: Savva Kolbachev <s....@gmail.com>
Authored: Wed May 6 16:32:03 2015 +0300
Committer: Savva Kolbachev <s....@gmail.com>
Committed: Wed May 6 16:32:03 2015 +0300

----------------------------------------------------------------------
 cayenne-joda/pom.xml                            | 102 +++++++++++++++
 .../org/apache/cayenne/CayenneJodaModule.java   |  48 +++++++
 .../cayenne/access/types/DateTimeType.java      |  79 +++++++++++
 .../cayenne/access/types/LocalDateTimeType.java |  80 +++++++++++
 .../cayenne/access/types/LocalDateType.java     |  88 +++++++++++++
 .../cayenne/access/types/LocalTimeType.java     |  92 +++++++++++++
 .../apache/cayenne/joda/DateTimeTypeTest.java   |  62 +++++++++
 .../org/apache/cayenne/joda/JodaTestCase.java   |  40 ++++++
 .../org/apache/cayenne/joda/JodaTimeIT.java     | 131 +++++++++++++++++++
 .../cayenne/joda/LocalDateTimeTypeTest.java     |  64 +++++++++
 .../apache/cayenne/joda/LocalDateTypeTest.java  |  76 +++++++++++
 .../apache/cayenne/joda/LocalTimeTypeTest.java  |  76 +++++++++++
 .../cayenne/joda/db/DateTimeTestEntity.java     |   9 ++
 .../cayenne/joda/db/LocalDateTestEntity.java    |   9 ++
 .../joda/db/LocalDateTimeTestEntity.java        |   9 ++
 .../cayenne/joda/db/LocalTimeTestEntity.java    |   9 ++
 .../joda/db/auto/_DateTimeTestEntity.java       |  28 ++++
 .../joda/db/auto/_LocalDateTestEntity.java      |  28 ++++
 .../joda/db/auto/_LocalDateTimeTestEntity.java  |  28 ++++
 .../joda/db/auto/_LocalTimeTestEntity.java      |  28 ++++
 .../src/test/resources/cayenne-joda.xml         |  17 +++
 cayenne-joda/src/test/resources/joda.map.xml    |  35 +++++
 docs/doc/src/main/resources/RELEASE-NOTES.txt   |   1 +
 pom.xml                                         |   3 +-
 24 files changed, 1141 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cayenne/blob/6d7bc02d/cayenne-joda/pom.xml
----------------------------------------------------------------------
diff --git a/cayenne-joda/pom.xml b/cayenne-joda/pom.xml
new file mode 100644
index 0000000..17c2df1
--- /dev/null
+++ b/cayenne-joda/pom.xml
@@ -0,0 +1,102 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 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. -->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <artifactId>cayenne-parent</artifactId>
+        <groupId>org.apache.cayenne</groupId>
+        <version>4.0.M3-SNAPSHOT</version>
+    </parent>
+    <artifactId>cayenne-joda</artifactId>
+    <name>Cayenne Joda Extensions</name>
+    <packaging>jar</packaging>
+
+    <dependencies>
+
+        <!-- Compile dependencies -->
+        <dependency>
+            <groupId>org.apache.cayenne</groupId>
+            <artifactId>cayenne-server</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>joda-time</groupId>
+            <artifactId>joda-time</artifactId>
+            <version>2.7</version>
+            <scope>provided</scope>
+        </dependency>
+
+        <!-- Test dependencies -->
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.cayenne.build-tools</groupId>
+            <artifactId>cayenne-test-utilities</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>mockrunner</groupId>
+            <artifactId>mockrunner</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>hsqldb</groupId>
+            <artifactId>hsqldb</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+    <build>
+        <plugins>
+            <plugin>
+                <artifactId>maven-remote-resources-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>process</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.cayenne.plugins</groupId>
+                <artifactId>maven-cayenne-plugin</artifactId>
+                <version>${project.version}</version>
+                <configuration>
+                    <map>${project.basedir}/src/test/resources/joda.map.xml</map>
+                    <destDir>${project.basedir}/src/test/java</destDir>
+                    <defaultPackage>org.apache.cayenne.joda.db</defaultPackage>
+                    <superPkg>org.apache.cayenne.joda.db.auto</superPkg>
+                </configuration>
+            </plugin>
+            <plugin>
+                <artifactId>maven-checkstyle-plugin</artifactId>
+                <!--<configuration>
+                    <suppressionsLocation>${project.basedir}/cayenne-checkstyle-suppression.xml</suppressionsLocation>
+                </configuration>-->
+            </plugin>
+            <plugin>
+                <artifactId>maven-pmd-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-failsafe-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+</project>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cayenne/blob/6d7bc02d/cayenne-joda/src/main/java/org/apache/cayenne/CayenneJodaModule.java
----------------------------------------------------------------------
diff --git a/cayenne-joda/src/main/java/org/apache/cayenne/CayenneJodaModule.java b/cayenne-joda/src/main/java/org/apache/cayenne/CayenneJodaModule.java
new file mode 100644
index 0000000..a481ea3
--- /dev/null
+++ b/cayenne-joda/src/main/java/org/apache/cayenne/CayenneJodaModule.java
@@ -0,0 +1,48 @@
+package org.apache.cayenne; /**
+ * **************************************************************
+ * 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
+ * <p/>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p/>
+ * 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.
+ * **************************************************************
+ */
+
+import org.apache.cayenne.access.types.DateTimeType;
+import org.apache.cayenne.access.types.LocalDateTimeType;
+import org.apache.cayenne.access.types.LocalDateType;
+import org.apache.cayenne.access.types.LocalTimeType;
+import org.apache.cayenne.configuration.Constants;
+import org.apache.cayenne.di.Binder;
+import org.apache.cayenne.di.Module;
+
+/**
+ * Include this module when creating a ServerRuntime in order to add
+ + support for joda-time ObjAttributes.
+ */
+public class CayenneJodaModule implements Module {
+
+    public CayenneJodaModule() {
+    }
+
+    @Override
+    public void configure(Binder binder) {
+        binder
+                .bindList(Constants.SERVER_DEFAULT_TYPES_LIST)
+                .add(new DateTimeType())
+                .add(new LocalDateType())
+                .add(new LocalTimeType())
+                .add(new LocalDateTimeType());
+    }
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/6d7bc02d/cayenne-joda/src/main/java/org/apache/cayenne/access/types/DateTimeType.java
----------------------------------------------------------------------
diff --git a/cayenne-joda/src/main/java/org/apache/cayenne/access/types/DateTimeType.java b/cayenne-joda/src/main/java/org/apache/cayenne/access/types/DateTimeType.java
new file mode 100644
index 0000000..77d5f46
--- /dev/null
+++ b/cayenne-joda/src/main/java/org/apache/cayenne/access/types/DateTimeType.java
@@ -0,0 +1,79 @@
+/*****************************************************************
+ *   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 org.joda.time.DateTime;
+
+import java.sql.CallableStatement;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.Timestamp;
+
+/**
+ * Handles <code>org.joda.time.DateTime</code> type mapping.
+ */
+public class DateTimeType implements ExtendedType {
+
+    @Override
+    public String getClassName() {
+        return DateTime.class.getName();
+    }
+
+    @Override
+    public DateTime materializeObject(ResultSet rs, int index, int type)
+            throws Exception {
+        if (rs.getTimestamp(index) != null) {
+            return new DateTime(rs.getTimestamp(index).getTime());
+        } else {
+            return null;
+        }
+    }
+
+    @Override
+    public DateTime materializeObject(CallableStatement rs, int index, int type)
+            throws Exception {
+        if (rs.getTimestamp(index) != null) {
+            return new DateTime(rs.getTimestamp(index).getTime());
+        } else {
+            return null;
+        }
+    }
+
+    @Override
+    public void setJdbcObject(
+            PreparedStatement statement,
+            Object value,
+            int pos,
+            int type,
+            int scale) throws Exception {
+
+        if (value == null) {
+            statement.setNull(pos, type);
+        } else {
+            Timestamp ts = new Timestamp(getMillis(value));
+            statement.setTimestamp(pos, ts);
+        }
+    }
+
+    protected long getMillis(Object value) {
+        return ((DateTime) value).getMillis();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/6d7bc02d/cayenne-joda/src/main/java/org/apache/cayenne/access/types/LocalDateTimeType.java
----------------------------------------------------------------------
diff --git a/cayenne-joda/src/main/java/org/apache/cayenne/access/types/LocalDateTimeType.java b/cayenne-joda/src/main/java/org/apache/cayenne/access/types/LocalDateTimeType.java
new file mode 100644
index 0000000..7f6b197
--- /dev/null
+++ b/cayenne-joda/src/main/java/org/apache/cayenne/access/types/LocalDateTimeType.java
@@ -0,0 +1,80 @@
+/*****************************************************************
+ *   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 org.joda.time.LocalDateTime;
+
+import java.sql.CallableStatement;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.Timestamp;
+import java.sql.Types;
+
+/**
+ * Handles <code>org.joda.time.LocalDateTime</code> type mapping.
+ */
+public class LocalDateTimeType implements ExtendedType {
+
+    @Override
+    public String getClassName() {
+        return LocalDateTime.class.getName();
+    }
+
+    @Override
+    public LocalDateTime materializeObject(ResultSet rs, int index, int type)
+            throws Exception {
+        if (rs.getTimestamp(index) != null) {
+            return new LocalDateTime(rs.getTimestamp(index).getTime());
+        } else {
+            return null;
+        }
+    }
+
+    @Override
+    public LocalDateTime materializeObject(CallableStatement rs, int index, int type)
+            throws Exception {
+        if (type == Types.TIMESTAMP && rs.getTimestamp(index) != null) {
+            return new LocalDateTime(rs.getTimestamp(index).getTime());
+        } else {
+            return null;
+        }
+    }
+
+    @Override
+    public void setJdbcObject(
+            PreparedStatement statement,
+            Object value,
+            int pos,
+            int type,
+            int scale) throws Exception {
+
+        if (value == null) {
+            statement.setNull(pos, type);
+        } else {
+            Timestamp ts = new Timestamp(getMillis(value));
+            statement.setTimestamp(pos, ts);
+        }
+    }
+
+    protected long getMillis(Object value) {
+        return ((LocalDateTime) value).toDateTime().getMillis();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/6d7bc02d/cayenne-joda/src/main/java/org/apache/cayenne/access/types/LocalDateType.java
----------------------------------------------------------------------
diff --git a/cayenne-joda/src/main/java/org/apache/cayenne/access/types/LocalDateType.java b/cayenne-joda/src/main/java/org/apache/cayenne/access/types/LocalDateType.java
new file mode 100644
index 0000000..3635365
--- /dev/null
+++ b/cayenne-joda/src/main/java/org/apache/cayenne/access/types/LocalDateType.java
@@ -0,0 +1,88 @@
+/*****************************************************************
+ *   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 org.joda.time.LocalDate;
+
+import java.sql.CallableStatement;
+import java.sql.Date;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.Timestamp;
+import java.sql.Types;
+
+/**
+ * Handles <code>org.joda.time.LocalDate</code> type mapping.
+ */
+public class LocalDateType implements ExtendedType {
+
+    @Override
+    public String getClassName() {
+        return LocalDate.class.getName();
+    }
+
+    @Override
+    public LocalDate materializeObject(ResultSet rs, int index, int type)
+            throws Exception {
+        if (type == Types.DATE && rs.getDate(index) != null) {
+            return new LocalDate(rs.getDate(index).getTime());
+        } else if (type == Types.TIMESTAMP && rs.getTimestamp(index) != null) {
+            return new LocalDate(rs.getTimestamp(index).getTime());
+        } else {
+            return null;
+        }
+    }
+
+    @Override
+    public LocalDate materializeObject(CallableStatement rs, int index, int type)
+            throws Exception {
+        if (type == Types.DATE && rs.getDate(index) != null) {
+            return new LocalDate(rs.getDate(index).getTime());
+        } else if (type == Types.TIMESTAMP && rs.getTimestamp(index) != null) {
+            return new LocalDate(rs.getTimestamp(index).getTime());
+        } else {
+            return null;
+        }
+    }
+
+    @Override
+    public void setJdbcObject(
+            PreparedStatement statement,
+            Object value,
+            int pos,
+            int type,
+            int scale) throws Exception {
+
+        if (value == null) {
+            statement.setNull(pos, type);
+        } else {
+            if (type == Types.DATE) {
+                statement.setDate(pos, new Date(getMillis(value)));
+            } else {
+                statement.setTimestamp(pos, new Timestamp(getMillis(value)));
+            }
+        }
+    }
+
+    protected long getMillis(Object value) {
+        return ((LocalDate) value).toDate().getTime();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/6d7bc02d/cayenne-joda/src/main/java/org/apache/cayenne/access/types/LocalTimeType.java
----------------------------------------------------------------------
diff --git a/cayenne-joda/src/main/java/org/apache/cayenne/access/types/LocalTimeType.java b/cayenne-joda/src/main/java/org/apache/cayenne/access/types/LocalTimeType.java
new file mode 100644
index 0000000..1e152ef
--- /dev/null
+++ b/cayenne-joda/src/main/java/org/apache/cayenne/access/types/LocalTimeType.java
@@ -0,0 +1,92 @@
+/*****************************************************************
+ *   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 org.joda.time.DateTimeZone;
+import org.joda.time.LocalDate;
+import org.joda.time.LocalTime;
+
+import java.sql.CallableStatement;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.Time;
+import java.sql.Timestamp;
+import java.sql.Types;
+
+/**
+ * Handles <code>org.joda.time.LocalTime</code> type mapping.
+ */
+public class LocalTimeType implements ExtendedType {
+
+    private static final LocalDate EPOCH = new LocalDate(0, DateTimeZone.UTC);
+
+    @Override
+    public String getClassName() {
+        return LocalTime.class.getName();
+    }
+
+    @Override
+    public LocalTime materializeObject(ResultSet rs, int index, int type)
+            throws Exception {
+        if (type == Types.TIME && rs.getTime(index) != null) {
+            return new LocalTime(rs.getTime(index).getTime());
+        } else if (type == Types.TIMESTAMP && rs.getTimestamp(index) != null) {
+            return new LocalTime(rs.getTimestamp(index).getTime());
+        } else {
+            return null;
+        }
+    }
+
+    @Override
+    public LocalTime materializeObject(CallableStatement rs, int index, int type)
+            throws Exception {
+        if (type == Types.TIME && rs.getTime(index) != null) {
+            return new LocalTime(rs.getTime(index).getTime());
+        } else if (type == Types.TIMESTAMP && rs.getTimestamp(index) != null) {
+            return new LocalTime(rs.getTimestamp(index).getTime());
+        } else {
+            return null;
+        }
+    }
+
+    @Override
+    public void setJdbcObject(
+            PreparedStatement statement,
+            Object value,
+            int pos,
+            int type,
+            int scale) throws Exception {
+
+        if (value == null) {
+            statement.setNull(pos, type);
+        } else {
+            if (type == Types.TIME) {
+                statement.setTime(pos, new Time(getMillis(value)));
+            } else {
+                statement.setTimestamp(pos, new Timestamp(getMillis(value)));
+            }
+        }
+    }
+
+    protected long getMillis(Object value) {
+        return EPOCH.toDateTime((LocalTime) value).getMillis();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/6d7bc02d/cayenne-joda/src/test/java/org/apache/cayenne/joda/DateTimeTypeTest.java
----------------------------------------------------------------------
diff --git a/cayenne-joda/src/test/java/org/apache/cayenne/joda/DateTimeTypeTest.java b/cayenne-joda/src/test/java/org/apache/cayenne/joda/DateTimeTypeTest.java
new file mode 100644
index 0000000..97be842
--- /dev/null
+++ b/cayenne-joda/src/test/java/org/apache/cayenne/joda/DateTimeTypeTest.java
@@ -0,0 +1,62 @@
+package org.apache.cayenne.joda;
+
+import com.mockrunner.mock.jdbc.MockConnection;
+import com.mockrunner.mock.jdbc.MockPreparedStatement;
+import org.apache.cayenne.access.types.DateTimeType;
+import org.joda.time.DateTime;
+
+import java.sql.PreparedStatement;
+import java.sql.Timestamp;
+import java.sql.Types;
+
+/*****************************************************************
+*   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.
+****************************************************************/
+
+public class DateTimeTypeTest extends JodaTestCase {
+
+	private DateTimeType type;
+
+	@Override
+	protected void setUp() throws Exception {
+		super.setUp();
+		type = new DateTimeType();
+	}
+
+	public void testMaterializeObjectTimestamp() throws Exception {
+		Object o = type.materializeObject(resultSet(new Timestamp(0)), 1, Types.TIMESTAMP);
+		assertEquals(new DateTime(0), o);
+	}
+
+	public void testSetJdbcObject() throws Exception {
+		PreparedStatement statement = new MockPreparedStatement(new MockConnection(), "update t set c = ?");
+		DateTime date = new DateTime(0);
+
+		type.setJdbcObject(statement, date, 1, Types.TIMESTAMP, 0);
+
+        Object object = ((MockPreparedStatement) statement).getParameter(1);
+        assertEquals(Timestamp.class, object.getClass());
+        assertEquals(date.getMillis(), ((Timestamp) object).getTime());
+
+		type.setJdbcObject(statement, null, 1, Types.TIMESTAMP, 0);
+
+        object = ((MockPreparedStatement) statement).getParameter(1);
+        assertNull(object);
+	}
+
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cayenne/blob/6d7bc02d/cayenne-joda/src/test/java/org/apache/cayenne/joda/JodaTestCase.java
----------------------------------------------------------------------
diff --git a/cayenne-joda/src/test/java/org/apache/cayenne/joda/JodaTestCase.java b/cayenne-joda/src/test/java/org/apache/cayenne/joda/JodaTestCase.java
new file mode 100644
index 0000000..5253aa3
--- /dev/null
+++ b/cayenne-joda/src/test/java/org/apache/cayenne/joda/JodaTestCase.java
@@ -0,0 +1,40 @@
+package org.apache.cayenne.joda;
+
+import com.mockrunner.mock.jdbc.MockResultSet;
+import junit.framework.TestCase;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+
+/**
+ * **************************************************************
+ * 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
+ * <p/>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p/>
+ * 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.
+ * **************************************************************
+ */
+
+public abstract class JodaTestCase extends TestCase {
+
+    ResultSet resultSet(Object value) throws SQLException {
+        MockResultSet rs = new MockResultSet("Test");
+        rs.addColumn("Col");
+        rs.addRow(new Object[]{value});
+        rs.next();
+        return rs;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/6d7bc02d/cayenne-joda/src/test/java/org/apache/cayenne/joda/JodaTimeIT.java
----------------------------------------------------------------------
diff --git a/cayenne-joda/src/test/java/org/apache/cayenne/joda/JodaTimeIT.java b/cayenne-joda/src/test/java/org/apache/cayenne/joda/JodaTimeIT.java
new file mode 100644
index 0000000..e360a9a
--- /dev/null
+++ b/cayenne-joda/src/test/java/org/apache/cayenne/joda/JodaTimeIT.java
@@ -0,0 +1,131 @@
+/*****************************************************************
+ *   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.joda;
+
+import org.apache.cayenne.CayenneJodaModule;
+import org.apache.cayenne.ObjectContext;
+import org.apache.cayenne.configuration.server.ServerRuntime;
+import org.apache.cayenne.di.Module;
+import org.apache.cayenne.joda.db.DateTimeTestEntity;
+import org.apache.cayenne.joda.db.LocalDateTestEntity;
+import org.apache.cayenne.joda.db.LocalDateTimeTestEntity;
+import org.apache.cayenne.joda.db.LocalTimeTestEntity;
+import org.apache.cayenne.query.SelectQuery;
+import org.joda.time.DateTime;
+import org.joda.time.LocalDate;
+import org.joda.time.LocalDateTime;
+import org.joda.time.LocalTime;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.sql.SQLException;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+public class JodaTimeIT {
+
+    private ServerRuntime runtime;
+
+    @Before
+    public void setUp() throws Exception {
+        Module jodaModule = new CayenneJodaModule();
+        this.runtime = new ServerRuntime("cayenne-joda.xml", jodaModule);
+    }
+
+    @Test
+    public void testJodaDateTime() throws SQLException {
+        ObjectContext context = runtime.newContext();
+
+        DateTimeTestEntity dateTimeTestEntity = context.newObject(DateTimeTestEntity.class);
+        DateTime dateTime = DateTime.now();
+        dateTimeTestEntity.setTimestamp(dateTime);
+
+        context.commitChanges();
+
+        SelectQuery q = new SelectQuery(DateTimeTestEntity.class);
+        DateTimeTestEntity testRead = (DateTimeTestEntity) context.performQuery(q).get(0);
+
+        DateTime timestamp = testRead.getTimestamp();
+        assertNotNull(timestamp);
+        assertEquals(DateTime.class, timestamp.getClass());
+        assertEquals(dateTime, timestamp);
+    }
+
+    @Test
+    public void testJodaLocalDate() {
+        ObjectContext context = runtime.newContext();
+
+        LocalDateTestEntity localDateTestEntity = context.newObject(LocalDateTestEntity.class);
+        LocalDate localDate = LocalDate.now();
+        localDateTestEntity.setDate(localDate);
+
+        context.commitChanges();
+
+        SelectQuery q = new SelectQuery(LocalDateTestEntity.class);
+        LocalDateTestEntity testRead = (LocalDateTestEntity) context.performQuery(q).get(0);
+
+        LocalDate date = testRead.getDate();
+        assertNotNull(date);
+        assertEquals(LocalDate.class, date.getClass());
+        assertEquals(localDate, date);
+    }
+
+    @Test
+    public void testJodaLocalTime() {
+        ObjectContext context = runtime.newContext();
+
+        LocalTimeTestEntity localTimeTestEntity = context.newObject(LocalTimeTestEntity.class);
+        LocalTime localTime = LocalTime.now();
+        localTimeTestEntity.setTime(localTime);
+
+        context.commitChanges();
+
+        SelectQuery q = new SelectQuery(LocalTimeTestEntity.class);
+        LocalTimeTestEntity testRead = (LocalTimeTestEntity) context.performQuery(q).get(0);
+
+        LocalTime time = testRead.getTime();
+        assertNotNull(time);
+        assertEquals(LocalTime.class, time.getClass());
+        assertEquals(localTime.getSecondOfMinute(), time.getSecondOfMinute());
+        assertEquals(localTime.getMinuteOfHour(), time.getMinuteOfHour());
+        assertEquals(localTime.getHourOfDay(), time.getHourOfDay());
+    }
+
+    @Test
+    public void testJodaLocalDateTime() {
+        ObjectContext context = runtime.newContext();
+
+        LocalDateTimeTestEntity localDateTimeTestEntity = context.newObject(LocalDateTimeTestEntity.class);
+        LocalDateTime localDateTime = LocalDateTime.now();
+        localDateTimeTestEntity.setTimestamp(localDateTime);
+
+        context.commitChanges();
+
+        SelectQuery q = new SelectQuery(LocalDateTimeTestEntity.class);
+        LocalDateTimeTestEntity testRead = (LocalDateTimeTestEntity) context.performQuery(q).get(0);
+
+        LocalDateTime timestamp = testRead.getTimestamp();
+        assertNotNull(timestamp);
+        assertEquals(LocalDateTime.class, timestamp.getClass());
+        assertEquals(localDateTime, timestamp);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/6d7bc02d/cayenne-joda/src/test/java/org/apache/cayenne/joda/LocalDateTimeTypeTest.java
----------------------------------------------------------------------
diff --git a/cayenne-joda/src/test/java/org/apache/cayenne/joda/LocalDateTimeTypeTest.java b/cayenne-joda/src/test/java/org/apache/cayenne/joda/LocalDateTimeTypeTest.java
new file mode 100644
index 0000000..12369c8
--- /dev/null
+++ b/cayenne-joda/src/test/java/org/apache/cayenne/joda/LocalDateTimeTypeTest.java
@@ -0,0 +1,64 @@
+package org.apache.cayenne.joda;
+
+import com.mockrunner.mock.jdbc.MockConnection;
+import com.mockrunner.mock.jdbc.MockPreparedStatement;
+import org.apache.cayenne.access.types.LocalDateTimeType;
+import org.joda.time.LocalDateTime;
+
+import java.sql.PreparedStatement;
+import java.sql.Timestamp;
+import java.sql.Types;
+
+/**
+ * **************************************************************
+ * 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
+ * <p/>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p/>
+ * 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.
+ * **************************************************************
+ */
+
+public class LocalDateTimeTypeTest extends JodaTestCase {
+
+    private LocalDateTimeType type;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        type = new LocalDateTimeType();
+    }
+
+    public void testMaterializeObjectTimestamp() throws Exception {
+        Object o = type.materializeObject(resultSet(new Timestamp(0)), 1, Types.TIMESTAMP);
+        assertEquals(new LocalDateTime(0), o);
+    }
+
+    public void testSetJdbcObject() throws Exception {
+        PreparedStatement statement = new MockPreparedStatement(new MockConnection(), "update t set c = ?");
+        LocalDateTime date = new LocalDateTime(0);
+
+        type.setJdbcObject(statement, date, 1, Types.TIMESTAMP, 0);
+
+        Object object = ((MockPreparedStatement) statement).getParameter(1);
+        assertEquals(Timestamp.class, object.getClass());
+        assertEquals(date.toDate().getTime(), ((Timestamp) object).getTime());
+
+        type.setJdbcObject(statement, null, 1, Types.TIMESTAMP, 0);
+
+        object = ((MockPreparedStatement) statement).getParameter(1);
+        assertNull(object);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/6d7bc02d/cayenne-joda/src/test/java/org/apache/cayenne/joda/LocalDateTypeTest.java
----------------------------------------------------------------------
diff --git a/cayenne-joda/src/test/java/org/apache/cayenne/joda/LocalDateTypeTest.java b/cayenne-joda/src/test/java/org/apache/cayenne/joda/LocalDateTypeTest.java
new file mode 100644
index 0000000..c91ebc3
--- /dev/null
+++ b/cayenne-joda/src/test/java/org/apache/cayenne/joda/LocalDateTypeTest.java
@@ -0,0 +1,76 @@
+package org.apache.cayenne.joda;
+
+import org.apache.cayenne.access.types.LocalDateType;
+import com.mockrunner.mock.jdbc.MockConnection;
+import com.mockrunner.mock.jdbc.MockPreparedStatement;
+import org.joda.time.LocalDate;
+
+import java.sql.Date;
+import java.sql.PreparedStatement;
+import java.sql.Timestamp;
+import java.sql.Types;
+
+/**
+ * **************************************************************
+ * 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
+ * <p/>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p/>
+ * 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.
+ * **************************************************************
+ */
+
+public class LocalDateTypeTest extends JodaTestCase {
+
+    private LocalDateType type;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        type = new LocalDateType();
+    }
+
+    public void testMaterializeObjectTimestamp() throws Exception {
+        Object o = type.materializeObject(resultSet(new Timestamp(0)), 1, Types.TIMESTAMP);
+        assertEquals(new LocalDate(0), o);
+    }
+
+    public void testMaterializeObjectDate() throws Exception {
+        Object o = type.materializeObject(resultSet(new Date(0)), 1, Types.DATE);
+        assertEquals(new LocalDate(0), o);
+    }
+
+    public void testSetJdbcObject() throws Exception {
+        PreparedStatement statement = new MockPreparedStatement(new MockConnection(), "update t set c = ?");
+        LocalDate savedObject = new LocalDate(0);
+
+        type.setJdbcObject(statement, savedObject, 1, Types.DATE, 0);
+
+        Object object = ((MockPreparedStatement) statement).getParameter(1);
+        assertEquals(Date.class, object.getClass());
+        assertEquals(savedObject.toDate().getTime(), ((Date) object).getTime());
+
+        type.setJdbcObject(statement, savedObject, 1, Types.TIMESTAMP, 0);
+
+        object = ((MockPreparedStatement) statement).getParameter(1);
+        assertEquals(Timestamp.class, object.getClass());
+        assertEquals(savedObject.toDate().getTime(), ((Timestamp) object).getTime());
+
+        type.setJdbcObject(statement, null, 1, Types.TIMESTAMP, 0);
+
+        object = ((MockPreparedStatement) statement).getParameter(1);
+        assertNull(object);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/6d7bc02d/cayenne-joda/src/test/java/org/apache/cayenne/joda/LocalTimeTypeTest.java
----------------------------------------------------------------------
diff --git a/cayenne-joda/src/test/java/org/apache/cayenne/joda/LocalTimeTypeTest.java b/cayenne-joda/src/test/java/org/apache/cayenne/joda/LocalTimeTypeTest.java
new file mode 100644
index 0000000..fc7b89f
--- /dev/null
+++ b/cayenne-joda/src/test/java/org/apache/cayenne/joda/LocalTimeTypeTest.java
@@ -0,0 +1,76 @@
+package org.apache.cayenne.joda;
+
+import org.apache.cayenne.access.types.LocalTimeType;
+import com.mockrunner.mock.jdbc.MockConnection;
+import com.mockrunner.mock.jdbc.MockPreparedStatement;
+import org.joda.time.DateTimeZone;
+import org.joda.time.LocalDate;
+import org.joda.time.LocalTime;
+
+import java.sql.PreparedStatement;
+import java.sql.Time;
+import java.sql.Timestamp;
+import java.sql.Types;
+
+/*****************************************************************
+*   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.
+****************************************************************/
+
+public class LocalTimeTypeTest extends JodaTestCase {
+
+	private LocalTimeType type;
+
+	@Override
+	protected void setUp() throws Exception {
+		super.setUp();
+		type = new LocalTimeType();
+	}
+
+	public void testMaterializeObjectTimestamp() throws Exception {
+		Object o = type.materializeObject(resultSet(new Timestamp(0)), 1, Types.TIMESTAMP);
+		assertEquals(new LocalTime(0), o);
+	}
+
+	public void testMaterializeObjectTime() throws Exception {
+		Object o = type.materializeObject(resultSet(new Time(0)), 1, Types.TIME);
+		assertEquals(new LocalTime(0), o);
+	}
+
+	public void testSetJdbcObject() throws Exception {
+		PreparedStatement statement = new MockPreparedStatement(new MockConnection(), "update t set c = ?");
+		LocalTime date = new LocalTime(0);
+
+		type.setJdbcObject(statement, date, 1, Types.TIME, 0);
+
+        Object object = ((MockPreparedStatement) statement).getParameter(1);
+        assertEquals(Time.class, object.getClass());
+        assertEquals(new LocalDate(0, DateTimeZone.UTC).toDateTime(date).getMillis(), ((Time) object).getTime());
+
+		type.setJdbcObject(statement, date, 1, Types.TIMESTAMP, 0);
+
+        object = ((MockPreparedStatement) statement).getParameter(1);
+        assertEquals(Timestamp.class, object.getClass());
+        assertEquals(new LocalDate(0, DateTimeZone.UTC).toDateTime(date).getMillis(), ((Timestamp) object).getTime());
+
+		type.setJdbcObject(statement, null, 1, Types.TIMESTAMP, 0);
+
+        object = ((MockPreparedStatement) statement).getParameter(1);
+        assertNull(object);
+	}
+
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cayenne/blob/6d7bc02d/cayenne-joda/src/test/java/org/apache/cayenne/joda/db/DateTimeTestEntity.java
----------------------------------------------------------------------
diff --git a/cayenne-joda/src/test/java/org/apache/cayenne/joda/db/DateTimeTestEntity.java b/cayenne-joda/src/test/java/org/apache/cayenne/joda/db/DateTimeTestEntity.java
new file mode 100644
index 0000000..2fc52d0
--- /dev/null
+++ b/cayenne-joda/src/test/java/org/apache/cayenne/joda/db/DateTimeTestEntity.java
@@ -0,0 +1,9 @@
+package org.apache.cayenne.joda.db;
+
+import org.apache.cayenne.joda.db.auto._DateTimeTestEntity;
+
+public class DateTimeTestEntity extends _DateTimeTestEntity {
+
+    private static final long serialVersionUID = 1L; 
+
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/6d7bc02d/cayenne-joda/src/test/java/org/apache/cayenne/joda/db/LocalDateTestEntity.java
----------------------------------------------------------------------
diff --git a/cayenne-joda/src/test/java/org/apache/cayenne/joda/db/LocalDateTestEntity.java b/cayenne-joda/src/test/java/org/apache/cayenne/joda/db/LocalDateTestEntity.java
new file mode 100644
index 0000000..9149e56
--- /dev/null
+++ b/cayenne-joda/src/test/java/org/apache/cayenne/joda/db/LocalDateTestEntity.java
@@ -0,0 +1,9 @@
+package org.apache.cayenne.joda.db;
+
+import org.apache.cayenne.joda.db.auto._LocalDateTestEntity;
+
+public class LocalDateTestEntity extends _LocalDateTestEntity {
+
+    private static final long serialVersionUID = 1L; 
+
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/6d7bc02d/cayenne-joda/src/test/java/org/apache/cayenne/joda/db/LocalDateTimeTestEntity.java
----------------------------------------------------------------------
diff --git a/cayenne-joda/src/test/java/org/apache/cayenne/joda/db/LocalDateTimeTestEntity.java b/cayenne-joda/src/test/java/org/apache/cayenne/joda/db/LocalDateTimeTestEntity.java
new file mode 100644
index 0000000..794cb95
--- /dev/null
+++ b/cayenne-joda/src/test/java/org/apache/cayenne/joda/db/LocalDateTimeTestEntity.java
@@ -0,0 +1,9 @@
+package org.apache.cayenne.joda.db;
+
+import org.apache.cayenne.joda.db.auto._LocalDateTimeTestEntity;
+
+public class LocalDateTimeTestEntity extends _LocalDateTimeTestEntity {
+
+    private static final long serialVersionUID = 1L; 
+
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/6d7bc02d/cayenne-joda/src/test/java/org/apache/cayenne/joda/db/LocalTimeTestEntity.java
----------------------------------------------------------------------
diff --git a/cayenne-joda/src/test/java/org/apache/cayenne/joda/db/LocalTimeTestEntity.java b/cayenne-joda/src/test/java/org/apache/cayenne/joda/db/LocalTimeTestEntity.java
new file mode 100644
index 0000000..46de23d
--- /dev/null
+++ b/cayenne-joda/src/test/java/org/apache/cayenne/joda/db/LocalTimeTestEntity.java
@@ -0,0 +1,9 @@
+package org.apache.cayenne.joda.db;
+
+import org.apache.cayenne.joda.db.auto._LocalTimeTestEntity;
+
+public class LocalTimeTestEntity extends _LocalTimeTestEntity {
+
+    private static final long serialVersionUID = 1L; 
+
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/6d7bc02d/cayenne-joda/src/test/java/org/apache/cayenne/joda/db/auto/_DateTimeTestEntity.java
----------------------------------------------------------------------
diff --git a/cayenne-joda/src/test/java/org/apache/cayenne/joda/db/auto/_DateTimeTestEntity.java b/cayenne-joda/src/test/java/org/apache/cayenne/joda/db/auto/_DateTimeTestEntity.java
new file mode 100644
index 0000000..611ba82
--- /dev/null
+++ b/cayenne-joda/src/test/java/org/apache/cayenne/joda/db/auto/_DateTimeTestEntity.java
@@ -0,0 +1,28 @@
+package org.apache.cayenne.joda.db.auto;
+
+import org.apache.cayenne.CayenneDataObject;
+import org.apache.cayenne.exp.Property;
+import org.joda.time.DateTime;
+
+/**
+ * Class _DateTimeTestEntity was generated by Cayenne.
+ * It is probably a good idea to avoid changing this class manually,
+ * since it may be overwritten next time code is regenerated.
+ * If you need to make any customizations, please use subclass.
+ */
+public abstract class _DateTimeTestEntity extends CayenneDataObject {
+
+    private static final long serialVersionUID = 1L; 
+
+    public static final String ID_PK_COLUMN = "ID";
+
+    public static final Property<DateTime> TIMESTAMP = new Property<DateTime>("timestamp");
+
+    public void setTimestamp(DateTime timestamp) {
+        writeProperty("timestamp", timestamp);
+    }
+    public DateTime getTimestamp() {
+        return (DateTime)readProperty("timestamp");
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/6d7bc02d/cayenne-joda/src/test/java/org/apache/cayenne/joda/db/auto/_LocalDateTestEntity.java
----------------------------------------------------------------------
diff --git a/cayenne-joda/src/test/java/org/apache/cayenne/joda/db/auto/_LocalDateTestEntity.java b/cayenne-joda/src/test/java/org/apache/cayenne/joda/db/auto/_LocalDateTestEntity.java
new file mode 100644
index 0000000..ac18b66
--- /dev/null
+++ b/cayenne-joda/src/test/java/org/apache/cayenne/joda/db/auto/_LocalDateTestEntity.java
@@ -0,0 +1,28 @@
+package org.apache.cayenne.joda.db.auto;
+
+import org.apache.cayenne.CayenneDataObject;
+import org.apache.cayenne.exp.Property;
+import org.joda.time.LocalDate;
+
+/**
+ * Class _LocalDateTestEntity was generated by Cayenne.
+ * It is probably a good idea to avoid changing this class manually,
+ * since it may be overwritten next time code is regenerated.
+ * If you need to make any customizations, please use subclass.
+ */
+public abstract class _LocalDateTestEntity extends CayenneDataObject {
+
+    private static final long serialVersionUID = 1L; 
+
+    public static final String ID_PK_COLUMN = "ID";
+
+    public static final Property<LocalDate> DATE = new Property<LocalDate>("date");
+
+    public void setDate(LocalDate date) {
+        writeProperty("date", date);
+    }
+    public LocalDate getDate() {
+        return (LocalDate)readProperty("date");
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/6d7bc02d/cayenne-joda/src/test/java/org/apache/cayenne/joda/db/auto/_LocalDateTimeTestEntity.java
----------------------------------------------------------------------
diff --git a/cayenne-joda/src/test/java/org/apache/cayenne/joda/db/auto/_LocalDateTimeTestEntity.java b/cayenne-joda/src/test/java/org/apache/cayenne/joda/db/auto/_LocalDateTimeTestEntity.java
new file mode 100644
index 0000000..fab8c8e
--- /dev/null
+++ b/cayenne-joda/src/test/java/org/apache/cayenne/joda/db/auto/_LocalDateTimeTestEntity.java
@@ -0,0 +1,28 @@
+package org.apache.cayenne.joda.db.auto;
+
+import org.apache.cayenne.CayenneDataObject;
+import org.apache.cayenne.exp.Property;
+import org.joda.time.LocalDateTime;
+
+/**
+ * Class _LocalDateTimeTestEntity was generated by Cayenne.
+ * It is probably a good idea to avoid changing this class manually,
+ * since it may be overwritten next time code is regenerated.
+ * If you need to make any customizations, please use subclass.
+ */
+public abstract class _LocalDateTimeTestEntity extends CayenneDataObject {
+
+    private static final long serialVersionUID = 1L; 
+
+    public static final String ID_PK_COLUMN = "ID";
+
+    public static final Property<LocalDateTime> TIMESTAMP = new Property<LocalDateTime>("timestamp");
+
+    public void setTimestamp(LocalDateTime timestamp) {
+        writeProperty("timestamp", timestamp);
+    }
+    public LocalDateTime getTimestamp() {
+        return (LocalDateTime)readProperty("timestamp");
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/6d7bc02d/cayenne-joda/src/test/java/org/apache/cayenne/joda/db/auto/_LocalTimeTestEntity.java
----------------------------------------------------------------------
diff --git a/cayenne-joda/src/test/java/org/apache/cayenne/joda/db/auto/_LocalTimeTestEntity.java b/cayenne-joda/src/test/java/org/apache/cayenne/joda/db/auto/_LocalTimeTestEntity.java
new file mode 100644
index 0000000..a06eb89
--- /dev/null
+++ b/cayenne-joda/src/test/java/org/apache/cayenne/joda/db/auto/_LocalTimeTestEntity.java
@@ -0,0 +1,28 @@
+package org.apache.cayenne.joda.db.auto;
+
+import org.apache.cayenne.CayenneDataObject;
+import org.apache.cayenne.exp.Property;
+import org.joda.time.LocalTime;
+
+/**
+ * Class _LocalTimeTestEntity was generated by Cayenne.
+ * It is probably a good idea to avoid changing this class manually,
+ * since it may be overwritten next time code is regenerated.
+ * If you need to make any customizations, please use subclass.
+ */
+public abstract class _LocalTimeTestEntity extends CayenneDataObject {
+
+    private static final long serialVersionUID = 1L; 
+
+    public static final String ID_PK_COLUMN = "ID";
+
+    public static final Property<LocalTime> TIME = new Property<LocalTime>("time");
+
+    public void setTime(LocalTime time) {
+        writeProperty("time", time);
+    }
+    public LocalTime getTime() {
+        return (LocalTime)readProperty("time");
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/6d7bc02d/cayenne-joda/src/test/resources/cayenne-joda.xml
----------------------------------------------------------------------
diff --git a/cayenne-joda/src/test/resources/cayenne-joda.xml b/cayenne-joda/src/test/resources/cayenne-joda.xml
new file mode 100644
index 0000000..0ba8345
--- /dev/null
+++ b/cayenne-joda/src/test/resources/cayenne-joda.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<domain project-version="7">
+	<map name="joda"/>
+
+	<node name="datanode"
+		 factory="org.apache.cayenne.configuration.server.XMLPoolingDataSourceFactory"
+		 schema-update-strategy="org.apache.cayenne.access.dbsync.CreateIfNoSchemaStrategy"
+		>
+		<map-ref name="joda"/>
+		<data-source>
+			<driver value="org.hsqldb.jdbcDriver"/>
+			<url value="jdbc:hsqldb:mem:joda"/>
+			<connectionPool min="1" max="1"/>
+			<login userName="sa"/>
+		</data-source>
+	</node>
+</domain>

http://git-wip-us.apache.org/repos/asf/cayenne/blob/6d7bc02d/cayenne-joda/src/test/resources/joda.map.xml
----------------------------------------------------------------------
diff --git a/cayenne-joda/src/test/resources/joda.map.xml b/cayenne-joda/src/test/resources/joda.map.xml
new file mode 100644
index 0000000..afb9c7a
--- /dev/null
+++ b/cayenne-joda/src/test/resources/joda.map.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<data-map xmlns="http://cayenne.apache.org/schema/7/modelMap"
+	 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	 xsi:schemaLocation="http://cayenne.apache.org/schema/7/modelMap http://cayenne.apache.org/schema/7/modelMap.xsd"
+	 project-version="7">
+	<property name="defaultPackage" value="org.apache.cayenne.joda.db"/>
+	<db-entity name="DATE_TIME_TEST">
+		<db-attribute name="ID" type="INTEGER" isPrimaryKey="true" isMandatory="true"/>
+		<db-attribute name="Timestamp" type="TIMESTAMP"/>
+	</db-entity>
+	<db-entity name="LOCAL_DATE_TEST">
+		<db-attribute name="Date" type="DATE"/>
+		<db-attribute name="ID" type="INTEGER" isPrimaryKey="true" isMandatory="true"/>
+	</db-entity>
+	<db-entity name="LOCAL_DATE_TIME_TEST">
+		<db-attribute name="ID" type="INTEGER" isPrimaryKey="true" isMandatory="true"/>
+		<db-attribute name="Timestamp" type="TIMESTAMP"/>
+	</db-entity>
+	<db-entity name="LOCAL_TIME_TEST">
+		<db-attribute name="ID" type="INTEGER" isPrimaryKey="true" isMandatory="true"/>
+		<db-attribute name="Time" type="TIME"/>
+	</db-entity>
+	<obj-entity name="DateTimeTest" className="org.apache.cayenne.joda.db.DateTimeTestEntity" dbEntityName="DATE_TIME_TEST">
+		<obj-attribute name="timestamp" type="org.joda.time.DateTime" db-attribute-path="Timestamp"/>
+	</obj-entity>
+	<obj-entity name="LocalDateTest" className="org.apache.cayenne.joda.db.LocalDateTestEntity" dbEntityName="LOCAL_DATE_TEST">
+		<obj-attribute name="date" type="org.joda.time.LocalDate" db-attribute-path="Date"/>
+	</obj-entity>
+	<obj-entity name="LocalDateTimeTest" className="org.apache.cayenne.joda.db.LocalDateTimeTestEntity" dbEntityName="LOCAL_DATE_TIME_TEST">
+		<obj-attribute name="timestamp" type="org.joda.time.LocalDateTime" db-attribute-path="Timestamp"/>
+	</obj-entity>
+	<obj-entity name="LocalTimeTest" className="org.apache.cayenne.joda.db.LocalTimeTestEntity" dbEntityName="LOCAL_TIME_TEST">
+		<obj-attribute name="time" type="org.joda.time.LocalTime" db-attribute-path="Time"/>
+	</obj-entity>
+</data-map>

http://git-wip-us.apache.org/repos/asf/cayenne/blob/6d7bc02d/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 c56c8a1..075256d 100644
--- a/docs/doc/src/main/resources/RELEASE-NOTES.txt
+++ b/docs/doc/src/main/resources/RELEASE-NOTES.txt
@@ -14,6 +14,7 @@ Date:
 ----------------------------------
 Changes/New Features:
 
+CAY-1626 Add JodaTime DateTime support
 CAY-1991 More control over generated String property names
 CAY-1992 Allow to exclude DataMap java class from Modeler class generation
 CAY-1995 Add support for iterators to Select

http://git-wip-us.apache.org/repos/asf/cayenne/blob/6d7bc02d/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index 8f3c199..35de260 100644
--- a/pom.xml
+++ b/pom.xml
@@ -63,7 +63,8 @@
 		<module>tutorials</module>
 		<module>docs</module>
 		<module>assembly</module>
-	</modules>
+        <module>cayenne-joda</module>
+    </modules>
 	<issueManagement>
 		<system>jira</system>
 		<url>http://issues.apache.org/cayenne/</url>