Skip to content

Commit 60c0e6e

Browse files
committed
Support for Dialect specific custom conversions and OffsetDateTime.
Dialects may now register a list of converters to take into consideration when reading or writing properties. See `JdbcSqlServerDialect` for an example. By default `OffsetDateTime` does not get converted anymore. If a database needs a conversion it can register it by implementing `Dialect.getConverters()` as described above. Closes #935
1 parent d3fba89 commit 60c0e6e

File tree

36 files changed

+718
-69
lines changed

36 files changed

+718
-69
lines changed

spring-data-jdbc/pom.xml

+2-2
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@
141141
<groupId>com.h2database</groupId>
142142
<artifactId>h2</artifactId>
143143
<version>${h2.version}</version>
144-
<scope>test</scope>
144+
<optional>true</optional>
145145
</dependency>
146146

147147
<dependency>
@@ -190,7 +190,7 @@
190190
<groupId>com.microsoft.sqlserver</groupId>
191191
<artifactId>mssql-jdbc</artifactId>
192192
<version>${mssql.version}</version>
193-
<scope>test</scope>
193+
<optional>true</optional>
194194
</dependency>
195195

196196
<dependency>

spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/JdbcColumnTypes.java

+2
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
package org.springframework.data.jdbc.core.convert;
1717

1818
import java.sql.Timestamp;
19+
import java.time.OffsetDateTime;
1920
import java.time.ZonedDateTime;
2021
import java.time.temporal.Temporal;
2122
import java.util.Date;
@@ -52,6 +53,7 @@ public Class<?> resolvePrimitiveType(Class<?> type) {
5253

5354
javaToDbType.put(Enum.class, String.class);
5455
javaToDbType.put(ZonedDateTime.class, String.class);
56+
javaToDbType.put(OffsetDateTime.class, OffsetDateTime.class);
5557
javaToDbType.put(Temporal.class, Timestamp.class);
5658
}
5759

spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/JdbcCustomConversions.java

+12-2
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import java.util.Arrays;
1919
import java.util.Collections;
2020
import java.util.List;
21+
import java.util.function.Predicate;
2122

