Estoy tratando de utilizar las nuevas java.time
clases con más reciente versión del controlador JDBC de SQL Server. Mientras lo leía simplemente debe trabajar con métodos: PreparedStatement.setObject()
e ResultSet.getObject()
.
Así que creé código de ejemplo, y no lo puede operar con conjuntos de resultados. No sé lo que estoy haciendo mal aquí.
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);
}
Esto arroja una excepción:
com.microsoft.sqlserver.jdbc.SQLServerException: La conversión a la clase java.time.LocalDateTime no es compatible.
versión del controlador: mssql-JDBC-6.5.4.jre8-preview.jar
Versión de servidor SQL: 2016
¿Cómo interpretar esta frase en el cuadro en la parte inferior:
Nuevas clases de Java en Java 8: LocalDate / LocalTime / LocalDateTime, OffsetTime / OffsetDateTime
Nuevos tipos de 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.
No sé lo que estoy haciendo mal aquí.
No estás haciendo mal nada. Se ha encontrado con una deficiencia en el controlador JDBC de Microsoft para SQL Server antes de la versión 7.1.0 , discutido aquí .
Si está utilizando JDBC-mssql versión 7.1.0 o posterior, puede utilizar getObject(x, LocalDateTime.class)
como se esperaba.
Para las versiones de MSSQL-JDBC anteriores a 7.1.0, como otros han sugerido, que necesita para recuperar una Timestamp
y convertirlo en una LocalDateTime
. Sin embargo, tenga en cuenta que la solución simplista ...
LocalDateTime dateTime = resultSet.getTimestamp("date_from").toLocalDateTime()
... corromperá ciertos valores de fecha / hora, si la zona horaria predeterminada para la JVM observa el horario de verano, también conocido como "tiempo de verano". Por ejemplo,
// 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());
imprimirá "2018-03-11T03: 00". Tenga en cuenta que el tiempo es "03:00", no "02:00".
En su lugar, tendrá que recuperar el Timestamp
como UTC y luego convertirlo en una LocalDateTime
de UTC, eliminando así el componente de zona horaria
// 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());
que imprime "2018-03-11T02: 00".