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

Loading…
Cancel
Save