提交 f4507bb3 authored 作者: Philippe Marschall's avatar Philippe Marschall 提交者: Sergi Vladykin

Add JSR-310 Support (#386)

Add support for Java 8 Date and Time API AKA JSR-310. On the face of it
the existing JDBC types java.sql.Date, java.sql.Time and
java.sql.Timestamp offer conversion methods to and from JSR-310 data
types. Unfortunately java.sql.Timestamp does silent data truncation
when the timestamp falls into a DST transition on the JVM time zone.
Worse still for java.time.OffsetDateTime there is no corresponding JDBC
type. Therefore these conversions have to be implemented. All of this
has to be programmed using reflection so the code compiles and can be
executed on Java 7.
上级 0cd81ddb
......@@ -38,6 +38,7 @@ import org.h2.result.ResultInterface;
import org.h2.result.UpdatableRow;
import org.h2.util.DateTimeUtils;
import org.h2.util.IOUtils;
import org.h2.util.LocalDateTimeUtils;
import org.h2.util.New;
import org.h2.util.StringUtils;
import org.h2.value.CompareMode;
......@@ -57,6 +58,7 @@ import org.h2.value.ValueShort;
import org.h2.value.ValueString;
import org.h2.value.ValueTime;
import org.h2.value.ValueTimestamp;
import org.h2.value.ValueTimestampTimeZone;
import com.vividsolutions.jts.geom.Geometry;
......@@ -77,6 +79,7 @@ import com.vividsolutions.jts.geom.Geometry;
* </p>
*/
public class JdbcResultSet extends TraceObject implements ResultSet, JdbcResultSetBackwardsCompat {
private final boolean closeStatement;
private final boolean scrollable;
private final boolean updatable;
......@@ -3779,6 +3782,16 @@ public class JdbcResultSet extends TraceObject implements ResultSet, JdbcResultS
return type.cast(value.getObject());
} else if (type.isAssignableFrom(Geometry.class)) {
return type.cast(value.getObject());
} else if (LocalDateTimeUtils.isLocalDate(type)) {
return type.cast(LocalDateTimeUtils.valueToLocalDate(value));
} else if (LocalDateTimeUtils.isLocalTime(type)) {
return type.cast(LocalDateTimeUtils.valueToLocalTime(value));
} else if (LocalDateTimeUtils.isLocalDateTime(type)) {
return type.cast(LocalDateTimeUtils.valueToLocalDateTime(
(ValueTimestamp) value));
} else if (LocalDateTimeUtils.isOffsetDateTime(type) && value instanceof ValueTimestampTimeZone) {
return type.cast(LocalDateTimeUtils.valueToOffsetDateTime(
(ValueTimestampTimeZone) value));
} else {
throw unsupported(type.getClass().getName());
}
......
......@@ -23,6 +23,7 @@ import java.sql.Types;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.UUID;
import org.h2.api.ErrorCode;
import org.h2.api.TimestampWithTimeZone;
import org.h2.engine.Constants;
......@@ -34,6 +35,7 @@ import org.h2.jdbc.JdbcConnection;
import org.h2.message.DbException;
import org.h2.tools.SimpleResultSet;
import org.h2.util.JdbcUtils;
import org.h2.util.LocalDateTimeUtils;
import org.h2.util.New;
import org.h2.util.Utils;
......@@ -957,6 +959,14 @@ public class DataType {
return Value.ARRAY;
} else if (isGeometryClass(x)) {
return Value.GEOMETRY;
} else if (LocalDateTimeUtils.isLocalDate(x)) {
return Value.DATE;
} else if (LocalDateTimeUtils.isLocalTime(x)) {
return Value.TIME;
} else if (LocalDateTimeUtils.isLocalDateTime(x)) {
return Value.TIMESTAMP;
} else if (LocalDateTimeUtils.isOffsetDateTime(x)) {
return Value.TIMESTAMP_TZ;
} else {
return Value.JAVA_OBJECT;
}
......@@ -1065,11 +1075,20 @@ public class DataType {
return ValueStringFixed.get(((Character) x).toString());
} else if (isGeometry(x)) {
return ValueGeometry.getFromGeometry(x);
} else if (LocalDateTimeUtils.isLocalDate(x.getClass())) {
return LocalDateTimeUtils.localDateToDateValue(x);
} else if (LocalDateTimeUtils.isLocalTime(x.getClass())) {
return LocalDateTimeUtils.localTimeToTimeValue(x);
} else if (LocalDateTimeUtils.isLocalDateTime(x.getClass())) {
return LocalDateTimeUtils.localDateTimeToValue(x);
} else if (LocalDateTimeUtils.isOffsetDateTime(x.getClass())) {
return LocalDateTimeUtils.offsetDateTimeToValue(x);
} else {
return ValueJavaObject.getNoCopy(x, null, session.getDataHandler());
}
}
/**
* Check whether a given class matches the Geometry class.
*
......
......@@ -27,6 +27,7 @@ import org.h2.test.TestBase;
import org.h2.tools.SimpleResultSet;
import org.h2.util.IOUtils;
import org.h2.util.JdbcUtils;
import org.h2.util.LocalDateTimeUtils;
import org.h2.util.Utils;
/**
......@@ -170,17 +171,29 @@ public class TestCallableStatement extends TestBase {
call.registerOutParameter(1, Types.DATE);
call.execute();
assertEquals("2000-01-01", call.getDate(1).toString());
if (LocalDateTimeUtils.isJava8DateApiPresent()) {
assertEquals("2000-01-01", call.getObject(1,
LocalDateTimeUtils.getLocalDateClass()).toString());
}
call.setTime(2, java.sql.Time.valueOf("01:02:03"));
call.registerOutParameter(1, Types.TIME);
call.execute();
assertEquals("01:02:03", call.getTime(1).toString());
if (LocalDateTimeUtils.isJava8DateApiPresent()) {
assertEquals("01:02:03", call.getObject(1,
LocalDateTimeUtils.getLocalTimeClass()).toString());
}
call.setTimestamp(2, java.sql.Timestamp.valueOf(
"2001-02-03 04:05:06.789"));
call.registerOutParameter(1, Types.TIMESTAMP);
call.execute();
assertEquals("2001-02-03 04:05:06.789", call.getTimestamp(1).toString());
if (LocalDateTimeUtils.isJava8DateApiPresent()) {
assertEquals("2001-02-03T04:05:06.789", call.getObject(1,
LocalDateTimeUtils.getLocalDateTimeClass()).toString());
}
call.setBoolean(2, true);
call.registerOutParameter(1, Types.BIT);
......@@ -264,10 +277,28 @@ public class TestCallableStatement extends TestBase {
assertEquals("2001-02-03 10:20:30.0", call.getTimestamp(4).toString());
assertEquals("2001-02-03 10:20:30.0", call.getTimestamp("D").toString());
if (LocalDateTimeUtils.isJava8DateApiPresent()) {
assertEquals("2001-02-03T10:20:30", call.getObject(4,
LocalDateTimeUtils.getLocalDateTimeClass()).toString());
assertEquals("2001-02-03T10:20:30", call.getObject("D",
LocalDateTimeUtils.getLocalDateTimeClass()).toString());
}
assertEquals("10:20:30", call.getTime(4).toString());
assertEquals("10:20:30", call.getTime("D").toString());
if (LocalDateTimeUtils.isJava8DateApiPresent()) {
assertEquals("10:20:30", call.getObject(4,
LocalDateTimeUtils.getLocalTimeClass()).toString());
assertEquals("10:20:30", call.getObject("D",
LocalDateTimeUtils.getLocalTimeClass()).toString());
}
assertEquals("2001-02-03", call.getDate(4).toString());
assertEquals("2001-02-03", call.getDate("D").toString());
if (LocalDateTimeUtils.isJava8DateApiPresent()) {
assertEquals("2001-02-03", call.getObject(4,
LocalDateTimeUtils.getLocalDateClass()).toString());
assertEquals("2001-02-03", call.getObject("D",
LocalDateTimeUtils.getLocalDateClass()).toString());
}
assertEquals(100, call.getInt(1));
assertEquals(100, call.getInt("A"));
......
......@@ -24,9 +24,11 @@ import java.sql.Statement;
import java.sql.Timestamp;
import java.sql.Types;
import java.util.UUID;
import org.h2.api.ErrorCode;
import org.h2.api.Trigger;
import org.h2.test.TestBase;
import org.h2.util.LocalDateTimeUtils;
import org.h2.util.Task;
/**
......@@ -71,6 +73,10 @@ public class TestPreparedStatement extends TestBase {
testCoalesce(conn);
testPreparedStatementMetaData(conn);
testDate(conn);
testDate8(conn);
testTime8(conn);
testDateTime8(conn);
testOffsetDateTime8(conn);
testArray(conn);
testUUIDGeneratedKeys(conn);
testSetObject(conn);
......@@ -589,6 +595,62 @@ public class TestPreparedStatement extends TestBase {
assertEquals(ts.toString(), ts2.toString());
}
private void testDate8(Connection conn) throws SQLException {
if (!LocalDateTimeUtils.isJava8DateApiPresent()) {
return;
}
PreparedStatement prep = conn.prepareStatement("SELECT ?");
Object localDate = LocalDateTimeUtils.parseLocalDate("2001-02-03");
prep.setObject(1, localDate);
ResultSet rs = prep.executeQuery();
rs.next();
Object localDate2 = rs.getObject(1, LocalDateTimeUtils.getLocalDateClass());
assertEquals(localDate, localDate2);
rs.close();
}
private void testTime8(Connection conn) throws SQLException {
if (!LocalDateTimeUtils.isJava8DateApiPresent()) {
return;
}
PreparedStatement prep = conn.prepareStatement("SELECT ?");
Object localTime = LocalDateTimeUtils.parseLocalTime("04:05:06");
prep.setObject(1, localTime);
ResultSet rs = prep.executeQuery();
rs.next();
Object localTime2 = rs.getObject(1, LocalDateTimeUtils.getLocalTimeClass());
assertEquals(localTime, localTime2);
rs.close();
}
private void testDateTime8(Connection conn) throws SQLException {
if (!LocalDateTimeUtils.isJava8DateApiPresent()) {
return;
}
PreparedStatement prep = conn.prepareStatement("SELECT ?");
Object localDateTime = LocalDateTimeUtils.parseLocalDateTime("2001-02-03T04:05:06");
prep.setObject(1, localDateTime);
ResultSet rs = prep.executeQuery();
rs.next();
Object localDateTime2 = rs.getObject(1, LocalDateTimeUtils.getLocalDateClass());
assertEquals(localDateTime, localDateTime2);
rs.close();
}
private void testOffsetDateTime8(Connection conn) throws SQLException {
if (!LocalDateTimeUtils.isJava8DateApiPresent()) {
return;
}
PreparedStatement prep = conn.prepareStatement("SELECT ?");
Object offsetDateTime = LocalDateTimeUtils.parseOffsetDateTime("2001-02-03T04:05:06+02:30");
prep.setObject(1, offsetDateTime);
ResultSet rs = prep.executeQuery();
rs.next();
Object offsetDateTime2 = rs.getObject(1, LocalDateTimeUtils.getOffsetDateTimeClass());
assertEquals(offsetDateTime, offsetDateTime2);
rs.close();
}
private void testPreparedSubquery(Connection conn) throws SQLException {
Statement s = conn.createStatement();
s.executeUpdate("CREATE TABLE TEST(ID IDENTITY, FLAG BIT)");
......
......@@ -37,6 +37,7 @@ import java.util.TimeZone;
import org.h2.api.ErrorCode;
import org.h2.test.TestBase;
import org.h2.util.IOUtils;
import org.h2.util.LocalDateTimeUtils;
/**
* Tests for the ResultSet implementation.
......@@ -1057,18 +1058,44 @@ public class TestResultSet extends TestBase {
assertEquals("2002-02-02 02:02:02.0", ts.toString());
rs.next();
assertEquals("1800-01-01", rs.getDate("value").toString());
if (LocalDateTimeUtils.isJava8DateApiPresent()) {
assertEquals("1800-01-01", rs.getObject("value",
LocalDateTimeUtils.getLocalDateClass()).toString());
}
assertEquals("00:00:00", rs.getTime("value").toString());
assertEquals("1800-01-01 00:00:00.0",
rs.getTimestamp("value").toString());
if (LocalDateTimeUtils.isJava8DateApiPresent()) {
assertEquals("00:00", rs.getObject("value",
LocalDateTimeUtils.getLocalTimeClass()).toString());
}
assertEquals("1800-01-01 00:00:00.0", rs.getTimestamp("value").toString());
if (LocalDateTimeUtils.isJava8DateApiPresent()) {
assertEquals("1800-01-01T00:00", rs.getObject("value",
LocalDateTimeUtils.getLocalDateTimeClass()).toString());
}
rs.next();
assertEquals("9999-12-31", rs.getDate("Value").toString());
if (LocalDateTimeUtils.isJava8DateApiPresent()) {
assertEquals("9999-12-31", rs.getObject("Value",
LocalDateTimeUtils.getLocalDateClass()).toString());
}
assertEquals("23:59:59", rs.getTime("Value").toString());
assertEquals("9999-12-31 23:59:59.0",
rs.getTimestamp("Value").toString());
if (LocalDateTimeUtils.isJava8DateApiPresent()) {
assertEquals("23:59:59", rs.getObject("Value",
LocalDateTimeUtils.getLocalTimeClass()).toString());
}
assertEquals("9999-12-31 23:59:59.0", rs.getTimestamp("Value").toString());
if (LocalDateTimeUtils.isJava8DateApiPresent()) {
assertEquals("9999-12-31T23:59:59", rs.getObject("Value",
LocalDateTimeUtils.getLocalDateTimeClass()).toString());
}
rs.next();
assertTrue(rs.getDate("Value") == null && rs.wasNull());
assertTrue(rs.getTime("vALUe") == null && rs.wasNull());
assertTrue(rs.getTimestamp(2) == null && rs.wasNull());
if (LocalDateTimeUtils.isJava8DateApiPresent()) {
assertTrue(rs.getObject(2,
LocalDateTimeUtils.getLocalDateTimeClass()) == null && rs.wasNull());
}
assertTrue(!rs.next());
rs = stat.executeQuery("SELECT DATE '2001-02-03' D, " +
......@@ -1087,6 +1114,18 @@ public class TestResultSet extends TestBase {
assertEquals("2001-02-03", date.toString());
assertEquals("14:15:16", time.toString());
assertEquals("2007-08-09 10:11:12.141516171", ts.toString());
if (LocalDateTimeUtils.isJava8DateApiPresent()) {
assertEquals("2001-02-03", rs.getObject(1,
LocalDateTimeUtils.getLocalDateClass()).toString());
}
if (LocalDateTimeUtils.isJava8DateApiPresent()) {
assertEquals("14:15:16", rs.getObject(2,
LocalDateTimeUtils.getLocalTimeClass()).toString());
}
if (LocalDateTimeUtils.isJava8DateApiPresent()) {
assertEquals("2007-08-09T10:11:12.141516171",
rs.getObject(3, LocalDateTimeUtils.getLocalDateTimeClass()).toString());
}
stat.execute("DROP TABLE TEST");
}
......
......@@ -11,6 +11,7 @@ import java.sql.SQLException;
import java.sql.Statement;
import org.h2.api.TimestampWithTimeZone;
import org.h2.test.TestBase;
import org.h2.util.LocalDateTimeUtils;
import org.h2.value.ValueTimestampTimeZone;
/**
......@@ -57,6 +58,10 @@ public class TestTimeStampWithTimeZone extends TestBase {
assertEquals(1, ts.getDay());
assertEquals(15, ts.getTimeZoneOffsetMins());
assertEquals(new TimestampWithTimeZone(1008673L, 43200000000000L, (short) 15), ts);
if (LocalDateTimeUtils.isJava8DateApiPresent()) {
assertEquals("1970-01-01T12:00+00:15", rs.getObject(1,
LocalDateTimeUtils.getOffsetDateTimeClass()).toString());
}
rs.next();
ts = (TimestampWithTimeZone) rs.getObject(1);
assertEquals(2016, ts.getYear());
......@@ -64,6 +69,10 @@ public class TestTimeStampWithTimeZone extends TestBase {
assertEquals(24, ts.getDay());
assertEquals(1, ts.getTimeZoneOffsetMins());
assertEquals(1L, ts.getNanosSinceMidnight());
if (LocalDateTimeUtils.isJava8DateApiPresent()) {
assertEquals("2016-09-24T00:00:00.000000001+00:01", rs.getObject(1,
LocalDateTimeUtils.getOffsetDateTimeClass()).toString());
}
rs.next();
ts = (TimestampWithTimeZone) rs.getObject(1);
assertEquals(2016, ts.getYear());
......@@ -71,16 +80,28 @@ public class TestTimeStampWithTimeZone extends TestBase {
assertEquals(24, ts.getDay());
assertEquals(-1, ts.getTimeZoneOffsetMins());
assertEquals(1L, ts.getNanosSinceMidnight());
if (LocalDateTimeUtils.isJava8DateApiPresent()) {
assertEquals("2016-09-24T00:00:00.000000001-00:01", rs.getObject(1,
LocalDateTimeUtils.getOffsetDateTimeClass()).toString());
}
rs.next();
ts = (TimestampWithTimeZone) rs.getObject(1);
assertEquals(2016, ts.getYear());
assertEquals(1, ts.getMonth());
assertEquals(1, ts.getDay());
if (LocalDateTimeUtils.isJava8DateApiPresent()) {
assertEquals("2016-01-01T05:00+10:00", rs.getObject(1,
LocalDateTimeUtils.getOffsetDateTimeClass()).toString());
}
rs.next();
ts = (TimestampWithTimeZone) rs.getObject(1);
assertEquals(2015, ts.getYear());
assertEquals(12, ts.getMonth());
assertEquals(31, ts.getDay());
if (LocalDateTimeUtils.isJava8DateApiPresent()) {
assertEquals("2015-12-31T19:00-10:00", rs.getObject(1,
LocalDateTimeUtils.getOffsetDateTimeClass()).toString());
}
rs.close();
stat.close();
conn.close();
......@@ -92,7 +113,7 @@ public class TestTimeStampWithTimeZone extends TestBase {
int c = a.compareTo(b, null);
assertEquals(c, 1);
}
private void test3() {
ValueTimestampTimeZone a = ValueTimestampTimeZone.parse("1970-01-02 00:00:02.00+01:15");
ValueTimestampTimeZone b = ValueTimestampTimeZone.parse("1970-01-01 23:00:01.00+00:15");
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论