LocalDateTime 和 SQL 服务器 JDBC 4.2 驱动程序
LocalDateTime and SQL Server JDBC 4.2 driver
我正在尝试将新的 java.time
类 与最新版本的 Sql 服务器 JDBC 驱动程序一起使用。正如我所读,它应该只适用于方法:PreparedStatement.setObject()
和 ResultSet.getObject()
.
所以我创建了示例代码,但无法使用 ResultSets。我不知道我在这里做错了什么。
Connection connection = DriverManager.getConnection(connectionString);
PreparedStatement preparedStatement = connection.prepareStatement("SELECT * FROM myTable WHERE ? BETWEEN date_from AND date_to");
preparedStatement.setObject(1, LocalDateTime.now()); // That works
ResultSet resultSet = preparedStatement.executeQuery();
if (resultSet.next()) {
Object o = resultSet.getObject("date_from");
o.getClass() returns java.sql.Timestamp
LocalDateTime dateTime = resultSet.getObject("date_from", LocalDateTime.class);
}
这会引发异常:
com.microsoft.sqlserver.jdbc.SQLServerException: The conversion to class java.time.LocalDateTime is unsupported.
驱动版本:mssql-jdbc-6.5.4.jre8-preview.jar
SQL服务器版本:2016
下面table这句话怎么解释:
新 Java 类 Java 8:
LocalDate/LocalTime/LocalDateTime,
OffsetTime/OffsetDateTime
新JDBC类型:
TIME_WITH_TIMEZONE,
TIMESTAMP_WITH_TIMEZONE,
REF_CURSOR
REF_CURSOR is not supported in SQL Server. Driver throws a SQLFeatureNotSupportedException exception if this type is used. The driver supports all other new Java and JDBC type mappings as specified in the JDBC 4.2 specification.
这是因为 resultSet.getObject(...)
的 Microsoft SQL Server JDBC 驱动程序实现无法自动从 java.sql.Timestamp
转换为 LocalDateTime
。
作为变通方法,您可以获取 java.sql.Timestamp
的值,然后使用以下方法将 java.sql.Timestamp
转换为 LocalDateTime
:java.sql.Timestamp.toLocalDateTime()
LocalDateTime dateTime = resultSet.getTimestamp("date_from").toLocalDateTime()
看起来 mssql-jdbc 驱动程序没有完全实现 JDBC 4.2 中指定的 java.time 支持。它不支持 ResultSet.getObject
,但它支持 PreparedStatement.setObject
。
如 Loc Le 的回答所建议的解决方法是检索为 Timestamp
并将其转换为 LocalDateTime
.
I don't know what I'm doing wrong here.
你没有做错任何事。在 version 7.1.0, discussed here.
之前,您遇到了 SQL 服务器的 Microsoft JDBC 驱动程序的缺陷
如果您使用的是 mssql-jdbc 版本 7.1.0 或更高版本,那么您可以按预期使用 getObject(x, LocalDateTime.class)
。
对于 7.1.0 之前的 mssql-jdbc 版本,正如其他人所建议的,您需要检索 Timestamp
并将其转换为 LocalDateTime
。但是,请注意简单的解决方案...
LocalDateTime dateTime = resultSet.getTimestamp("date_from").toLocalDateTime()
... 如果 JVM 的默认时区遵守夏令时 a.k.a,将破坏某些 date/time 值。 "Summer Time"。例如,
// time zone with Daylight Time
TimeZone.setDefault(TimeZone.getTimeZone("America/Edmonton"));
// test environment
Statement st = conn.createStatement();
st.execute("CREATE TABLE #tmp (id INT PRIMARY KEY, dt2 DATETIME2)");
st.execute("INSERT INTO #tmp (id, dt2) VALUES (1, '2018-03-11 02:00:00')");
ResultSet rs = st.executeQuery("SELECT dt2 FROM #tmp WHERE id=1");
rs.next();
// test code
LocalDateTime x = rs.getTimestamp("dt2").toLocalDateTime(); // bad
System.out.println(x.toString());
将打印“2018-03-11T03:00”。请注意,时间是“03:00”,而不是“02:00”。
相反,您需要将 Timestamp
检索为 UTC,然后将其转换为 UTC 的 LocalDateTime
,从而删除时区组件
// time zone with Daylight Time
TimeZone.setDefault(TimeZone.getTimeZone("America/Edmonton"));
// test environment
Statement st = conn.createStatement();
st.execute("CREATE TABLE #tmp (id INT PRIMARY KEY, dt2 DATETIME2)");
st.execute("INSERT INTO #tmp (id, dt2) VALUES (1, '2018-03-11 02:00:00')");
ResultSet rs = st.executeQuery("SELECT dt2 FROM #tmp WHERE id=1");
rs.next();
// test code
Timestamp ts = getTimestamp("dt2", Calendar.getInstance(TimeZone.getTimeZone("UTC")));
LocalDateTime x = LocalDateTime.ofInstant(ts.toInstant(), ZoneId.of("UTC")); // good
System.out.println(x.toString());
打印“2018-03-11T02:00”。
如果您遇到困难并想尝试“老办法”:
这里有一个等价转换的简单例子。测试你自己。
@Test
public void testingConversionTimestampAndLocalDateTime(){
Timestamp initialTimestamp = new Timestamp(System.currentTimeMillis());
LocalDateTime localDateTime = LocalDateTime.ofInstant(initialTimestamp.toInstant(), ZoneId.of("America/Sao_Paulo"));
ZoneOffset zoneOffset = ZoneId.of("America/Sao_Paulo").getRules().getOffset(localDateTime);
Timestamp timestamp = Timestamp.from(localDateTime.toInstant(zoneOffset));
Timestamp timestamp1 = Timestamp.valueOf(localDateTime);
assertEquals(initialTimestamp, timestamp);
assertEquals(initialTimestamp, timestamp1);
}
我正在尝试将新的 java.time
类 与最新版本的 Sql 服务器 JDBC 驱动程序一起使用。正如我所读,它应该只适用于方法:PreparedStatement.setObject()
和 ResultSet.getObject()
.
所以我创建了示例代码,但无法使用 ResultSets。我不知道我在这里做错了什么。
Connection connection = DriverManager.getConnection(connectionString);
PreparedStatement preparedStatement = connection.prepareStatement("SELECT * FROM myTable WHERE ? BETWEEN date_from AND date_to");
preparedStatement.setObject(1, LocalDateTime.now()); // That works
ResultSet resultSet = preparedStatement.executeQuery();
if (resultSet.next()) {
Object o = resultSet.getObject("date_from");
o.getClass() returns java.sql.Timestamp
LocalDateTime dateTime = resultSet.getObject("date_from", LocalDateTime.class);
}
这会引发异常:
com.microsoft.sqlserver.jdbc.SQLServerException: The conversion to class java.time.LocalDateTime is unsupported.
驱动版本:mssql-jdbc-6.5.4.jre8-preview.jar
SQL服务器版本:2016
下面table这句话怎么解释:
新 Java 类 Java 8: LocalDate/LocalTime/LocalDateTime, OffsetTime/OffsetDateTime
新JDBC类型: TIME_WITH_TIMEZONE, TIMESTAMP_WITH_TIMEZONE, REF_CURSOR
REF_CURSOR is not supported in SQL Server. Driver throws a SQLFeatureNotSupportedException exception if this type is used. The driver supports all other new Java and JDBC type mappings as specified in the JDBC 4.2 specification.
这是因为 resultSet.getObject(...)
的 Microsoft SQL Server JDBC 驱动程序实现无法自动从 java.sql.Timestamp
转换为 LocalDateTime
。
作为变通方法,您可以获取 java.sql.Timestamp
的值,然后使用以下方法将 java.sql.Timestamp
转换为 LocalDateTime
:java.sql.Timestamp.toLocalDateTime()
LocalDateTime dateTime = resultSet.getTimestamp("date_from").toLocalDateTime()
看起来 mssql-jdbc 驱动程序没有完全实现 JDBC 4.2 中指定的 java.time 支持。它不支持 ResultSet.getObject
,但它支持 PreparedStatement.setObject
。
如 Loc Le 的回答所建议的解决方法是检索为 Timestamp
并将其转换为 LocalDateTime
.
I don't know what I'm doing wrong here.
你没有做错任何事。在 version 7.1.0, discussed here.
之前,您遇到了 SQL 服务器的 Microsoft JDBC 驱动程序的缺陷如果您使用的是 mssql-jdbc 版本 7.1.0 或更高版本,那么您可以按预期使用 getObject(x, LocalDateTime.class)
。
对于 7.1.0 之前的 mssql-jdbc 版本,正如其他人所建议的,您需要检索 Timestamp
并将其转换为 LocalDateTime
。但是,请注意简单的解决方案...
LocalDateTime dateTime = resultSet.getTimestamp("date_from").toLocalDateTime()
... 如果 JVM 的默认时区遵守夏令时 a.k.a,将破坏某些 date/time 值。 "Summer Time"。例如,
// time zone with Daylight Time
TimeZone.setDefault(TimeZone.getTimeZone("America/Edmonton"));
// test environment
Statement st = conn.createStatement();
st.execute("CREATE TABLE #tmp (id INT PRIMARY KEY, dt2 DATETIME2)");
st.execute("INSERT INTO #tmp (id, dt2) VALUES (1, '2018-03-11 02:00:00')");
ResultSet rs = st.executeQuery("SELECT dt2 FROM #tmp WHERE id=1");
rs.next();
// test code
LocalDateTime x = rs.getTimestamp("dt2").toLocalDateTime(); // bad
System.out.println(x.toString());
将打印“2018-03-11T03:00”。请注意,时间是“03:00”,而不是“02:00”。
相反,您需要将 Timestamp
检索为 UTC,然后将其转换为 UTC 的 LocalDateTime
,从而删除时区组件
// time zone with Daylight Time
TimeZone.setDefault(TimeZone.getTimeZone("America/Edmonton"));
// test environment
Statement st = conn.createStatement();
st.execute("CREATE TABLE #tmp (id INT PRIMARY KEY, dt2 DATETIME2)");
st.execute("INSERT INTO #tmp (id, dt2) VALUES (1, '2018-03-11 02:00:00')");
ResultSet rs = st.executeQuery("SELECT dt2 FROM #tmp WHERE id=1");
rs.next();
// test code
Timestamp ts = getTimestamp("dt2", Calendar.getInstance(TimeZone.getTimeZone("UTC")));
LocalDateTime x = LocalDateTime.ofInstant(ts.toInstant(), ZoneId.of("UTC")); // good
System.out.println(x.toString());
打印“2018-03-11T02:00”。
如果您遇到困难并想尝试“老办法”:
这里有一个等价转换的简单例子。测试你自己。
@Test
public void testingConversionTimestampAndLocalDateTime(){
Timestamp initialTimestamp = new Timestamp(System.currentTimeMillis());
LocalDateTime localDateTime = LocalDateTime.ofInstant(initialTimestamp.toInstant(), ZoneId.of("America/Sao_Paulo"));
ZoneOffset zoneOffset = ZoneId.of("America/Sao_Paulo").getRules().getOffset(localDateTime);
Timestamp timestamp = Timestamp.from(localDateTime.toInstant(zoneOffset));
Timestamp timestamp1 = Timestamp.valueOf(localDateTime);
assertEquals(initialTimestamp, timestamp);
assertEquals(initialTimestamp, timestamp1);
}