Browse Source

Fix missing class issue with H2 version 2.0.202

With older versions H2 returned a proprietary  instance of `TimestampWithTimeZone` from `ResultSet.getObject()`.
We used to support the conversion of that to an `OffsetDateTime`.

With the most recent versions `TimestampWithTimeZone` is no longer part of the H2 driver, and we only register the converter when we encounter older versions of H2.

Closes #1114
See https://github.com/h2database/h2database/pull/1359
2.3.x
Jens Schauder 4 years ago
parent
commit
632718b379
No known key found for this signature in database
GPG Key ID: 45CC872F17423DBF
  1. 63
      spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/H2TimestampWithTimeZoneToOffsetDateTimeConverter.java
  2. 46
      spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/JdbcH2Dialect.java
  3. 2
      spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/dialect/JdbcH2DialectTests.java

63
spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/H2TimestampWithTimeZoneToOffsetDateTimeConverter.java

@ -0,0 +1,63 @@ @@ -0,0 +1,63 @@
/*
* Copyright 2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.jdbc.core.dialect;
import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import org.h2.api.TimestampWithTimeZone;
import org.springframework.core.convert.converter.Converter;
import org.springframework.data.convert.ReadingConverter;
/**
* Converter converting from an H2 internal representation of a timestamp with time zone to an OffsetDateTime.
*
* Only required for H2 versions < 2.0
*
* @author Jens Schauder
* @since 2.7
*/
@ReadingConverter
public enum H2TimestampWithTimeZoneToOffsetDateTimeConverter
implements Converter<TimestampWithTimeZone, OffsetDateTime> {
INSTANCE;
@Override
public OffsetDateTime convert(TimestampWithTimeZone source) {
long nanosInSecond = 1_000_000_000;
long nanosInMinute = nanosInSecond * 60;
long nanosInHour = nanosInMinute * 60;
long hours = (source.getNanosSinceMidnight() / nanosInHour);
long nanosInHours = hours * nanosInHour;
long nanosLeft = source.getNanosSinceMidnight() - nanosInHours;
long minutes = nanosLeft / nanosInMinute;
long nanosInMinutes = minutes * nanosInMinute;
nanosLeft -= nanosInMinutes;
long seconds = nanosLeft / nanosInSecond;
long nanosInSeconds = seconds * nanosInSecond;
nanosLeft -= nanosInSeconds;
ZoneOffset offset = ZoneOffset.ofTotalSeconds(source.getTimeZoneOffsetSeconds());
return OffsetDateTime.of(source.getYear(), source.getMonth(), source.getDay(), (int) hours, (int) minutes,
(int) seconds, (int) nanosLeft, offset);
}
}

46
spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/JdbcH2Dialect.java

@ -15,15 +15,10 @@ @@ -15,15 +15,10 @@
*/
package org.springframework.data.jdbc.core.dialect;
import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.h2.api.TimestampWithTimeZone;
import org.springframework.core.convert.converter.Converter;
import org.springframework.data.convert.ReadingConverter;
import org.springframework.data.relational.core.dialect.Db2Dialect;
import org.springframework.data.relational.core.dialect.H2Dialect;
@ -43,39 +38,26 @@ public class JdbcH2Dialect extends H2Dialect { @@ -43,39 +38,26 @@ public class JdbcH2Dialect extends H2Dialect {
@Override
public Collection<Object> getConverters() {
List<Object> converters = new ArrayList<>(super.getConverters());
converters.add(TimestampWithTimeZoneToOffsetDateTimeConverter.INSTANCE);
return converters;
}
@ReadingConverter
enum TimestampWithTimeZoneToOffsetDateTimeConverter implements Converter<TimestampWithTimeZone, OffsetDateTime> {
INSTANCE;
final Collection<Object> originalConverters = super.getConverters();
@Override
public OffsetDateTime convert(TimestampWithTimeZone source) {
if (isH2belowVersion2()) {
long nanosInSecond = 1_000_000_000;
long nanosInMinute = nanosInSecond * 60;
long nanosInHour = nanosInMinute * 60;
long hours = (source.getNanosSinceMidnight() / nanosInHour);
List<Object> converters = new ArrayList<>(originalConverters);
converters.add(H2TimestampWithTimeZoneToOffsetDateTimeConverter.INSTANCE);
return converters;
}
long nanosInHours = hours * nanosInHour;
long nanosLeft = source.getNanosSinceMidnight() - nanosInHours;
long minutes = nanosLeft / nanosInMinute;
return originalConverters;
}
long nanosInMinutes = minutes * nanosInMinute;
nanosLeft -= nanosInMinutes;
long seconds = nanosLeft / nanosInSecond;
static boolean isH2belowVersion2() {
long nanosInSeconds = seconds * nanosInSecond;
nanosLeft -= nanosInSeconds;
ZoneOffset offset = ZoneOffset.ofTotalSeconds(source.getTimeZoneOffsetSeconds());
try {
return OffsetDateTime.of(source.getYear(), source.getMonth(), source.getDay(), (int) hours, (int) minutes,
(int) seconds, (int) nanosLeft, offset);
JdbcH2Dialect.class.getClassLoader().loadClass("org.h2.api.TimestampWithTimeZone");
return true;
} catch (ClassNotFoundException e) {
return false;
}
}
}

2
spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/dialect/JdbcH2DialectTests.java

@ -33,7 +33,7 @@ class JdbcH2DialectTests { @@ -33,7 +33,7 @@ class JdbcH2DialectTests {
@Test
void TimestampWithTimeZone2OffsetDateTimeConverterConvertsProperly() {
JdbcH2Dialect.TimestampWithTimeZoneToOffsetDateTimeConverter converter = JdbcH2Dialect.TimestampWithTimeZoneToOffsetDateTimeConverter.INSTANCE;
H2TimestampWithTimeZoneToOffsetDateTimeConverter converter = H2TimestampWithTimeZoneToOffsetDateTimeConverter.INSTANCE;
long dateValue = 123456789;
long timeNanos = 987654321;
int timeZoneOffsetSeconds = 4 * 60 * 60;

Loading…
Cancel
Save