Avoid conversion warnings for store specific simple types.

Dialects now can define a set of known simple types the driver can handle without further interaction. This is done to avoid warnings during converter registration for types known in one environment but not the other.
Also move types around a bit, change visibility and make sure jdbc specific dialects inherit converters from their parents.

Original pull request #981
See #935
This commit is contained in:
Christoph Strobl
2021-05-28 12:58:35 +02:00
committed by Jens Schauder
parent 6114d8e1fd
commit 35589af3d3
11 changed files with 118 additions and 63 deletions

View File

@@ -16,6 +16,7 @@
package org.springframework.data.jdbc.core.convert;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.function.Predicate;
@@ -30,14 +31,14 @@ import org.springframework.data.jdbc.core.mapping.JdbcSimpleTypes;
*
* @author Mark Paluch
* @author Jens Schauder
* @author Christoph Strobl
* @see CustomConversions
* @see org.springframework.data.mapping.model.SimpleTypeHolder
* @see JdbcSimpleTypes
*/
public class JdbcCustomConversions extends CustomConversions {
public static final List<Object> STORE_CONVERTERS = Arrays
.asList(Jsr310TimestampBasedConverters.getConvertersToRegister().toArray());
private static final Collection<Object> STORE_CONVERTERS = Collections.unmodifiableCollection(Jsr310TimestampBasedConverters.getConvertersToRegister());
private static final StoreConversions STORE_CONVERSIONS = StoreConversions.of(JdbcSimpleTypes.HOLDER,
STORE_CONVERTERS);
@@ -77,6 +78,16 @@ public class JdbcCustomConversions extends CustomConversions {
super(converterConfiguration);
}
/**
* Obtain a read only copy of default store converters.
*
* @return never {@literal null}.
* @since 2.3
*/
public static Collection<Object> storeConverters() {
return STORE_CONVERTERS;
}
private static boolean isDateTimeApiConversion(ConvertiblePair cp) {
if (cp.getSourceType().equals(java.util.Date.class)) {

View File

@@ -15,22 +15,29 @@
*/
package org.springframework.data.jdbc.core.dialect;
import java.sql.Timestamp;
import java.time.OffsetDateTime;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.springframework.core.convert.converter.Converter;
import org.springframework.data.convert.WritingConverter;
import org.springframework.data.relational.core.dialect.Db2Dialect;
/**
* {@link Db2Dialect} that registers JDBC specific converters.
*
* @author Jens Schauder
* @author Christoph Strobl
* @since 2.3
*/
public class JdbcDb2Dialect extends Db2Dialect {
public static JdbcDb2Dialect INSTANCE = new JdbcDb2Dialect();
protected JdbcDb2Dialect() {}
@Override
public Collection<Object> getConverters() {
@@ -40,4 +47,21 @@ public class JdbcDb2Dialect extends Db2Dialect {
return converters;
}
/**
* {@link WritingConverter} from {@link OffsetDateTime} to {@link Timestamp}. The conversion preserves the
* {@link java.time.Instant} represented by {@link OffsetDateTime}
*
* @author Jens Schauder
* @since 2.3
*/
@WritingConverter
enum OffsetDateTimeToTimestampConverter implements Converter<OffsetDateTime, Timestamp> {
INSTANCE;
@Override
public Timestamp convert(OffsetDateTime source) {
return Timestamp.from(source.toInstant());
}
}
}

View File

@@ -15,36 +15,43 @@
*/
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;
import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import java.util.Collection;
import java.util.Collections;
/**
* {@link Db2Dialect} that registers JDBC specific converters.
*
* @author Jens Schauder
* @author Christoph Strobl
* @since 2.3
*/
public class JdbcH2Dialect extends H2Dialect {
public static JdbcH2Dialect INSTANCE = new JdbcH2Dialect();
protected JdbcH2Dialect() {}
@Override
public Collection<Object> getConverters() {
return Collections.singletonList(TimestampWithTimeZoneToOffsetDateTimeConverter.INSTANCE);
List<Object> converters = new ArrayList<>(super.getConverters());
converters.add(TimestampWithTimeZoneToOffsetDateTimeConverter.INSTANCE);
return converters;
}
@ReadingConverter
enum TimestampWithTimeZoneToOffsetDateTimeConverter implements Converter<TimestampWithTimeZone, OffsetDateTime> {
INSTANCE;
INSTANCE;
@Override
public OffsetDateTime convert(TimestampWithTimeZone source) {
@@ -67,8 +74,8 @@ public class JdbcH2Dialect extends H2Dialect {
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 );
return OffsetDateTime.of(source.getYear(), source.getMonth(), source.getDay(), (int) hours, (int) minutes,
(int) seconds, (int) nanosLeft, offset);
}
}
}

View File

@@ -31,6 +31,7 @@ import org.springframework.data.relational.core.sql.IdentifierProcessing;
* {@link Db2Dialect} that registers JDBC specific converters.
*
* @author Jens Schauder
* @author Christoph Strobl
* @since 2.3
*/
public class JdbcMySqlDialect extends MySqlDialect {
@@ -39,6 +40,8 @@ public class JdbcMySqlDialect extends MySqlDialect {
super(identifierProcessing);
}
protected JdbcMySqlDialect() {}
@Override
public Collection<Object> getConverters() {
@@ -50,6 +53,7 @@ public class JdbcMySqlDialect extends MySqlDialect {
@WritingConverter
enum OffsetDateTimeToTimestampJdbcValueConverter implements Converter<OffsetDateTime, JdbcValue> {
INSTANCE;
@Override

View File

@@ -18,8 +18,9 @@ package org.springframework.data.jdbc.core.dialect;
import microsoft.sql.DateTimeOffset;
import java.time.OffsetDateTime;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import org.springframework.core.convert.converter.Converter;
import org.springframework.data.convert.ReadingConverter;
@@ -29,6 +30,7 @@ import org.springframework.data.relational.core.dialect.SqlServerDialect;
* {@link SqlServerDialect} that registers JDBC specific converters.
*
* @author Jens Schauder
* @author Christoph Strobl
* @since 2.3
*/
public class JdbcSqlServerDialect extends SqlServerDialect {
@@ -37,11 +39,15 @@ public class JdbcSqlServerDialect extends SqlServerDialect {
@Override
public Collection<Object> getConverters() {
return Collections.singletonList(DateTimeOffsetToOffsetDateTimeConverter.INSTANCE);
List<Object> converters = new ArrayList<>(super.getConverters());
converters.add(DateTimeOffsetToOffsetDateTimeConverter.INSTANCE);
return converters;
}
@ReadingConverter
enum DateTimeOffsetToOffsetDateTimeConverter implements Converter<DateTimeOffset, OffsetDateTime> {
INSTANCE;
@Override

View File

@@ -1,41 +0,0 @@
/*
* 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 org.springframework.core.convert.converter.Converter;
import org.springframework.data.convert.WritingConverter;
import org.springframework.data.relational.core.dialect.Db2Dialect;
import java.sql.Timestamp;
import java.time.OffsetDateTime;
import java.time.ZoneOffset;
/**
* {@link WritingConverter} from {@link OffsetDateTime} to {@link Timestamp}.
* The conversion preserves the {@link java.time.Instant} represented by {@link OffsetDateTime}
*
* @author Jens Schauder
* @since 2.3
*/
@WritingConverter
enum OffsetDateTimeToTimestampConverter implements Converter<OffsetDateTime, Timestamp> {
INSTANCE;
@Override
public Timestamp convert(OffsetDateTime source) {
return Timestamp.from(source.toInstant());
}
}

View File

@@ -31,6 +31,7 @@ import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
import org.springframework.core.convert.converter.Converter;
import org.springframework.data.convert.CustomConversions;
import org.springframework.data.convert.CustomConversions.StoreConversions;
import org.springframework.data.jdbc.core.JdbcAggregateOperations;
import org.springframework.data.jdbc.core.JdbcAggregateTemplate;
import org.springframework.data.jdbc.core.convert.BasicJdbcConverter;
@@ -41,9 +42,12 @@ import org.springframework.data.jdbc.core.convert.JdbcConverter;
import org.springframework.data.jdbc.core.convert.JdbcCustomConversions;
import org.springframework.data.jdbc.core.convert.RelationResolver;
import org.springframework.data.jdbc.core.convert.SqlGeneratorSource;
import org.springframework.data.jdbc.core.dialect.JdbcDb2Dialect;
import org.springframework.data.jdbc.core.mapping.JdbcMappingContext;
import org.springframework.data.jdbc.core.mapping.JdbcSimpleTypes;
import org.springframework.data.mapping.model.SimpleTypeHolder;
import org.springframework.data.relational.core.conversion.RelationalConverter;
import org.springframework.data.relational.core.dialect.Db2Dialect;
import org.springframework.data.relational.core.dialect.Dialect;
import org.springframework.data.relational.core.mapping.NamingStrategy;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations;
@@ -116,9 +120,11 @@ public class AbstractJdbcConfiguration implements ApplicationContextAware {
try {
Dialect dialect = applicationContext.getBean(Dialect.class);
SimpleTypeHolder simpleTypeHolder = dialect.simpleTypes().isEmpty() ? JdbcSimpleTypes.HOLDER : new SimpleTypeHolder(dialect.simpleTypes(), JdbcSimpleTypes.HOLDER);
return new JdbcCustomConversions(
CustomConversions.StoreConversions.of(JdbcSimpleTypes.HOLDER, storeConverters(dialect)), userConverters());
CustomConversions.StoreConversions.of(simpleTypeHolder, storeConverters(dialect)), userConverters());
} catch (NoSuchBeanDefinitionException exception) {
LOG.warn("No dialect found. CustomConversions will be configured without dialect specific conversions.");
@@ -135,7 +141,7 @@ public class AbstractJdbcConfiguration implements ApplicationContextAware {
List<Object> converters = new ArrayList<>();
converters.addAll(dialect.getConverters());
converters.addAll(JdbcCustomConversions.STORE_CONVERTERS);
converters.addAll(JdbcCustomConversions.storeConverters());
return converters;
}

View File

@@ -25,7 +25,7 @@ import static org.assertj.core.api.Assertions.*;
*/
/**
* Tests for {@link OffsetDateTimeToTimestampConverter}.
* Tests for {@link JdbcDb2Dialect.OffsetDateTimeToTimestampConverter}.
*
* @author Jens Schauder
*/
@@ -36,8 +36,8 @@ class OffsetDateTimeToTimestampConverterUnitTests {
OffsetDateTime offsetDateTime = OffsetDateTime.of(5, 5, 5, 5,5,5,123456789, ZoneOffset.ofHours(3));
Timestamp timestamp = OffsetDateTimeToTimestampConverter.INSTANCE.convert(offsetDateTime);
Timestamp timestamp = JdbcDb2Dialect.OffsetDateTimeToTimestampConverter.INSTANCE.convert(offsetDateTime);
assertThat(timestamp.toInstant()).isEqualTo(offsetDateTime.toInstant());
}
}
}

View File

@@ -16,7 +16,6 @@
package org.springframework.data.jdbc.testing;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
@@ -45,6 +44,7 @@ import org.springframework.data.jdbc.core.mapping.JdbcMappingContext;
import org.springframework.data.jdbc.core.mapping.JdbcSimpleTypes;
import org.springframework.data.jdbc.repository.config.DialectResolver;
import org.springframework.data.jdbc.repository.support.JdbcRepositoryFactory;
import org.springframework.data.mapping.model.SimpleTypeHolder;
import org.springframework.data.relational.core.dialect.Dialect;
import org.springframework.data.relational.core.mapping.NamingStrategy;
import org.springframework.data.relational.core.mapping.RelationalMappingContext;
@@ -62,6 +62,7 @@ import org.springframework.transaction.PlatformTransactionManager;
* @author Mark Paluch
* @author Fei Dong
* @author Myeonghyeon Lee
* @author Christoph Strobl
*/
@Configuration
@ComponentScan // To pick up configuration classes (per activated profile)
@@ -114,15 +115,19 @@ public class TestConfiguration {
@Bean
CustomConversions jdbcCustomConversions(Dialect dialect) {
return new JdbcCustomConversions(CustomConversions.StoreConversions.of(JdbcSimpleTypes.HOLDER,
storeConverters(dialect)), Collections.emptyList());
SimpleTypeHolder simpleTypeHolder = dialect.simpleTypes().isEmpty() ? JdbcSimpleTypes.HOLDER
: new SimpleTypeHolder(dialect.simpleTypes(), JdbcSimpleTypes.HOLDER);
return new JdbcCustomConversions(CustomConversions.StoreConversions.of(simpleTypeHolder, storeConverters(dialect)),
Collections.emptyList());
}
private List<Object> storeConverters(Dialect dialect) {
List<Object> converters = new ArrayList<>();
converters.addAll(dialect.getConverters());
converters.addAll(JdbcCustomConversions.STORE_CONVERTERS);
converters.addAll(JdbcCustomConversions.storeConverters());
return converters;
}

View File

@@ -17,6 +17,7 @@ package org.springframework.data.relational.core.dialect;
import java.util.Collection;
import java.util.Collections;
import java.util.Set;
import org.springframework.data.relational.core.sql.IdentifierProcessing;
import org.springframework.data.relational.core.sql.SqlIdentifier;
@@ -30,6 +31,7 @@ import org.springframework.data.relational.core.sql.render.SelectRenderContext;
* @author Mark Paluch
* @author Jens Schauder
* @author Myeonghyeon Lee
* @author Christoph Strobl
* @since 1.1
*/
public interface Dialect {
@@ -97,4 +99,14 @@ public interface Dialect {
default Collection<Object> getConverters() {
return Collections.emptySet();
}
/**
* Return the {@link Set} of types considered store native types that can be handeled by the driver.
*
* @return never {@literal null}.
* @since 2.3
*/
default Set<Class<?>> simpleTypes() {
return Collections.emptySet();
}
}

View File

@@ -15,6 +15,9 @@
*/
package org.springframework.data.relational.core.dialect;
import java.util.Collections;
import java.util.Set;
import org.springframework.data.relational.core.sql.IdentifierProcessing;
import org.springframework.data.relational.core.sql.IdentifierProcessing.LetterCasing;
import org.springframework.data.relational.core.sql.IdentifierProcessing.Quoting;
@@ -26,6 +29,7 @@ import org.springframework.util.ClassUtils;
*
* @author Mark Paluch
* @author Myeonghyeon Lee
* @author Christph Strobl
* @since 2.0
*/
public class H2Dialect extends AbstractDialect {
@@ -137,4 +141,21 @@ public class H2Dialect extends AbstractDialect {
public IdentifierProcessing getIdentifierProcessing() {
return IdentifierProcessing.create(Quoting.ANSI, LetterCasing.UPPER_CASE);
}
/*
* (non-Javadoc)
* @see org.springframework.data.relational.core.dialect.Dialect#simpleTypes()
*/
@Override
public Set<Class<?>> simpleTypes() {
if (!ClassUtils.isPresent("org.h2.api.TimestampWithTimeZone", getClass().getClassLoader())) {
return Collections.emptySet();
}
try {
return Collections.singleton(ClassUtils.forName("org.h2.api.TimestampWithTimeZone", getClass().getClassLoader()));
} catch (ClassNotFoundException e) {
throw new IllegalStateException(e);
}
}
}