You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cayenne.apache.org by nt...@apache.org on 2020/11/18 09:14:49 UTC

[cayenne] 01/02: CAY-2691 MySQL driver 8.0.x stores LocalDateTime differently than 5.1.x

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

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

commit 3b64f0c1b2583ab505f32e1a14569a199e00b566
Author: Nikita Timofeev <st...@gmail.com>
AuthorDate: Wed Nov 18 12:14:16 2020 +0300

    CAY-2691 MySQL driver 8.0.x stores LocalDateTime differently than 5.1.x
---
 .../org/apache/cayenne/access/types/DateType.java  | 29 ++++++-
 .../org/apache/cayenne/access/types/TimeType.java  | 29 ++++++-
 .../apache/cayenne/access/types/TimestampType.java | 29 ++++++-
 .../apache/cayenne/access/types/UtilDateType.java  | 97 ++++++++++++++++------
 .../org/apache/cayenne/dba/mysql/MySQLAdapter.java | 10 +++
 .../org/apache/cayenne/access/DateTimeTypesIT.java | 28 +++----
 6 files changed, 178 insertions(+), 44 deletions(-)

diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/types/DateType.java b/cayenne-server/src/main/java/org/apache/cayenne/access/types/DateType.java
index 5521ada..0c5ba3d 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/access/types/DateType.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/access/types/DateType.java
@@ -22,12 +22,35 @@ import java.sql.CallableStatement;
 import java.sql.Date;
 import java.sql.PreparedStatement;
 import java.sql.ResultSet;
