LocalDateTime和SQL Server JDBC 4.2驱动程序

时间:2018-07-10 21:14:50

标签: java sql-server jdbc mssql-jdbc

我正在尝试将新的java.time类与最新版本的Sql Server 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:不支持转换为类java.time.LocalDateTime。

驱动程序版本:mssql-jdbc-6.5.4.jre8-preview.jar

SQL Server版本:2016


https://docs.microsoft.com/en-us/sql/connect/jdbc/jdbc-4-2-compliance-for-the-jdbc-driver?view=sql-server-2017

如何解释底部表格中的这句话:

Java 8中的新Java类: 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.

4 个答案:

答案 0 :(得分:2)

这是因为resultSet.getObject(...)的Microsoft SQL Server JDBC驱动程序实现无法从java.sql.Timestamp自动转换为LocalDateTime

作为一种解决方法,您可以获取值为java.sql.Timestamp,然后使用以下方法将java.sql.Timestamp转换为LocalDateTimejava.sql.Timestamp.toLocalDateTime()

LocalDateTime dateTime = resultSet.getTimestamp("date_from").toLocalDateTime()

答案 1 :(得分:1)

  

我不知道我在做什么错。

您没有做错任何事情。 here报告说,您遇到了Microsoft JDBC驱动程序的缺陷。

因此,正如其他人所建议的,暂时需要检索时间戳并将其转换为LocalDateTime。但是,请注意,这种简单的解决方案

LocalDateTime dateTime = resultSet.getTimestamp("date_from").toLocalDateTime()
如果JVM的默认时区遵循夏令时,也就是“夏令时”,则

将破坏某些日期/时间值。例如,

// 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”。

相反,您需要将时间戳记提取为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”。

答案 2 :(得分:0)

mssql-jdbc驱动程序似乎没有完全实现JDBC 4.2中指定的java.time支持。 ResultSet.getObject不支持它,但是PreparedStatement.setObject不支持它。

根据Loc Le的答案建议,解决方法是以Timestamp的身份检索并将其转换为LocalDateTime

答案 3 :(得分:0)

如果您遇到困难并想尝试“旧方法”:

这是等效转换的简单示例。测试自己。

@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);
}