You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@openjpa.apache.org by st...@apache.org on 2019/01/25 18:11:56 UTC

[openjpa] 01/04: OPENJPA-2713 implement native java8 types

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

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

commit e57fd518b32d1ebc85fbdb818b1ca818d1abe46a
Author: Mark Struberg <st...@apache.org>
AuthorDate: Sun Jan 20 23:51:10 2019 +0100

    OPENJPA-2713 implement native java8 types
    
    Since some DBs (e.g. PostgreSQL) do support LocalDate, etc
    in their JDBC drivers, it's probably the best to support it
    on a way deeper level.
---
 .../openjpa/jdbc/meta/MappingDefaultsImpl.java     |   6 -
 .../openjpa/jdbc/meta/MappingRepository.java       |   5 +
 .../openjpa/jdbc/meta/strats/BlobValueHandler.java |   7 +-
 .../jdbc/meta/strats/ImmutableValueHandler.java    |   5 +
 .../jdbc/meta/strats/LocalDateValueHandler.java    |  81 ------------
 .../apache/openjpa/jdbc/sql/AbstractResult.java    |  57 ++++++++-
 .../org/apache/openjpa/jdbc/sql/DBDictionary.java  | 140 ++++++++++++++++-----
 .../org/apache/openjpa/jdbc/sql/MergedResult.java  |  21 +++-
 .../java/org/apache/openjpa/jdbc/sql/Result.java   |  21 ++++
 .../apache/openjpa/jdbc/sql/ResultSetResult.java   |  31 ++++-
 .../org/apache/openjpa/kernel/AttachStrategy.java  |   7 +-
 .../java/org/apache/openjpa/meta/JavaTypes.java    |  28 ++++-
 .../openjpa/persistence/simple/Java8TimeTypes.java |  82 ++++++++++++
 .../persistence/simple/TestBasicAnnotation.java    |   4 +
 .../persistence/simple/TestJava8TimeTypes.java     |  65 ++++++++++
 .../strategy/value/ValueStrategyHandler.java       |  50 ++++----
 .../persistence/PersistenceMetaDataDefaults.java   |   5 +
 .../persistence/meta/AbstractManagedType.java      |  20 +++
 18 files changed, 474 insertions(+), 161 deletions(-)

diff --git a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/MappingDefaultsImpl.java b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/MappingDefaultsImpl.java
index fe4f5bc..ca1bf38 100644
--- a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/MappingDefaultsImpl.java
+++ b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/MappingDefaultsImpl.java
@@ -27,7 +27,6 @@ import org.apache.openjpa.jdbc.conf.JDBCConfiguration;
 import org.apache.openjpa.jdbc.identifier.DBIdentifier;
 import org.apache.openjpa.jdbc.identifier.Normalizer;
 import org.apache.openjpa.jdbc.meta.strats.EnumValueHandler;
-import org.apache.openjpa.jdbc.meta.strats.LocalDateValueHandler;
 import org.apache.openjpa.jdbc.meta.strats.UntypedPCValueHandler;
 import org.apache.openjpa.jdbc.schema.Column;
 import org.apache.openjpa.jdbc.schema.ForeignKey;
@@ -537,11 +536,6 @@ public class MappingDefaultsImpl
             return enumHandler;
         }
 
-        if (java.time.LocalDate.class == type) {
-            // we can compare with == since LocalDate is final
-            return new LocalDateValueHandler();
-        }
-
         return null;
     }
 