+import java.util.Calendar;
 
 /**
  * @since 3.0
  */
 public class DateType implements ExtendedType<Date> {
 
+    private final Calendar calendar;
+    private final boolean useCalendar;
+
+    /**
+     * @since 4.2
+     */
+    public DateType() {
+        this(false);
+    }
+
+    /**
+     * @since 4.2
+     */
+    public DateType(boolean useCalendar) {
+        this.useCalendar = useCalendar;
+        if(this.useCalendar) {
+            this.calendar = Calendar.getInstance();
+        } else {
+            this.calendar = null;
+        }
+    }
+
     @Override
     public String getClassName() {
         return Date.class.getName();
@@ -35,12 +58,12 @@ public class DateType implements ExtendedType<Date> {
 
     @Override
     public Date materializeObject(ResultSet rs, int index, int type) throws Exception {
-        return rs.getDate(index);
+        return useCalendar ? rs.getDate(index, calendar) : rs.getDate(index);
     }
 
     @Override
     public Date materializeObject(CallableStatement rs, int index, int type) throws Exception {
-        return rs.getDate(index);
+        return useCalendar ? rs.getDate(index, calendar) : rs.getDate(index);
     }
 
     @Override
@@ -53,6 +76,8 @@ public class DateType implements ExtendedType<Date> {
 
         if (value == null) {
             statement.setNull(pos, type);
+        } else if(useCalendar) {
+            statement.setDate(pos, value, calendar);
         } else {
             statement.setDate(pos, value);
         }
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/types/TimeType.java b/cayenne-server/src/main/java/org/apache/cayenne/access/types/TimeType.java
index 46821bd..d0db8c8 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/access/types/TimeType.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/access/types/TimeType.java
@@ -22,12 +22,35 @@ import java.sql.CallableStatement;
 import java.sql.PreparedStatement;
 import java.sql.ResultSet;
 import java.sql.Time;
+import java.util.Calendar;
 
 /**
  * @since 3.0
  */
 public class TimeType implements ExtendedType<Time> {
 
+    private final Calendar calendar;
+    private final boolean useCalendar;
+
+    /**
+     * @since 4.2
+     */
+    public TimeType() {
+        this(false);
+    }
+
+    /**
+     * @since 4.2
+     */
+    public TimeType(boolean useCalendar) {
+        this.useCalendar = useCalendar;
+        if(this.useCalendar) {
+            this.calendar = Calendar.getInstance();
+        } else {
+            this.calendar = null;
+        }
+    }
+
     @Override
     public String getClassName() {
         return Time.class.getName();
@@ -35,12 +58,12 @@ public class TimeType implements ExtendedType<Time> {
 
     @Override
     public Time materializeObject(ResultSet rs, int index, int type) throws Exception {
-        return rs.getTime(index);
+        return useCalendar ? rs.getTime(index, calendar) : rs.getTime(index);
     }
 
     @Override
     public Time materializeObject(CallableStatement rs, int index, int type) throws Exception {
-        return rs.getTime(index);
+        return useCalendar ? rs.getTime(index, calendar) : rs.getTime(index);
     }
 
     @Override
@@ -53,6 +76,8 @@ public class TimeType implements ExtendedType<Time> {
 
         if (value == null) {
             statement.setNull(pos, type);
+        } else if(useCalendar) {
+            statement.setTime(pos, value, calendar);
         } else {
             statement.setTime(pos, value);
         }
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/types/TimestampType.java b/cayenne-server/src/main/java/org/apache/cayenne/access/types/TimestampType.java
index cecf325..a89772d 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/access/types/TimestampType.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/access/types/TimestampType.java
@@ -22,12 +22,35 @@ import java.sql.CallableStatement;
 import java.sql.PreparedStatement;
 import java.sql.ResultSet;
 import java.sql.Timestamp;
+import java.util.Calendar;
 
 /**
  * @since 3.0
  */
 public class TimestampType implements ExtendedType<Timestamp> {
 
+    private final Calendar calendar;
+    private final boolean useCalendar;
+
+    /**
+     * @since 4.2
+     */
+    public TimestampType() {
+        this(false);
+    }
+
+    /**
+     * @since 4.2
+     */
+    public TimestampType(boolean useCalendar) {
+        this.useCalendar = useCalendar;
+        if(this.useCalendar) {
+            this.calendar = Calendar.getInstance();
+        } else {
+            this.calendar = null;
+        }
+    }
+
     @Override
     public String getClassName() {
         return Timestamp.class.getName();
@@ -35,12 +58,12 @@ public class TimestampType implements ExtendedType<Timestamp> {
 
     @Override
     public Timestamp materializeObject(ResultSet rs, int index, int type) throws Exception {
-        return rs.getTimestamp(index);
+        return useCalendar ? rs.getTimestamp(index, calendar) : rs.getTimestamp(index);
     }
 
     @Override
     public Timestamp materializeObject(CallableStatement cs, int index, int type) throws Exception {
-        return cs.getTimestamp(index);
+        return useCalendar ? cs.getTimestamp(index, calendar) : cs.getTimestamp(index);
     }
 
     @Override
@@ -53,6 +76,8 @@ public class TimestampType implements ExtendedType<Timestamp> {
 
         if (value == null) {
             statement.setNull(pos, type);
+        } else if(useCalendar) {
+            statement.setTimestamp(pos, value, calendar);
         } else {
             statement.setTimestamp(pos, value);
         }
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/types/UtilDateType.java b/cayenne-server/src/main/java/org/apache/cayenne/access/types/UtilDateType.java
index b87a72e..2e9d7b0 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/access/types/UtilDateType.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/access/types/UtilDateType.java
@@ -24,7 +24,9 @@ import org.apache.cayenne.dba.TypesMapping;
 import java.sql.CallableStatement;
 import java.sql.PreparedStatement;
 import java.sql.ResultSet;
+import java.sql.Time;
 import java.sql.Types;
+import java.util.Calendar;
 import java.util.Date;
 
 /**
@@ -33,43 +35,59 @@ import java.util.Date;
  */
 public class UtilDateType implements ExtendedType<Date> {
 
+    private final Calendar calendar;
+    private final boolean useCalendar;
+
     /**
-     * Returns "java.util.Date".
+     * @since 4.2
      */
-    @Override
-    public String getClassName() {
-        return Date.class.getName();
+    public UtilDateType() {
+        this(false);
     }
 
-    protected Object convertToJdbcObject(Date val, int type) throws Exception {
-        if (type == Types.DATE) {
-            return new java.sql.Date(val.getTime());
-        } else if (type == Types.TIME) {
-            return new java.sql.Time(val.getTime());
-        } else if (type == Types.TIMESTAMP) {
-            return new java.sql.Timestamp(val.getTime());
+    /**
+     * @since 4.2
+     */
+    public UtilDateType(boolean useCalendar) {
+        this.useCalendar = useCalendar;
+        if(this.useCalendar) {
+            this.calendar = Calendar.getInstance();
         } else {
-            throw new IllegalArgumentException(
-                    "Only DATE, TIME or TIMESTAMP can be mapped as '" + getClassName()
-                            + "', got " + TypesMapping.getSqlNameByType(type));
+            this.calendar = null;
         }
     }
 
+    /**
+     * Returns "java.util.Date".
+     */
+    @Override
+    public String getClassName() {
+        return Date.class.getName();
+    }
+
     @Override
     public Date materializeObject(ResultSet rs, int index, int type) throws Exception {
         Date val;
         switch (type) {
             case Types.TIMESTAMP:
-                val = rs.getTimestamp(index);
+                val = useCalendar
+                        ? rs.getTimestamp(index, calendar)
+                        : rs.getTimestamp(index);
                 break;
             case Types.DATE:
-                val = rs.getDate(index);
+                val = useCalendar
+                        ? rs.getDate(index, calendar)
+                        : rs.getDate(index);
                 break;
             case Types.TIME:
-                val = rs.getTime(index);
+                val = useCalendar
+                        ? rs.getTime(index, calendar)
+                        : rs.getTime(index);
                 break;
             default:
-                val = rs.getTimestamp(index);
+                val = useCalendar
+                        ? rs.getTimestamp(index, calendar)
+                        : rs.getTimestamp(index);
                 break;
         }
 
@@ -82,16 +100,24 @@ public class UtilDateType implements ExtendedType<Date> {
         Date val;
         switch (type) {
             case Types.TIMESTAMP:
-                val = cs.getTimestamp(index);
+                val = useCalendar
+                        ? cs.getTimestamp(index, calendar)
+                        : cs.getTimestamp(index);
                 break;
             case Types.DATE:
-                val = cs.getDate(index);
+                val = useCalendar
+                        ? cs.getDate(index, calendar)
+                        : cs.getDate(index);
                 break;
             case Types.TIME:
-                val = cs.getTime(index);
+                val = useCalendar
+                        ? cs.getTime(index, calendar)
+                        : cs.getTime(index);
                 break;
             default:
-                val = cs.getTimestamp(index);
+                val = useCalendar
+                        ? cs.getTimestamp(index, calendar)
+                        : cs.getTimestamp(index);
                 break;
         }
 
@@ -110,7 +136,30 @@ public class UtilDateType implements ExtendedType<Date> {
         if (value == null) {
             statement.setNull(pos, type);
         } else {
-            statement.setObject(pos, convertToJdbcObject(value, type), type);
+            if (type == Types.DATE) {
+                if(useCalendar) {
+                    statement.setDate(pos, new java.sql.Date(value.getTime()), calendar);
+                } else {
+                    statement.setDate(pos, new java.sql.Date(value.getTime()));
+                }
+            } else if (type == Types.TIME) {
+                Time time = new Time(value.getTime());
+                if(useCalendar) {
+                    statement.setTime(pos, time, calendar);
+                } else {
+                    statement.setTime(pos, time);
+                }
+            } else if (type == Types.TIMESTAMP) {
+                if(useCalendar) {
+                    statement.setTimestamp(pos, new java.sql.Timestamp(value.getTime()), calendar);
+                } else {
+                    statement.setTimestamp(pos, new java.sql.Timestamp(value.getTime()));
+                }
+            } else {
+                throw new IllegalArgumentException(
+                        "Only DATE, TIME or TIMESTAMP can be mapped as '" + getClassName()
+                                + "', got " + TypesMapping.getSqlNameByType(type));
+            }
         }
     }
 
@@ -121,6 +170,6 @@ public class UtilDateType implements ExtendedType<Date> {
         }
 
         long time = value.getTime();
-        return "\'" + new java.sql.Timestamp(time) + "\'";
+        return '\'' + new java.sql.Timestamp(time).toString() + '\'';
     }
 }
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/dba/mysql/MySQLAdapter.java b/cayenne-server/src/main/java/org/apache/cayenne/dba/mysql/MySQLAdapter.java
index 1c28171..32044e0 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/dba/mysql/MySQLAdapter.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/dba/mysql/MySQLAdapter.java
@@ -36,10 +36,14 @@ import org.apache.cayenne.access.translator.ejbql.EJBQLTranslatorFactory;
 import org.apache.cayenne.access.translator.ejbql.JdbcEJBQLTranslatorFactory;
 import org.apache.cayenne.access.types.ByteArrayType;
 import org.apache.cayenne.access.types.CharType;
+import org.apache.cayenne.access.types.DateType;
 import org.apache.cayenne.access.types.ExtendedType;
 import org.apache.cayenne.access.types.ExtendedTypeFactory;
 import org.apache.cayenne.access.types.ExtendedTypeMap;
 import org.apache.cayenne.access.types.JsonType;
+import org.apache.cayenne.access.types.TimeType;
+import org.apache.cayenne.access.types.TimestampType;
+import org.apache.cayenne.access.types.UtilDateType;
 import org.apache.cayenne.access.types.ValueObjectTypeRegistry;
 import org.apache.cayenne.configuration.Constants;
 import org.apache.cayenne.configuration.RuntimeProperties;
@@ -153,6 +157,12 @@ public class MySQLAdapter extends JdbcAdapter {
 		map.registerType(charType);
 		map.registerType(new ByteArrayType(false, false));
 		map.registerType(new JsonType(charType, true));
+
+		// register non-default types for the dates, see CAY-2691
+		map.registerType(new DateType(true));
+		map.registerType(new TimeType(true));
+		map.registerType(new TimestampType(true));
+		map.registerType(new UtilDateType(true));
 	}
 
 	@Override
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/access/DateTimeTypesIT.java b/cayenne-server/src/test/java/org/apache/cayenne/access/DateTimeTypesIT.java
index 2031afa..94b2312 100644
--- a/cayenne-server/src/test/java/org/apache/cayenne/access/DateTimeTypesIT.java
+++ b/cayenne-server/src/test/java/org/apache/cayenne/access/DateTimeTypesIT.java
@@ -48,13 +48,13 @@ public class DateTimeTypesIT extends ServerCase {
     private DataContext context;
 
     @Test
-    public void testCalendar() throws Exception {
+    public void testCalendar() {
 
         CalendarEntity test = context.newObject(CalendarEntity.class);
 
         Calendar cal = Calendar.getInstance();
         cal.clear();
-        cal.set(2002, 1, 1);
+        cal.set(2002, Calendar.FEBRUARY, 1);
 
         test.setCalendarField(cal);
         context.commitChanges();
@@ -69,12 +69,12 @@ public class DateTimeTypesIT extends ServerCase {
     }
 
     @Test
-    public void testDate() throws Exception {
+    public void testDate() {
         DateTestEntity test = context.newObject(DateTestEntity.class);
 
         Calendar cal = Calendar.getInstance();
         cal.clear();
-        cal.set(2002, 1, 1);
+        cal.set(2002, Calendar.FEBRUARY, 1);
         Date nowDate = cal.getTime();
         test.setDateColumn(nowDate);
         context.commitChanges();
@@ -87,12 +87,12 @@ public class DateTimeTypesIT extends ServerCase {
     }
 
     @Test
-    public void testTime() throws Exception {
+    public void testTime() {
         DateTestEntity test = context.newObject(DateTestEntity.class);
 
         Calendar cal = Calendar.getInstance();
         cal.clear();
-        cal.set(1970, 0, 1, 1, 20, 30);
+        cal.set(1970, Calendar.JANUARY, 1, 1, 20, 30);
         Date nowTime = cal.getTime();
         test.setTimeColumn(nowTime);
         context.commitChanges();
@@ -112,12 +112,12 @@ public class DateTimeTypesIT extends ServerCase {
     }
 
     @Test
-    public void testTimestamp() throws Exception {
+    public void testTimestamp() {
         DateTestEntity test = context.newObject(DateTestEntity.class);
 
         Calendar cal = Calendar.getInstance();
         cal.clear();
-        cal.set(2003, 1, 1, 1, 20, 30);
+        cal.set(2003, Calendar.FEBRUARY, 1, 1, 20, 30);
 
         // most databases fail millisecond accuracy
         // cal.set(Calendar.MILLISECOND, 55);
@@ -133,12 +133,12 @@ public class DateTimeTypesIT extends ServerCase {
     }
 
     @Test
-    public void testSQLTemplateTimestamp() throws Exception {
+    public void testSQLTemplateTimestamp() {
         DateTestEntity test = context.newObject(DateTestEntity.class);
 
         Calendar cal = Calendar.getInstance();
         cal.clear();
-        cal.set(2003, 1, 1, 1, 20, 30);
+        cal.set(2003, Calendar.FEBRUARY, 1, 1, 20, 30);
 
         // most databases fail millisecond accuracy
         // cal.set(Calendar.MILLISECOND, 55);
@@ -154,12 +154,12 @@ public class DateTimeTypesIT extends ServerCase {
     }
 
     @Test
-    public void testSQLTemplateDate() throws Exception {
+    public void testSQLTemplateDate() {
         DateTestEntity test = (DateTestEntity) context.newObject("DateTestEntity");
 
         Calendar cal = Calendar.getInstance();
         cal.clear();
-        cal.set(2003, 1, 1, 1, 20, 30);
+        cal.set(2003, Calendar.FEBRUARY, 1, 1, 20, 30);
 
         // most databases fail millisecond accuracy
         // cal.set(Calendar.MILLISECOND, 55);
@@ -175,12 +175,12 @@ public class DateTimeTypesIT extends ServerCase {
     }
 
     @Test
-    public void testSQLTemplateTime() throws Exception {
+    public void testSQLTemplateTime() {
         DateTestEntity test = (DateTestEntity) context.newObject("DateTestEntity");
 
         Calendar cal = Calendar.getInstance();
         cal.clear();
-        cal.set(2003, 1, 1, 1, 20, 30);
+        cal.set(2003, Calendar.FEBRUARY, 1, 1, 20, 30);
 
         // most databases fail millisecond accuracy
         // cal.set(Calendar.MILLISECOND, 55);