Skip to content

Commit 1acb666

Browse files
committed
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 h2database/h2database#1359
1 parent ee1f420 commit 1acb666

File tree

3 files changed

+78
-33
lines changed

3 files changed

+78
-33
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
/*
2+
* Copyright 2021 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.jdbc.core.dialect;
17+
18+
import java.time.OffsetDateTime;
19+
import java.time.ZoneOffset;
20+
21+
import org.h2.api.TimestampWithTimeZone;
22+
import org.springframework.core.convert.converter.Converter;
23+
import org.springframework.data.convert.ReadingConverter;
24+
25+
/**
26+
* Converter converting from an H2 internal representation of a timestamp with time zone to an OffsetDateTime.
27+
*
28+
* Only required for H2 versions < 2.0
29+
*
30+
* @author Jens Schauder
31+
* @since 2.7
32+
*/
33+
@ReadingConverter
34+
public enum H2TimestampWithTimeZoneToOffsetDateTimeConverter
35+
implements Converter<TimestampWithTimeZone, OffsetDateTime> {
36+
37+
INSTANCE;
38+
39+
@Override
40+
public OffsetDateTime convert(TimestampWithTimeZone source) {
41+
42+
long nanosInSecond = 1_000_000_000;
43+
long nanosInMinute = nanosInSecond * 60;
44+
long nanosInHour = nanosInMinute * 60;
45+
46+
long hours = (source.getNanosSinceMidnight() / nanosInHour);
47+
48+
long nanosInHours = hours * nanosInHour;
49+
long nanosLeft = source.getNanosSinceMidnight() - nanosInHours;
50+
long minutes = nanosLeft / nanosInMinute;
51+
52+
long nanosInMinutes = minutes * nanosInMinute;
53+
nanosLeft -= nanosInMinutes;
54+
long seconds = nanosLeft / nanosInSecond;
55+
56+
long nanosInSeconds = seconds * nanosInSecond;
57+
nanosLeft -= nanosInSeconds;
58+
ZoneOffset offset = ZoneOffset.ofTotalSeconds(source.getTimeZoneOffsetSeconds());
59+
60+
return OffsetDateTime.of(source.getYear(), source.getMonth(), source.getDay(), (int) hours, (int) minutes,
61+
(int) seconds, (int) nanosLeft, offset);
62+
}
63+
}

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

+14-32
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,10 @@
1515
*/
1616
package org.springframework.data.jdbc.core.dialect;
1717

18-
import java.time.OffsetDateTime;
19-
import java.time.ZoneOffset;
2018
import java.util.ArrayList;
2119
import java.util.Collection;
2220
import java.util.List;
2321

24-
import org.h2.api.TimestampWithTimeZone;
25-
import org.springframework.core.convert.converter.Converter;
26-
import org.springframework.data.convert.ReadingConverter;
2722
import org.springframework.data.relational.core.dialect.Db2Dialect;
2823
import org.springframework.data.relational.core.dialect.H2Dialect;
2924

@@ -43,39 +38,26 @@ protected JdbcH2Dialect() {}
4338
@Override
4439
public Collection<Object> getConverters() {
4540

46-
List<Object> converters = new ArrayList<>(super.getConverters());
47-
converters.add(TimestampWithTimeZoneToOffsetDateTimeConverter.INSTANCE);
48-
return converters;
49-
}
50-
51-
@ReadingConverter
52-
enum TimestampWithTimeZoneToOffsetDateTimeConverter implements Converter<TimestampWithTimeZone, OffsetDateTime> {
53-
54-
INSTANCE;
41+
final Collection<Object> originalConverters = super.getConverters();
5542

56-
@Override
57-
public OffsetDateTime convert(TimestampWithTimeZone source) {
43+
if (isH2belowVersion2()) {
5844

59-
long nanosInSecond = 1_000_000_000;
60-
long nanosInMinute = nanosInSecond * 60;
61-
long nanosInHour = nanosInMinute * 60;
62-
63-
long hours = (source.getNanosSinceMidnight() / nanosInHour);
45+
List<Object> converters = new ArrayList<>(originalConverters);
46+
converters.add(H2TimestampWithTimeZoneToOffsetDateTimeConverter.INSTANCE);
47+
return converters;
48+
}
6449

65-
long nanosInHours = hours * nanosInHour;
66-
long nanosLeft = source.getNanosSinceMidnight() - nanosInHours;
67-
long minutes = nanosLeft / nanosInMinute;
50+
return originalConverters;
51+
}
6852

69-
long nanosInMinutes = minutes * nanosInMinute;
70-
nanosLeft -= nanosInMinutes;
71-
long seconds = nanosLeft / nanosInSecond;
53+
static boolean isH2belowVersion2() {
7254

73-
long nanosInSeconds = seconds * nanosInSecond;
74-
nanosLeft -= nanosInSeconds;
75-
ZoneOffset offset = ZoneOffset.ofTotalSeconds(source.getTimeZoneOffsetSeconds());
55+
try {
7656

77-
return OffsetDateTime.of(source.getYear(), source.getMonth(), source.getDay(), (int) hours, (int) minutes,
78-
(int) seconds, (int) nanosLeft, offset);
57+
JdbcH2Dialect.class.getClassLoader().loadClass("org.h2.api.TimestampWithTimeZone");
58+
return true;
59+
} catch (ClassNotFoundException e) {
60+
return false;
7961
}
8062
}
8163
}

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ class JdbcH2DialectTests {
3333
@Test
3434
void TimestampWithTimeZone2OffsetDateTimeConverterConvertsProperly() {
3535

36-
JdbcH2Dialect.TimestampWithTimeZoneToOffsetDateTimeConverter converter = JdbcH2Dialect.TimestampWithTimeZoneToOffsetDateTimeConverter.INSTANCE;
36+
H2TimestampWithTimeZoneToOffsetDateTimeConverter converter = H2TimestampWithTimeZoneToOffsetDateTimeConverter.INSTANCE;
3737
long dateValue = 123456789;
3838
long timeNanos = 987654321;
3939
int timeZoneOffsetSeconds = 4 * 60 * 60;

0 commit comments

Comments
 (0)