2223
import org.springframework.core.convert.converter.GenericConverter.ConvertiblePair;
2324
import org.springframework.data.convert.CustomConversions;
@@ -35,7 +36,7 @@
3536
*/
3637
public class JdbcCustomConversions extends CustomConversions {
3738

38-
private static final List<Object> STORE_CONVERTERS = Arrays
39+
public static final List<Object> STORE_CONVERTERS = Arrays
3940
.asList(Jsr310TimestampBasedConverters.getConvertersToRegister().toArray());
4041
private static final StoreConversions STORE_CONVERSIONS = StoreConversions.of(JdbcSimpleTypes.HOLDER,
4142
STORE_CONVERTERS);
@@ -48,14 +49,23 @@ public JdbcCustomConversions() {
4849
}
4950

5051
/**
51-
* Create a new {@link JdbcCustomConversions} instance registering the given converters.
52+
* Create a new {@link JdbcCustomConversions} instance registering the given converters and the default store converters.
5253
*
5354
* @param converters must not be {@literal null}.
5455
*/
5556
public JdbcCustomConversions(List<?> converters) {
5657
super(new ConverterConfiguration(STORE_CONVERSIONS, converters, JdbcCustomConversions::isDateTimeApiConversion));
5758
}
5859

60+
/**
61+
* Create a new {@link JdbcCustomConversions} instance registering the given converters and the default store converters.
62+
*
63+
* @since 2.3
64+
*/
65+
public JdbcCustomConversions(StoreConversions storeConversions, List<?> userConverters) {
66+
super(new ConverterConfiguration(storeConversions, userConverters, JdbcCustomConversions::isDateTimeApiConversion));
67+
}
68+
5969
/**
6070
* Create a new {@link JdbcCustomConversions} instance given
6171
* {@link org.springframework.data.convert.CustomConversions.ConverterConfiguration}.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
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.util.ArrayList;
19+
import java.util.Collection;
20+
21+
import org.springframework.data.relational.core.dialect.Db2Dialect;
22+
import org.springframework.data.relational.core.sql.IdentifierProcessing;
23+
24+
/**
25+
* {@link Db2Dialect} that registers JDBC specific converters.
26+
*
27+
* @author Jens Schauder
28+
* @since 2.3
29+
*/
30+
public class JdbcDb2Dialect extends Db2Dialect {
31+
32+
public static JdbcDb2Dialect INSTANCE = new JdbcDb2Dialect();
33+
34+
@Override
35+
public Collection<Object> getConverters() {
36+
37+
ArrayList<Object> converters = new ArrayList<>(super.getConverters());
38+
converters.add(OffsetDateTime2TimestampConverter.INSTANCE);
39+
40+
return converters;
41+
}
42+
43+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
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 org.h2.api.TimestampWithTimeZone;
19+
import org.springframework.core.convert.converter.Converter;
20+
import org.springframework.data.convert.ReadingConverter;
21+
import org.springframework.data.relational.core.dialect.Db2Dialect;
22+
import org.springframework.data.relational.core.dialect.H2Dialect;
23+
24+
import java.time.OffsetDateTime;
25+
import java.time.ZoneOffset;
26+
import java.util.Collection;
27+
import java.util.Collections;
28+
29+
/**
30+
* {@link Db2Dialect} that registers JDBC specific converters.
31+
*
32+
* @author Jens Schauder
33+
* @since 2.3
34+
*/
35+
public class JdbcH2Dialect extends H2Dialect {
36+
37+
public static JdbcH2Dialect INSTANCE = new JdbcH2Dialect();
38+
39+
@Override
40+
public Collection<Object> getConverters() {
41+
return Collections.singletonList(TimestampWithTimeZone2OffsetDateTimeConverter.INSTANCE);
42+
}
43+
44+
@ReadingConverter
45+
enum TimestampWithTimeZone2OffsetDateTimeConverter implements Converter<TimestampWithTimeZone, OffsetDateTime> {
46+
INSTANCE;
47+
48+
49+
@Override
50+
public OffsetDateTime convert(TimestampWithTimeZone source) {
51+
52+
long nanosInSecond = 1_000_000_000;
53+
long nanosInMinute = nanosInSecond * 60;
54+
long nanosInHour = nanosInMinute * 60;
55+
56+
long hours = (source.getNanosSinceMidnight() / nanosInHour);
57+
58+
long nanosInHours = hours * nanosInHour;
59+
long nanosLeft = source.getNanosSinceMidnight() - nanosInHours;
60+
long minutes = nanosLeft / nanosInMinute;
61+
62+
long nanosInMinutes = minutes * nanosInMinute;
63+
nanosLeft -= nanosInMinutes;
64+
long seconds = nanosLeft / nanosInSecond;
65+
66+
long nanosInSeconds = seconds * nanosInSecond;
67+
nanosLeft -= nanosInSeconds;
68+
ZoneOffset offset = ZoneOffset.ofTotalSeconds(source.getTimeZoneOffsetSeconds());
69+
70+
return OffsetDateTime.of(source.getYear(), source.getMonth(), source.getDay(), (int)hours, (int)minutes, (int)seconds, (int)nanosLeft, offset );
71+
72+
}
73+
}
74+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
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.sql.JDBCType;
19+
import java.time.OffsetDateTime;
20+
import java.util.ArrayList;
21+
import java.util.Collection;
22+
23+
import org.springframework.core.convert.converter.Converter;
24+
import org.springframework.data.convert.WritingConverter;
25+
import org.springframework.data.jdbc.core.convert.JdbcValue;
26+
import org.springframework.data.relational.core.dialect.Db2Dialect;
27+
import org.springframework.data.relational.core.dialect.MySqlDialect;
28+
import org.springframework.data.relational.core.sql.IdentifierProcessing;
29+
30+
/**
31+
* {@link Db2Dialect} that registers JDBC specific converters.
32+
*
33+
* @author Jens Schauder
34+
* @since 2.3
35+
*/
36+
public class JdbcMySqlDialect extends MySqlDialect {
37+
38+
public JdbcMySqlDialect(IdentifierProcessing identifierProcessing) {
39+
super(identifierProcessing);
40+
}
41+
42+
@Override
43+
public Collection<Object> getConverters() {
44+
45+
ArrayList<Object> converters = new ArrayList<>(super.getConverters());
46+
converters.add(OffsetDateTime2TimestampJdbcValueConverter.INSTANCE);
47+
48+
return converters;
49+
}
50+
51+
@WritingConverter
52+
enum OffsetDateTime2TimestampJdbcValueConverter implements Converter<OffsetDateTime, JdbcValue> {
53+
INSTANCE;
54+
55+
@Override
56+
public JdbcValue convert(OffsetDateTime source) {
57+
return JdbcValue.of(source, JDBCType.TIMESTAMP);
58+
}
59+
}
60+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
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 microsoft.sql.DateTimeOffset;
19+
20+
import java.time.OffsetDateTime;
21+
import java.util.Collection;
22+
import java.util.Collections;
23+
24+
import org.springframework.core.convert.converter.Converter;
25+
import org.springframework.data.convert.ReadingConverter;
26+
import org.springframework.data.relational.core.dialect.Db2Dialect;
27+
import org.springframework.data.relational.core.dialect.SqlServerDialect;
28+
29+
/**
30+
* {@link Db2Dialect} that registers JDBC specific converters.
31+
*
32+
* @author Jens Schauder
33+
* @since 2.3
34+
*/
35+
public class JdbcSqlServerDialect extends SqlServerDialect {
36+
37+
public static JdbcSqlServerDialect INSTANCE = new JdbcSqlServerDialect();
38+
39+
@Override
40+
public Collection<Object> getConverters() {
41+
return Collections.singletonList(DateTimeOffset2OffsetDateTimeConverter.INSTANCE);
42+
}
43+
44+
@ReadingConverter
45+
enum DateTimeOffset2OffsetDateTimeConverter implements Converter<DateTimeOffset, OffsetDateTime> {
46+
INSTANCE;
47+
48+
@Override
49+
public OffsetDateTime convert(DateTimeOffset source) {
50+
return source.getOffsetDateTime();
51+
}
52+
}
53+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
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 org.springframework.core.convert.converter.Converter;
19+
import org.springframework.data.convert.WritingConverter;
20+
import org.springframework.data.relational.core.dialect.Db2Dialect;
21+
22+
import java.sql.Timestamp;
23+
import java.time.OffsetDateTime;
24+
import java.time.ZoneOffset;
25+
26+
/**
27+
* {@link WritingConverter} from {@link OffsetDateTime} to {@link Timestamp}.
28+
* The conversion preserves the {@link java.time.Instant} represented by {@link OffsetDateTime}
29+
*
30+
* @author Jens Schauder
31+
* @since 2.3
32+
*/
33+
@WritingConverter
34+
enum OffsetDateTime2TimestampConverter implements Converter<OffsetDateTime, Timestamp> {
35+
36+
INSTANCE;
37+
@Override
38+
public Timestamp convert(OffsetDateTime source) {
39+
return Timestamp.from(source.toInstant());
40+
}
41+
}

0 commit comments

Comments
 (0)