diff --git a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/MappingRepository.java b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/MappingRepository.java
index 66f6970..b62cdbb 100644
--- a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/MappingRepository.java
+++ b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/MappingRepository.java
@@ -1356,6 +1356,11 @@ public class MappingRepository extends MetaDataRepository {
             case JavaTypes.DATE:
             case JavaTypes.CALENDAR:
             case JavaTypes.LOCALE:
+            case JavaTypes.LOCAL_DATE:
+            case JavaTypes.LOCAL_TIME:
+            case JavaTypes.LOCAL_DATETIME:
+            case JavaTypes.OFFSET_TIME:
+            case JavaTypes.OFFSET_DATETIME:
                 return ImmutableValueHandler.getInstance();
             case JavaTypes.STRING:
                 if (isClob(val, true))
diff --git a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/BlobValueHandler.java b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/BlobValueHandler.java
index ed76deb..def2112 100644
--- a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/BlobValueHandler.java
+++ b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/BlobValueHandler.java
@@ -29,8 +29,7 @@ import org.apache.openjpa.meta.JavaTypes;
  * Handler for blob values.
  *
  */
-public class BlobValueHandler
-    extends AbstractValueHandler {
+public class BlobValueHandler extends AbstractValueHandler {
 
     
     private static final long serialVersionUID = 1L;
@@ -43,10 +42,6 @@ public class BlobValueHandler
         return _instance;
     }
 
-    /**
-     * @deprecated
-     */
-    @Deprecated
     @Override
     public Column[] map(ValueMapping vm, String name, ColumnIO io,
         boolean adapt) {
diff --git a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/ImmutableValueHandler.java b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/ImmutableValueHandler.java
index e148963..362d18d 100644
--- a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/ImmutableValueHandler.java
+++ b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/ImmutableValueHandler.java
@@ -87,6 +87,11 @@ public class ImmutableValueHandler extends AbstractValueHandler {
             case JavaTypes.SHORT_OBJ:
             case JavaTypes.STRING:
             case JavaTypes.DATE:
+            case JavaTypes.LOCAL_DATE:
+            case JavaTypes.LOCAL_TIME:
+            case JavaTypes.LOCAL_DATETIME:
+            case JavaTypes.OFFSET_TIME:
+            case JavaTypes.OFFSET_DATETIME:
             case JavaTypes.BIGINTEGER:
             case JavaTypes.LOCALE:
                 return true;
diff --git a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/LocalDateValueHandler.java b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/LocalDateValueHandler.java
deleted file mode 100644
index 1701dbf..0000000
--- a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/strats/LocalDateValueHandler.java
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * 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.openjpa.jdbc.meta.strats;
-
-import java.sql.Types;
-import java.time.LocalDate;
-
-import org.apache.openjpa.jdbc.identifier.DBIdentifier;
-import org.apache.openjpa.jdbc.kernel.JDBCStore;
-import org.apache.openjpa.jdbc.meta.JavaSQLTypes;
-import org.apache.openjpa.jdbc.meta.ValueMapping;
-import org.apache.openjpa.jdbc.schema.Column;
-import org.apache.openjpa.jdbc.schema.ColumnIO;
-import org.apache.openjpa.jdbc.sql.DBDictionary;
-import org.apache.openjpa.lib.util.Localizer;
-
-/**
- * Value handler for JDK8 java.time.LocalDate field types.
- *
- */
-public class LocalDateValueHandler extends AbstractValueHandler {
-    private static final long serialVersionUID = 1L;
-    private static final Localizer _loc = Localizer.forPackage(LocalDateValueHandler.class);
-
-    /**
-     * @deprecated
-     */
-    @Deprecated
-    @Override
-    public Column[] map(ValueMapping vm, String name, ColumnIO io,
-        boolean adapt) {
-        DBDictionary dict = vm.getMappingRepository().getDBDictionary();
-        DBIdentifier colName = DBIdentifier.newColumn(name, dict != null ? dict.delimitAll() : false);
-
-        Column column = new Column();
-        column.setIdentifier(colName);
-        column.setJavaType(JavaSQLTypes.SQL_DATE);
-        column.setType(Types.DATE);
-
-        return new Column[]{column};
-    }
-
-    @Override
-    public boolean isVersionable(ValueMapping vm) {
-        return true;
-    }
-
-    @Override
-    public Object toDataStoreValue(ValueMapping vm, Object val, JDBCStore store) {
-        if (val == null) {
-            return null;
-        }
-
-        return java.sql.Date.valueOf((LocalDate) val);
-    }
-
-    @Override
-    public Object toObjectValue(ValueMapping vm, Object val) {
-        if (val == null) {
-            return null;
-        }
-
-        return ((java.sql.Date) val).toLocalDate();
-    }
-}
diff --git a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/AbstractResult.java b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/AbstractResult.java
index 61f3143..ab925f1 100644
--- a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/AbstractResult.java
+++ b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/AbstractResult.java
@@ -31,6 +31,9 @@ import java.sql.SQLException;
 import java.sql.Time;
 import java.sql.Timestamp;
 import java.sql.Types;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
 import java.util.Calendar;
 import java.util.Date;
 import java.util.HashMap;
@@ -502,8 +505,7 @@ public abstract class AbstractResult
 
     protected Calendar getCalendarInternal(Object obj, Joins joins)
         throws SQLException {
-        Object val = checkNull(getObjectInternal(obj, JavaTypes.CALENDAR,
-            null, joins));
+        Object val = checkNull(getObjectInternal(obj, JavaTypes.CALENDAR, null, joins));
         if (val == null)
             return null;
         if (val instanceof Calendar)
@@ -515,6 +517,51 @@ public abstract class AbstractResult
     }
 
     @Override
+    public LocalDate getLocalDate(Object obj) throws SQLException {
+        return getLocalDateInternal(translate(obj, null), null);
+    }
+
+    protected LocalDate getLocalDateInternal(Object obj, Joins joins) throws SQLException {
+        Object val = checkNull(getObjectInternal(obj, JavaTypes.LOCAL_DATE, null, joins));
+        if (val == null)
+            return null;
+        if (val instanceof LocalDate)
+            return (LocalDate) val;
+
+        return LocalDate.parse(val.toString());
+    }
+
+    @Override
+    public LocalTime getLocalTime(Object obj) throws SQLException {
+        return getLocalTimeInternal(translate(obj, null), null);
+    }
+
+    protected LocalTime getLocalTimeInternal(Object obj, Joins joins) throws SQLException {
+        Object val = checkNull(getObjectInternal(obj, JavaTypes.LOCAL_TIME, null, joins));
+        if (val == null)
+            return null;
+        if (val instanceof LocalTime)
+            return (LocalTime) val;
+
+        return LocalTime.parse(val.toString());
+    }
+
+    @Override
+    public LocalDateTime getLocalDateTime(Object obj) throws SQLException {
+        return getLocalDateTimeInternal(translate(obj, null), null);
+    }
+
+    protected LocalDateTime getLocalDateTimeInternal(Object obj, Joins joins) throws SQLException {
+        Object val = checkNull(getObjectInternal(obj, JavaTypes.LOCAL_DATETIME, null, joins));
+        if (val == null)
+            return null;
+        if (val instanceof LocalDateTime)
+            return (LocalDateTime) val;
+
+        return LocalDateTime.parse(val.toString());
+    }
+
+    @Override
     public char getChar(Object obj)
         throws SQLException {
         return getCharInternal(translate(obj, null), null);
@@ -594,8 +641,7 @@ public abstract class AbstractResult
 
     protected Date getDateInternal(Object obj, Joins joins)
         throws SQLException {
-        Object val = checkNull(getObjectInternal(obj, JavaTypes.DATE,
-            null, joins));
+        Object val = checkNull(getObjectInternal(obj, JavaTypes.DATE, null, joins));
         if (val == null)
             return null;
         if (val instanceof Date)
@@ -615,8 +661,7 @@ public abstract class AbstractResult
         return getDateInternal(translate(col, joins), cal, joins);
     }
 
-    protected java.sql.Date getDateInternal(Object obj, Calendar cal,
-        Joins joins)
+    protected java.sql.Date getDateInternal(Object obj, Calendar cal, Joins joins)
         throws SQLException {
         return (java.sql.Date) checkNull(getObjectInternal(obj,
             JavaSQLTypes.SQL_DATE, cal, joins));
diff --git a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DBDictionary.java b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DBDictionary.java
index 3f02899..81d249f 100644
--- a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DBDictionary.java
+++ b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DBDictionary.java
@@ -45,6 +45,9 @@ import java.sql.Time;
 import java.sql.Timestamp;
 import java.sql.Types;
 import java.text.MessageFormat;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Calendar;
@@ -775,6 +778,33 @@ public class DBDictionary
         return cal;
     }
 
+    /**
+     * Retrieve the specified column of the SQL ResultSet to the proper
+     * {@link LocalDate} java type.
+     */
+    public LocalDate getLocalDate(ResultSet rs, int column) throws SQLException {
+        java.sql.Date date = rs.getDate(column);
+        return date != null ? date.toLocalDate() : null;
+    }
+
+    /**
+     * Retrieve the specified column of the SQL ResultSet to the proper
+     * {@link LocalTime} java type.
+     */
+    public LocalTime getLocalTime(ResultSet rs, int column) throws SQLException {
+        java.sql.Time time = rs.getTime(column);
+        return time != null ? time.toLocalTime() : null;
+    }
+
+    /**
+     * Retrieve the specified column of the SQL ResultSet to the proper
+     * {@link LocalDateTime} java type.
+     */
+    public LocalDateTime getLocalDateTime(ResultSet rs, int column) throws SQLException {
+        Timestamp tst = rs.getTimestamp(column);
+        return tst != null ? tst.toLocalDateTime() : null;
+    }
+
     private ProxyManager getProxyManager() {
         if (_proxyManager == null) {
             _proxyManager = conf.getProxyManagerInstance();
@@ -1006,8 +1036,7 @@ public class DBDictionary
     /**
      * Set the given value as a parameter to the statement.
      */
-    public void setAsciiStream(PreparedStatement stmnt, int idx,
-        InputStream val, int length, Column col)
+    public void setAsciiStream(PreparedStatement stmnt, int idx, InputStream val, int length, Column col)
         throws SQLException {
         stmnt.setAsciiStream(idx, val, length);
     }
@@ -1015,47 +1044,48 @@ public class DBDictionary
     /**
      * Set the given value as a parameter to the statement.
      */
-    public void setBigDecimal(PreparedStatement stmnt, int idx, BigDecimal val,
-        Column col)
+    public void setBigDecimal(PreparedStatement stmnt, int idx, BigDecimal val, Column col)
         throws SQLException {
         if ((col != null && col.isCompatible(Types.VARCHAR, null, 0, 0))
-            || (col == null && storeLargeNumbersAsStrings))
+            || (col == null && storeLargeNumbersAsStrings)) {
             setString(stmnt, idx, val.toString(), col);
-        else
+        }
+        else {
             stmnt.setBigDecimal(idx, val);
+        }
     }
 
     /**
      * Set the given value as a parameter to the statement.
      */
-    public void setBigInteger(PreparedStatement stmnt, int idx, BigInteger val,
-        Column col)
+    public void setBigInteger(PreparedStatement stmnt, int idx, BigInteger val, Column col)
         throws SQLException {
         if ((col != null && col.isCompatible(Types.VARCHAR, null, 0, 0))
-            || (col == null && storeLargeNumbersAsStrings))
+            || (col == null && storeLargeNumbersAsStrings)) {
             setString(stmnt, idx, val.toString(), col);
-        else
+        }
+        else {
             setBigDecimal(stmnt, idx, new BigDecimal(val), col);
+        }
     }
 
     /**
      * Set the given value as a parameter to the statement.
      */
-    public void setBinaryStream(PreparedStatement stmnt, int idx,
-        InputStream val, int length, Column col)
+    public void setBinaryStream(PreparedStatement stmnt, int idx, InputStream val, int length, Column col)
         throws SQLException {
 
-    	//OPENJPA-2067: If the user has set the 'useJDBC4SetBinaryStream' property
-    	//then lets use the JDBC 4.0 version of the setBinaryStream method.
-		if (useJDBC4SetBinaryStream) {
-			if (isJDBC4){
-				stmnt.setBinaryStream(idx, val);
-				return;
-			}
-			else {
-				log.trace(_loc.get("jdbc4-setbinarystream-unsupported"));
-			}
-		}
+        //OPENJPA-2067: If the user has set the 'useJDBC4SetBinaryStream' property
+        //then lets use the JDBC 4.0 version of the setBinaryStream method.
+        if (useJDBC4SetBinaryStream) {
+            if (isJDBC4){
+                stmnt.setBinaryStream(idx, val);
+                return;
+            }
+            else {
+                log.trace(_loc.get("jdbc4-setbinarystream-unsupported"));
+            }
+        }
 
         stmnt.setBinaryStream(idx, val, length);
     }
@@ -1072,8 +1102,7 @@ public class DBDictionary
      * Set the given value as a parameter to the statement. Uses the
      * {@link #serialize} method to serialize the value.
      */
-    public void setBlobObject(PreparedStatement stmnt, int idx, Object val,
-        Column col, JDBCStore store)
+    public void setBlobObject(PreparedStatement stmnt, int idx, Object val, Column col, JDBCStore store)
         throws SQLException {
         setBytes(stmnt, idx, serialize(val, store), col);
     }
@@ -1169,8 +1198,7 @@ public class DBDictionary
     /**
      * Set the given value as a parameter to the statement.
      */
-    public void setDate(PreparedStatement stmnt, int idx, java.sql.Date val,
-        Calendar cal, Column col)
+    public void setDate(PreparedStatement stmnt, int idx, java.sql.Date val, Calendar cal, Column col)
         throws SQLException {
         if (cal == null)
             stmnt.setDate(idx, val);
@@ -1181,14 +1209,40 @@ public class DBDictionary
     /**
      * Set the given value as a parameter to the statement.
      */
-    public void setCalendar(PreparedStatement stmnt, int idx, Calendar val,
-        Column col)
+    public void setCalendar(PreparedStatement stmnt, int idx, Calendar val, Column col)
         throws SQLException {
         // by default we merely delegate to the Date parameter
         setDate(stmnt, idx, val.getTime(), col);
     }
 
     /**
+     * Set the given LocalDate value as a parameter to the statement.
+     *
+     */
+    public void setLocalDate(PreparedStatement stmnt, int idx, LocalDate val, Column col)
+        throws SQLException {
+        setDate(stmnt, idx, java.sql.Date.valueOf(val), null, col);
+    }
+
+    /**
+     * Set the given LocalTime value as a parameter to the statement.
+     *
+     */
+    public void setLocalTime(PreparedStatement stmnt, int idx, LocalTime val, Column col)
+        throws SQLException {
+        setTime(stmnt, idx, java.sql.Time.valueOf(val), null, col);
+    }
+
+    /**
+     * Set the given LocalTime value as a parameter to the statement.
+     *
+     */
+    public void setLocalDateTime(PreparedStatement stmnt, int idx, LocalDateTime val, Column col)
+        throws SQLException {
+        setTimestamp(stmnt, idx, java.sql.Timestamp.valueOf(val), null, col);
+    }
+
+    /**
      * Set the given value as a parameter to the statement.
      */
     public void setDouble(PreparedStatement stmnt, int idx, double val,
@@ -1398,6 +1452,15 @@ public class DBDictionary
             case JavaTypes.CALENDAR:
                 setCalendar(stmnt, idx, (Calendar) val, col);
                 break;
+            case JavaTypes.LOCAL_DATE:
+                setLocalDate(stmnt, idx, (LocalDate) val, col);
+                break;
+            case JavaTypes.LOCAL_TIME:
+                setLocalTime(stmnt, idx, (LocalTime) val, col);
+                break;
+            case JavaTypes.LOCAL_DATETIME:
+                setLocalDateTime(stmnt, idx, (LocalDateTime) val, col);
+                break;
             case JavaTypes.BIGDECIMAL:
                 setBigDecimal(stmnt, idx, (BigDecimal) val, col);
                 break;
@@ -1547,6 +1610,15 @@ public class DBDictionary
             setDate(stmnt, idx, (Date) val, col);
         else if (val instanceof Calendar)
             setDate(stmnt, idx, ((Calendar) val).getTime(), col);
+        else if (val instanceof LocalDate) {
+            setLocalDate(stmnt, idx, (LocalDate) val, col);
+        }
+        else if (val instanceof LocalTime) {
+            setLocalTime(stmnt, idx, (LocalTime) val, col);
+        }
+        else if (val instanceof LocalDateTime) {
+            setLocalDateTime(stmnt, idx, (LocalDateTime) val, col);
+        }
         else if (val instanceof Reader)
             setCharacterStream(stmnt, idx, (Reader) val,
                 (sized == null) ? 0 : sized.size, col);
@@ -1725,6 +1797,16 @@ public class DBDictionary
             case JavaTypes.CALENDAR:
             case JavaTypes.DATE:
                 return getPreferredType(Types.TIMESTAMP);
+            case JavaTypes.LOCAL_DATE:
+                return getPreferredType(Types.DATE);
+            case JavaTypes.LOCAL_TIME:
+                return getPreferredType(Types.TIME);
+            case JavaTypes.LOCAL_DATETIME:
+                return getPreferredType(Types.TIMESTAMP);
+            case JavaTypes.OFFSET_TIME:
+                return getPreferredType(Types.TIME);
+            case JavaTypes.OFFSET_DATETIME:
+                return getPreferredType(Types.TIMESTAMP);
             case JavaSQLTypes.SQL_ARRAY:
                 return getPreferredType(Types.ARRAY);
             case JavaSQLTypes.BINARY_STREAM:
diff --git a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/MergedResult.java b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/MergedResult.java
index 3334399..c22539e 100644
--- a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/MergedResult.java
+++ b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/MergedResult.java
@@ -29,6 +29,9 @@ import java.sql.Ref;
 import java.sql.SQLException;
 import java.sql.Time;
 import java.sql.Timestamp;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
 import java.util.Calendar;
 import java.util.Comparator;
 import java.util.Date;
@@ -49,8 +52,7 @@ import org.apache.openjpa.util.UnsupportedException;
  *
  * @author Abe White
  */
-public class MergedResult
-    implements Result {
+public class MergedResult implements Result {
 
     private static final byte NEXT = 0;
     private static final byte CURRENT = 1;
@@ -338,6 +340,21 @@ public class MergedResult
     }
 
     @Override
+    public LocalDate getLocalDate(Object obj) throws SQLException {
+        return _res[_idx].getLocalDate(obj);
+    }
+
+    @Override
+    public LocalTime getLocalTime(Object obj) throws SQLException {
+        return _res[_idx].getLocalTime(obj);
+    }
+
+    @Override
+    public LocalDateTime getLocalDateTime(Object obj) throws SQLException {
+        return _res[_idx].getLocalDateTime(obj);
+    }
+
+    @Override
     public char getChar(Object obj)
         throws SQLException {
         return _res[_idx].getChar(obj);
diff --git a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/Result.java b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/Result.java
index f803e42..35abde7 100644
--- a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/Result.java
+++ b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/Result.java
@@ -29,6 +29,9 @@ import java.sql.Ref;
 import java.sql.SQLException;
 import java.sql.Time;
 import java.sql.Timestamp;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
 import java.util.Calendar;
 import java.util.Date;
 import java.util.Locale;
@@ -285,6 +288,24 @@ public interface Result
     /**
      * Return the value stored in the given column or id.
      */
+    LocalDate getLocalDate(Object obj)
+        throws SQLException;
+
+    /**
+     * Return the value stored in the given column or id.
+     */
+    LocalTime getLocalTime(Object obj)
+        throws SQLException;
+
+    /**
+     * Return the value stored in the given column or id.
+     */
+    LocalDateTime getLocalDateTime(Object obj)
+        throws SQLException;
+
+    /**
+     * Return the value stored in the given column or id.
+     */
     char getChar(Object obj)
         throws SQLException;
 
diff --git a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/ResultSetResult.java b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/ResultSetResult.java
index 9b5a1aa..143c524 100644
--- a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/ResultSetResult.java
+++ b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/ResultSetResult.java
@@ -33,6 +33,9 @@ import java.sql.Statement;
 import java.sql.Time;
 import java.sql.Timestamp;
 import java.sql.Types;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
 import java.util.Calendar;
 import java.util.Date;
 import java.util.Locale;
@@ -318,6 +321,25 @@ public class ResultSetResult
     }
 
     @Override
+    protected LocalDate getLocalDateInternal(Object obj, Joins joins)
+            throws SQLException {
+        return _dict.getLocalDate(_rs, ((Number) obj).intValue());
+    }
+
+    @Override
+    protected LocalTime getLocalTimeInternal(Object obj, Joins joins)
+            throws SQLException {
+        return _dict.getLocalTime(_rs, ((Number) obj).intValue());
+    }
+
+    @Override
+    protected LocalDateTime getLocalDateTimeInternal(Object obj, Joins joins)
+            throws SQLException {
+        return _dict.getLocalDateTime(_rs, ((Number) obj).intValue());
+    }
+
+
+    @Override
     protected char getCharInternal(Object obj, Joins joins)
         throws SQLException {
         return _dict.getChar(_rs, ((Number) obj).intValue());
@@ -385,8 +407,7 @@ public class ResultSetResult
     }
 
     @Override
-    protected Object getObjectInternal(Object obj, int metaTypeCode,
-        Object arg, Joins joins)
+    protected Object getObjectInternal(Object obj, int metaTypeCode, Object arg, Joins joins)
         throws SQLException {
         if (metaTypeCode == -1 && obj instanceof Column)
             metaTypeCode = ((Column) obj).getJavaType();
@@ -439,6 +460,12 @@ public class ResultSetResult
                 return getDateInternal(obj, joins);
             case JavaTypes.CALENDAR:
                 return getCalendarInternal(obj, joins);
+            case JavaTypes.LOCAL_DATE:
+                return getLocalDateInternal(obj, joins);
+            case JavaTypes.LOCAL_TIME:
+                return getLocalTimeInternal(obj, joins);
+            case JavaTypes.LOCAL_DATETIME:
+                return getLocalDateTimeInternal(obj, joins);
             case JavaTypes.BIGDECIMAL:
                 return getBigDecimalInternal(obj, joins);
             case JavaTypes.NUMBER:
diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/AttachStrategy.java b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/AttachStrategy.java
index 4cefe80..349f244 100644
--- a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/AttachStrategy.java
+++ b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/AttachStrategy.java
@@ -181,6 +181,11 @@ abstract class AttachStrategy
                 break;
             case JavaTypes.DATE:
             case JavaTypes.CALENDAR:
+            case JavaTypes.LOCAL_DATE:
+            case JavaTypes.LOCAL_TIME:
+            case JavaTypes.LOCAL_DATETIME:
+            case JavaTypes.OFFSET_TIME:
+            case JavaTypes.OFFSET_DATETIME:
             case JavaTypes.NUMBER:
             case JavaTypes.BOOLEAN_OBJ:
             case JavaTypes.BYTE_OBJ:
@@ -219,7 +224,7 @@ abstract class AttachStrategy
                         if (cpy != null) {
                             frmpc = cpy;
                         } else {
-                        	frmpc = getReference(manager, frmpc, sm, fmd);
+                            frmpc = getReference(manager, frmpc, sm, fmd);
                         }
                     }
                     else {
diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/meta/JavaTypes.java b/openjpa-kernel/src/main/java/org/apache/openjpa/meta/JavaTypes.java
index c2ffd07..c1c9f8f 100644
--- a/openjpa-kernel/src/main/java/org/apache/openjpa/meta/JavaTypes.java
+++ b/openjpa-kernel/src/main/java/org/apache/openjpa/meta/JavaTypes.java
@@ -24,6 +24,11 @@ import java.io.Serializable;
 import java.lang.reflect.Array;
 import java.math.BigDecimal;
 import java.math.BigInteger;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.time.OffsetDateTime;
+import java.time.OffsetTime;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Calendar;
@@ -84,6 +89,15 @@ public class JavaTypes {
     public static final int INPUT_READER = 31;
     public static final int ENUM = 32;
 
+    // Java8 time API
+    public static final int LOCAL_DATE = 33;
+    public static final int LOCAL_TIME = 34;
+    public static final int LOCAL_DATETIME = 35;
+    public static final int OFFSET_TIME = 36;
+    public static final int OFFSET_DATETIME = 37;
+
+
+
     private static final Localizer _loc = Localizer.forPackage(JavaTypes.class);
 
     private static final Map<Class<?>, Integer> _typeCodes = new HashMap<>();
@@ -110,6 +124,13 @@ public class JavaTypes {
         _typeCodes.put(PersistenceCapable.class, PC_UNTYPED);
         _typeCodes.put(Properties.class, MAP);
         _typeCodes.put(Calendar.class, CALENDAR);
+
+        // Java8 time API
+        _typeCodes.put(LocalDate.class, LOCAL_DATE);
+        _typeCodes.put(LocalTime.class, LOCAL_TIME);
+        _typeCodes.put(LocalDateTime.class, LOCAL_DATETIME);
+        _typeCodes.put(OffsetTime.class, OFFSET_TIME);
+        _typeCodes.put(OffsetDateTime.class, OFFSET_DATETIME);
     }
 
     /**
@@ -139,9 +160,10 @@ public class JavaTypes {
             }
         }
 
-        Integer code = (Integer) _typeCodes.get(type);
-        if (code != null)
+        Integer code = _typeCodes.get(type);
+        if (code != null) {
             return code.intValue();
+        }
 
         // have to do this first to catch custom collection and map types;
         // on resolve we figure out if these custom types are
@@ -238,7 +260,7 @@ public class JavaTypes {
      */
     private static Class<?> classForName(String name, ClassMetaData meta,
             Class<?> dec, ValueMetaData vmd, ClassLoader loader) {
-    	return classForName(name, meta, dec, vmd,  loader, true);
+        return classForName(name, meta, dec, vmd,  loader, true);
     }
 
     /**
diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/simple/Java8TimeTypes.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/simple/Java8TimeTypes.java
new file mode 100644
index 0000000..d5b1681
--- /dev/null
+++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/simple/Java8TimeTypes.java
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.openjpa.persistence.simple;
+
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.util.Date;
+
+/**
+ * Java8 DateTime types which are required by the JPA-2.2 spec
+ */
+@Entity
+public class Java8TimeTypes {
+
+    @Id
+    private int id;
+
+    private java.util.Date oldDateField;
+
+    private LocalTime localTimeField;
+    private LocalDate localDateField;
+    private LocalDateTime localDateTimeField;
+
+    public int getId() {
+        return id;
+    }
+
+    public void setId(int id) {
+        this.id = id;
+    }
+
+    public Date getOldDateField() {
+        return oldDateField;
+    }
+
+    public void setOldDateField(Date oldDateField) {
+        this.oldDateField = oldDateField;
+    }
+
+    public LocalTime getLocalTimeField() {
+        return localTimeField;
+    }
+
+    public void setLocalTimeField(LocalTime localTimeField) {
+        this.localTimeField = localTimeField;
+    }
+
+    public LocalDate getLocalDateField() {
+        return localDateField;
+    }
+
+    public void setLocalDateField(LocalDate localDateField) {
+        this.localDateField = localDateField;
+    }
+
+    public LocalDateTime getLocalDateTimeField() {
+        return localDateTimeField;
+    }
+
+    public void setLocalDateTimeField(LocalDateTime localDateTimeField) {
+        this.localDateTimeField = localDateTimeField;
+    }
+}
diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/simple/TestBasicAnnotation.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/simple/TestBasicAnnotation.java
index 754e30c..c0f0d90 100644
--- a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/simple/TestBasicAnnotation.java
+++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/simple/TestBasicAnnotation.java
@@ -20,6 +20,7 @@ package org.apache.openjpa.persistence.simple;
 
 import java.math.BigDecimal;
 import java.time.LocalDate;
+import java.time.LocalTime;
 import java.util.Calendar;
 import java.util.Date;
 
@@ -35,6 +36,7 @@ import org.apache.openjpa.persistence.test.SingleEMFTestCase;
  */
 public class TestBasicAnnotation extends SingleEMFTestCase {
     private static String VAL_LOCAL_DATE = "2019-01-01";
+    private static String VAL_LOCAL_TIME = "14:57:15";
 
     @Override
     public void setUp() {
@@ -61,6 +63,7 @@ public class TestBasicAnnotation extends SingleEMFTestCase {
         aft.setWDoubleField(new Double(1));
 
         aft.setLocalDateField(LocalDate.parse(VAL_LOCAL_DATE));
+        aft.setLocalTimeField(LocalTime.parse(VAL_LOCAL_TIME));
 
 
         em.persist(aft);
@@ -87,6 +90,7 @@ public class TestBasicAnnotation extends SingleEMFTestCase {
         assertNotNull(aftQuery.getWDoubleField());
 
         assertEquals(LocalDate.parse(VAL_LOCAL_DATE), aftQuery.getLocalDateField());
+        assertEquals(LocalTime.parse(VAL_LOCAL_TIME), aftQuery.getLocalTimeField());
 
         em.close();
     }
diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/simple/TestJava8TimeTypes.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/simple/TestJava8TimeTypes.java
new file mode 100644
index 0000000..3de2258
--- /dev/null
+++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/simple/TestJava8TimeTypes.java
@@ -0,0 +1,65 @@
+/*
+ * 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.openjpa.persistence.simple;
+
+import org.apache.openjpa.persistence.test.SingleEMFTestCase;
+
+import javax.persistence.EntityManager;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.util.Date;
+
+/**
+ * Test for JPA-2.2 java.time.* functionality
+ */
+public class TestJava8TimeTypes extends SingleEMFTestCase {
+    private static String VAL_LOCAL_DATE = "2019-01-01";
+    private static String VAL_LOCAL_TIME = "14:57:15";
+    private static String VAL_LOCAL_DATETIME = "2019-01-01T01:00:00";
+
+    @Override
+    public void setUp() {
+        setUp(CLEAR_TABLES, Java8TimeTypes.class);
+    }
+
+    public void testJava8Types() throws Exception {
+        EntityManager em = emf.createEntityManager();
+        em.getTransaction().begin();
+        Java8TimeTypes e = new Java8TimeTypes();
+        e.setId(1);
+        e.setOldDateField(new Date());
+        e.setLocalTimeField(LocalTime.parse(VAL_LOCAL_TIME));
+        e.setLocalDateField(LocalDate.parse(VAL_LOCAL_DATE));
+        e.setLocalDateTimeField(LocalDateTime.parse(VAL_LOCAL_DATETIME));
+
+        em.persist(e);
+        em.getTransaction().commit();
+        em.close();
+
+        // now read it back.
+        em = emf.createEntityManager();
+        Java8TimeTypes eRead = em.find(Java8TimeTypes.class, 1);
+
+        assertEquals(LocalTime.parse(VAL_LOCAL_TIME), eRead.getLocalTimeField());
+        assertEquals(LocalDate.parse(VAL_LOCAL_DATE), eRead.getLocalDateField());
+        assertEquals(LocalDateTime.parse(VAL_LOCAL_DATETIME), eRead.getLocalDateTimeField());
+    }
+
+}
diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/strategy/value/ValueStrategyHandler.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/strategy/value/ValueStrategyHandler.java
index 26d2ea1..2017c24 100644
--- a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/strategy/value/ValueStrategyHandler.java
+++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/strategy/value/ValueStrategyHandler.java
@@ -27,42 +27,42 @@ import org.apache.openjpa.meta.JavaTypes;
 
 public class ValueStrategyHandler extends AbstractValueHandler {
 
-	private static final long serialVersionUID = 8371304701543038775L;
+    private static final long serialVersionUID = 8371304701543038775L;
 
-	private static final ValueStrategyHandler _instance = new ValueStrategyHandler();
+    private static final ValueStrategyHandler _instance = new ValueStrategyHandler();
 
-	public static ValueStrategyHandler getInstance(){
-		return _instance;
-	}
+    public static ValueStrategyHandler getInstance(){
+        return _instance;
+    }
 
-	@Override
-	public Column[] map(ValueMapping arg0, String name, ColumnIO arg2,
-			boolean arg3) {
+    @Override
+    public Column[] map(ValueMapping arg0, String name, ColumnIO arg2,
+                        boolean arg3) {
 
-		Column col = new Column();
-		col.setName(name);
-		col.setJavaType(JavaTypes.STRING);
+        Column col = new Column();
+        col.setName(name);
+        col.setJavaType(JavaTypes.STRING);
 
-		return new Column[]{col};
-	}
+        return new Column[]{col};
+    }
 
-	@Override
+    @Override
     public Object toDataStoreValue(ValueMapping vm, Object val, JDBCStore store){
 
-		if(val == null){
-			return null;
-		}
+        if(val == null){
+            return null;
+        }
 
-		return val.toString();
-	}
+        return val.toString();
+    }
 
-	@Override
+    @Override
     public Object toObjectValue(ValueMapping vm, Object val){
-		if(val == null){
-			return null;
-		}
+        if(val == null){
+            return null;
+        }
 
-		return val.toString();
-	}
+        return val.toString();
+    }
 
 }
diff --git a/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PersistenceMetaDataDefaults.java b/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PersistenceMetaDataDefaults.java
index 21e9358..6953db7 100644
--- a/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PersistenceMetaDataDefaults.java
+++ b/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PersistenceMetaDataDefaults.java
@@ -227,6 +227,11 @@ public class PersistenceMetaDataDefaults
             case JavaTypes.BIGDECIMAL:
             case JavaTypes.BIGINTEGER:
             case JavaTypes.DATE:
+            case JavaTypes.LOCAL_DATE:
+            case JavaTypes.LOCAL_TIME:
+            case JavaTypes.LOCAL_DATETIME:
+            case JavaTypes.OFFSET_TIME:
+            case JavaTypes.OFFSET_DATETIME:
                 return BASIC;
             case JavaTypes.OBJECT:
                 if (Enum.class.isAssignableFrom(type))
diff --git a/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/meta/AbstractManagedType.java b/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/meta/AbstractManagedType.java
index 56b0a37..a3b0dee 100644
--- a/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/meta/AbstractManagedType.java
+++ b/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/meta/AbstractManagedType.java
@@ -20,6 +20,11 @@ package org.apache.openjpa.persistence.meta;
 
 import java.math.BigDecimal;
 import java.math.BigInteger;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.time.OffsetDateTime;
+import java.time.OffsetTime;
 import java.util.Arrays;
 import java.util.Calendar;
 import java.util.Collection;
@@ -137,6 +142,21 @@ public abstract class AbstractManagedType<X> extends Types.BaseType<X>
             case JavaTypes.DATE:
                 attrs.add(new Members.SingularAttributeImpl<X, Date>(this, f));
                 break;
+            case JavaTypes.LOCAL_DATE:
+                attrs.add(new Members.SingularAttributeImpl<X, LocalDate>(this, f));
+                break;
+            case JavaTypes.LOCAL_TIME:
+                attrs.add(new Members.SingularAttributeImpl<X, LocalTime>(this, f));
+                break;
+            case JavaTypes.LOCAL_DATETIME:
+                attrs.add(new Members.SingularAttributeImpl<X, LocalDateTime>(this, f));
+                break;
+            case JavaTypes.OFFSET_TIME:
+                attrs.add(new Members.SingularAttributeImpl<X, OffsetTime>(this, f));
+                break;
+            case JavaTypes.OFFSET_DATETIME:
+                attrs.add(new Members.SingularAttributeImpl<X, OffsetDateTime>(this, f));
+                break;
             case JavaTypes.CALENDAR:
                 attrs.add(new Members.SingularAttributeImpl<X, Calendar>(this, f));
                 break;