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 Original pull request #981
This commit is contained in:
@@ -141,7 +141,7 @@
|
||||
<groupId>com.h2database</groupId>
|
||||
<artifactId>h2</artifactId>
|
||||
<version>${h2.version}</version>
|
||||
<scope>test</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -190,7 +190,7 @@
|
||||
<groupId>com.microsoft.sqlserver</groupId>
|
||||
<artifactId>mssql-jdbc</artifactId>
|
||||
<version>${mssql.version}</version>
|
||||
<scope>test</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
package org.springframework.data.jdbc.core.convert;
|
||||
|
||||
import java.sql.Timestamp;
|
||||
import java.time.OffsetDateTime;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.temporal.Temporal;
|
||||
import java.util.Date;
|
||||
@@ -52,6 +53,7 @@ public enum JdbcColumnTypes {
|
||||
|
||||
javaToDbType.put(Enum.class, String.class);
|
||||
javaToDbType.put(ZonedDateTime.class, String.class);
|
||||
javaToDbType.put(OffsetDateTime.class, OffsetDateTime.class);
|
||||
javaToDbType.put(Temporal.class, Timestamp.class);
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ package org.springframework.data.jdbc.core.convert;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import org.springframework.core.convert.converter.GenericConverter.ConvertiblePair;
|
||||
import org.springframework.data.convert.CustomConversions;
|
||||
@@ -35,7 +36,7 @@ import org.springframework.data.jdbc.core.mapping.JdbcSimpleTypes;
|
||||
*/
|
||||
public class JdbcCustomConversions extends CustomConversions {
|
||||
|
||||
private static final List<Object> STORE_CONVERTERS = Arrays
|
||||
public static final List<Object> STORE_CONVERTERS = Arrays
|
||||
.asList(Jsr310TimestampBasedConverters.getConvertersToRegister().toArray());
|
||||
private static final StoreConversions STORE_CONVERSIONS = StoreConversions.of(JdbcSimpleTypes.HOLDER,
|
||||
STORE_CONVERTERS);
|
||||
@@ -48,7 +49,7 @@ public class JdbcCustomConversions extends CustomConversions {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link JdbcCustomConversions} instance registering the given converters.
|
||||
* Create a new {@link JdbcCustomConversions} instance registering the given converters and the default store converters.
|
||||
*
|
||||
* @param converters must not be {@literal null}.
|
||||
*/
|
||||
@@ -56,6 +57,15 @@ public class JdbcCustomConversions extends CustomConversions {
|
||||
super(new ConverterConfiguration(STORE_CONVERSIONS, converters, JdbcCustomConversions::isDateTimeApiConversion));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link JdbcCustomConversions} instance registering the given converters and the default store converters.
|
||||
*
|
||||
* @since 2.3
|
||||
*/
|
||||
public JdbcCustomConversions(StoreConversions storeConversions, List<?> userConverters) {
|
||||
super(new ConverterConfiguration(storeConversions, userConverters, JdbcCustomConversions::isDateTimeApiConversion));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link JdbcCustomConversions} instance given
|
||||
* {@link org.springframework.data.convert.CustomConversions.ConverterConfiguration}.
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* 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.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
|
||||
import org.springframework.data.relational.core.dialect.Db2Dialect;
|
||||
import org.springframework.data.relational.core.sql.IdentifierProcessing;
|
||||
|
||||
/**
|
||||
* {@link Db2Dialect} that registers JDBC specific converters.
|
||||
*
|
||||
* @author Jens Schauder
|
||||
* @since 2.3
|
||||
*/
|
||||
public class JdbcDb2Dialect extends Db2Dialect {
|
||||
|
||||
public static JdbcDb2Dialect INSTANCE = new JdbcDb2Dialect();
|
||||
|
||||
@Override
|
||||
public Collection<Object> getConverters() {
|
||||
|
||||
ArrayList<Object> converters = new ArrayList<>(super.getConverters());
|
||||
converters.add(OffsetDateTime2TimestampConverter.INSTANCE);
|
||||
|
||||
return converters;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* 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.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
|
||||
* @since 2.3
|
||||
*/
|
||||
public class JdbcH2Dialect extends H2Dialect {
|
||||
|
||||
public static JdbcH2Dialect INSTANCE = new JdbcH2Dialect();
|
||||
|
||||
@Override
|
||||
public Collection<Object> getConverters() {
|
||||
return Collections.singletonList(TimestampWithTimeZone2OffsetDateTimeConverter.INSTANCE);
|
||||
}
|
||||
|
||||
@ReadingConverter
|
||||
enum TimestampWithTimeZone2OffsetDateTimeConverter 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 );
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* 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.sql.JDBCType;
|
||||
import java.time.OffsetDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
import org.springframework.data.convert.WritingConverter;
|
||||
import org.springframework.data.jdbc.core.convert.JdbcValue;
|
||||
import org.springframework.data.relational.core.dialect.Db2Dialect;
|
||||
import org.springframework.data.relational.core.dialect.MySqlDialect;
|
||||
import org.springframework.data.relational.core.sql.IdentifierProcessing;
|
||||
|
||||
/**
|
||||
* {@link Db2Dialect} that registers JDBC specific converters.
|
||||
*
|
||||
* @author Jens Schauder
|
||||
* @since 2.3
|
||||
*/
|
||||
public class JdbcMySqlDialect extends MySqlDialect {
|
||||
|
||||
public JdbcMySqlDialect(IdentifierProcessing identifierProcessing) {
|
||||
super(identifierProcessing);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Object> getConverters() {
|
||||
|
||||
ArrayList<Object> converters = new ArrayList<>(super.getConverters());
|
||||
converters.add(OffsetDateTime2TimestampJdbcValueConverter.INSTANCE);
|
||||
|
||||
return converters;
|
||||
}
|
||||
|
||||
@WritingConverter
|
||||
enum OffsetDateTime2TimestampJdbcValueConverter implements Converter<OffsetDateTime, JdbcValue> {
|
||||
INSTANCE;
|
||||
|
||||
@Override
|
||||
public JdbcValue convert(OffsetDateTime source) {
|
||||
return JdbcValue.of(source, JDBCType.TIMESTAMP);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* 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 microsoft.sql.DateTimeOffset;
|
||||
|
||||
import java.time.OffsetDateTime;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
||||
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.SqlServerDialect;
|
||||
|
||||
/**
|
||||
* {@link Db2Dialect} that registers JDBC specific converters.
|
||||
*
|
||||
* @author Jens Schauder
|
||||
* @since 2.3
|
||||
*/
|
||||
public class JdbcSqlServerDialect extends SqlServerDialect {
|
||||
|
||||
public static JdbcSqlServerDialect INSTANCE = new JdbcSqlServerDialect();
|
||||
|
||||
@Override
|
||||
public Collection<Object> getConverters() {
|
||||
return Collections.singletonList(DateTimeOffset2OffsetDateTimeConverter.INSTANCE);
|
||||
}
|
||||
|
||||
@ReadingConverter
|
||||
enum DateTimeOffset2OffsetDateTimeConverter implements Converter<DateTimeOffset, OffsetDateTime> {
|
||||
INSTANCE;
|
||||
|
||||
@Override
|
||||
public OffsetDateTime convert(DateTimeOffset source) {
|
||||
return source.getOffsetDateTime();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* 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 OffsetDateTime2TimestampConverter implements Converter<OffsetDateTime, Timestamp> {
|
||||
|
||||
INSTANCE;
|
||||
@Override
|
||||
public Timestamp convert(OffsetDateTime source) {
|
||||
return Timestamp.from(source.toInstant());
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,9 @@
|
||||
*/
|
||||
package org.springframework.data.jdbc.repository.config;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.springframework.context.ApplicationContext;
|
||||
@@ -22,6 +25,7 @@ import org.springframework.context.annotation.Bean;
|
||||
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.jdbc.core.JdbcAggregateOperations;
|
||||
import org.springframework.data.jdbc.core.JdbcAggregateTemplate;
|
||||
import org.springframework.data.jdbc.core.convert.BasicJdbcConverter;
|
||||
@@ -33,6 +37,7 @@ 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.mapping.JdbcMappingContext;
|
||||
import org.springframework.data.jdbc.core.mapping.JdbcSimpleTypes;
|
||||
import org.springframework.data.relational.core.conversion.RelationalConverter;
|
||||
import org.springframework.data.relational.core.dialect.Dialect;
|
||||
import org.springframework.data.relational.core.mapping.NamingStrategy;
|
||||
@@ -56,7 +61,7 @@ public class AbstractJdbcConfiguration {
|
||||
* Register a {@link JdbcMappingContext} and apply an optional {@link NamingStrategy}.
|
||||
*
|
||||
* @param namingStrategy optional {@link NamingStrategy}. Use {@link NamingStrategy#INSTANCE} as fallback.
|
||||
* @param customConversions see {@link #jdbcCustomConversions()}.
|
||||
* @param customConversions see {@link #jdbcCustomConversions(Dialect)}.
|
||||
* @return must not be {@literal null}.
|
||||
*/
|
||||
@Bean
|
||||
@@ -71,10 +76,10 @@ public class AbstractJdbcConfiguration {
|
||||
|
||||
/**
|
||||
* Creates a {@link RelationalConverter} using the configured
|
||||
* {@link #jdbcMappingContext(Optional, JdbcCustomConversions)}. Will get {@link #jdbcCustomConversions()} applied.
|
||||
* {@link #jdbcMappingContext(Optional, JdbcCustomConversions)}. Will get {@link #jdbcCustomConversions(Dialect)} ()} applied.
|
||||
*
|
||||
* @see #jdbcMappingContext(Optional, JdbcCustomConversions)
|
||||
* @see #jdbcCustomConversions()
|
||||
* @see #jdbcCustomConversions(Dialect) ()
|
||||
* @return must not be {@literal null}.
|
||||
*/
|
||||
@Bean
|
||||
@@ -96,8 +101,22 @@ public class AbstractJdbcConfiguration {
|
||||
* @return will never be {@literal null}.
|
||||
*/
|
||||
@Bean
|
||||
public JdbcCustomConversions jdbcCustomConversions() {
|
||||
return new JdbcCustomConversions();
|
||||
public JdbcCustomConversions jdbcCustomConversions(Dialect dialect) {
|
||||
|
||||
return new JdbcCustomConversions(CustomConversions.StoreConversions.of(JdbcSimpleTypes.HOLDER,
|
||||
storeConverters(dialect)), userConverters());
|
||||
}
|
||||
|
||||
private List<?> userConverters() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
private List<Object> storeConverters(Dialect dialect) {
|
||||
|
||||
List<Object> converters = new ArrayList<>();
|
||||
converters.addAll(dialect.getConverters());
|
||||
converters.addAll(JdbcCustomConversions.STORE_CONVERTERS);
|
||||
return converters;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -134,7 +153,7 @@ public class AbstractJdbcConfiguration {
|
||||
* Resolves a {@link Dialect JDBC dialect} by inspecting {@link NamedParameterJdbcOperations}.
|
||||
*
|
||||
* @param operations the {@link NamedParameterJdbcOperations} allowing access to a {@link java.sql.Connection}.
|
||||
* @return
|
||||
* @return the {@link Dialect} to be used.
|
||||
* @since 2.0
|
||||
* @throws org.springframework.data.jdbc.repository.config.DialectResolver.NoDialectException if the {@link Dialect}
|
||||
* cannot be determined.
|
||||
|
||||
@@ -28,10 +28,15 @@ import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.core.io.support.SpringFactoriesLoader;
|
||||
import org.springframework.dao.NonTransientDataAccessException;
|
||||
import org.springframework.data.jdbc.core.dialect.JdbcDb2Dialect;
|
||||
import org.springframework.data.jdbc.core.dialect.JdbcH2Dialect;
|
||||
import org.springframework.data.jdbc.core.dialect.JdbcMySqlDialect;
|
||||
import org.springframework.data.jdbc.core.dialect.JdbcSqlServerDialect;
|
||||
import org.springframework.data.relational.core.dialect.Db2Dialect;
|
||||
import org.springframework.data.relational.core.dialect.Dialect;
|
||||
import org.springframework.data.relational.core.dialect.H2Dialect;
|
||||
import org.springframework.data.relational.core.dialect.HsqlDbDialect;
|
||||
import org.springframework.data.relational.core.dialect.MariaDbDialect;
|
||||
import org.springframework.data.relational.core.dialect.MySqlDialect;
|
||||
import org.springframework.data.relational.core.dialect.OracleDialect;
|
||||
import org.springframework.data.relational.core.dialect.PostgresDialect;
|
||||
@@ -118,19 +123,22 @@ public class DialectResolver {
|
||||
return HsqlDbDialect.INSTANCE;
|
||||
}
|
||||
if (name.contains("h2")) {
|
||||
return H2Dialect.INSTANCE;
|
||||
return JdbcH2Dialect.INSTANCE;
|
||||
}
|
||||
if (name.contains("mysql") || name.contains("mariadb")) {
|
||||
return new MySqlDialect(getIdentifierProcessing(metaData));
|
||||
if (name.contains("mysql")) {
|
||||
return new JdbcMySqlDialect(getIdentifierProcessing(metaData));
|
||||
}
|
||||
if (name.contains("mariadb")) {
|
||||
return new MariaDbDialect(getIdentifierProcessing(metaData));
|
||||
}
|
||||
if (name.contains("postgresql")) {
|
||||
return PostgresDialect.INSTANCE;
|
||||
}
|
||||
if (name.contains("microsoft")) {
|
||||
return SqlServerDialect.INSTANCE;
|
||||
return JdbcSqlServerDialect.INSTANCE;
|
||||
}
|
||||
if (name.contains("db2")) {
|
||||
return Db2Dialect.INSTANCE;
|
||||
return JdbcDb2Dialect.INSTANCE;
|
||||
}
|
||||
if (name.contains("oracle")) {
|
||||
return OracleDialect.INSTANCE;
|
||||
|
||||
@@ -22,6 +22,7 @@ import java.sql.JDBCType;
|
||||
import java.sql.Time;
|
||||
import java.sql.Timestamp;
|
||||
import java.sql.Types;
|
||||
import java.time.OffsetDateTime;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -62,6 +63,7 @@ public final class JdbcUtil {
|
||||
sqlTypeMappings.put(Date.class, Types.DATE);
|
||||
sqlTypeMappings.put(Time.class, Types.TIME);
|
||||
sqlTypeMappings.put(Timestamp.class, Types.TIMESTAMP);
|
||||
sqlTypeMappings.put(OffsetDateTime.class, Types.TIMESTAMP_WITH_TIMEZONE);
|
||||
}
|
||||
|
||||
private JdbcUtil() {
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
package org.springframework.data.jdbc.core.dialect;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
|
||||
import java.time.OffsetDateTime;
|
||||
|
||||
import org.h2.api.TimestampWithTimeZone;
|
||||
import org.h2.util.DateTimeUtils;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Tests for {@link JdbcH2Dialect}.
|
||||
*
|
||||
* @author Jens Schauder
|
||||
*/
|
||||
class JdbcH2DialectTests {
|
||||
|
||||
@Test
|
||||
void TimestampWithTimeZone2OffsetDateTimeConverterConvertsProperly() {
|
||||
|
||||
JdbcH2Dialect.TimestampWithTimeZone2OffsetDateTimeConverter converter = JdbcH2Dialect.TimestampWithTimeZone2OffsetDateTimeConverter.INSTANCE;
|
||||
long dateValue = 123456789;
|
||||
long timeNanos = 987654321;
|
||||
int timeZoneOffsetSeconds = 4 * 60 * 60;
|
||||
TimestampWithTimeZone timestampWithTimeZone = new TimestampWithTimeZone(dateValue, timeNanos,
|
||||
timeZoneOffsetSeconds);
|
||||
|
||||
OffsetDateTime offsetDateTime = converter.convert(timestampWithTimeZone);
|
||||
|
||||
assertThat(offsetDateTime.getOffset().getTotalSeconds()).isEqualTo(timeZoneOffsetSeconds);
|
||||
assertThat(offsetDateTime.getNano()).isEqualTo(timeNanos);
|
||||
assertThat(offsetDateTime.toEpochSecond())
|
||||
.isEqualTo(DateTimeUtils.getEpochSeconds(dateValue, timeNanos, timeZoneOffsetSeconds));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package org.springframework.data.jdbc.core.dialect;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.sql.Timestamp;
|
||||
import java.time.OffsetDateTime;
|
||||
import java.time.ZoneOffset;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Tests for {@link OffsetDateTime2TimestampConverter}.
|
||||
*
|
||||
* @author Jens Schauder
|
||||
*/
|
||||
class OffsetDateTime2TimestampConverterUnitTests {
|
||||
|
||||
@Test
|
||||
void conversionPreservesInstant() {
|
||||
|
||||
OffsetDateTime offsetDateTime = OffsetDateTime.of(5, 5, 5, 5,5,5,123456789, ZoneOffset.ofHours(3));
|
||||
|
||||
Timestamp timestamp = OffsetDateTime2TimestampConverter.INSTANCE.convert(offsetDateTime);
|
||||
|
||||
assertThat(timestamp.toInstant()).isEqualTo(offsetDateTime.toInstant());
|
||||
}
|
||||
}
|
||||
@@ -27,14 +27,16 @@ import lombok.Value;
|
||||
import java.io.IOException;
|
||||
import java.sql.ResultSet;
|
||||
import java.time.Instant;
|
||||
import java.time.OffsetDateTime;
|
||||
import java.time.ZoneOffset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.config.PropertiesFactoryBean;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
@@ -92,6 +94,9 @@ public class JdbcRepositoryIntegrationTests {
|
||||
|
||||
@BeforeEach
|
||||
public void before() {
|
||||
|
||||
repository.deleteAll();
|
||||
|
||||
eventListener.events.clear();
|
||||
}
|
||||
|
||||
@@ -282,17 +287,7 @@ public class JdbcRepositoryIntegrationTests {
|
||||
@Test // DATAJDBC-464, DATAJDBC-318
|
||||
public void executeQueryWithParameterRequiringConversion() {
|
||||
|
||||
Instant now = Instant.now();
|
||||
|
||||
DummyEntity first = repository.save(createDummyEntity());
|
||||
first.setPointInTime(now.minusSeconds(1000L));
|
||||
first.setName("first");
|
||||
|
||||
DummyEntity second = repository.save(createDummyEntity());
|
||||
second.setPointInTime(now.plusSeconds(1000L));
|
||||
second.setName("second");
|
||||
|
||||
repository.saveAll(asList(first, second));
|
||||
Instant now = createDummyBeforeAndAfterNow();
|
||||
|
||||
assertThat(repository.after(now)) //
|
||||
.extracting(DummyEntity::getName) //
|
||||
@@ -408,7 +403,7 @@ public class JdbcRepositoryIntegrationTests {
|
||||
@Test // #945
|
||||
@EnabledOnFeature(TestDatabaseFeatures.Feature.IS_POSTGRES)
|
||||
public void usePrimitiveArrayAsArgument() {
|
||||
assertThat(repository.unnestPrimitive(new int[]{1, 2, 3})).containsExactly(1,2,3);
|
||||
assertThat(repository.unnestPrimitive(new int[] { 1, 2, 3 })).containsExactly(1, 2, 3);
|
||||
}
|
||||
|
||||
@Test // GH-774
|
||||
@@ -442,6 +437,17 @@ public class JdbcRepositoryIntegrationTests {
|
||||
assertThat(slice.hasNext()).isTrue();
|
||||
}
|
||||
|
||||
@Test // #935
|
||||
public void queryByOffsetDateTime() {
|
||||
|
||||
Instant now = createDummyBeforeAndAfterNow();
|
||||
OffsetDateTime timeArgument = OffsetDateTime.ofInstant(now, ZoneOffset.ofHours(2));
|
||||
|
||||
List<DummyEntity> entities = repository.findByOffsetDateTime(timeArgument);
|
||||
|
||||
assertThat(entities).extracting(DummyEntity::getName).containsExactly("second");
|
||||
}
|
||||
|
||||
@Test // #971
|
||||
public void stringQueryProjectionShouldReturnProjectedEntities() {
|
||||
|
||||
@@ -486,6 +492,29 @@ public class JdbcRepositoryIntegrationTests {
|
||||
assertThat(result.getContent().get(0).getName()).isEqualTo("Entity Name");
|
||||
}
|
||||
|
||||
private Instant createDummyBeforeAndAfterNow() {
|
||||
|
||||
Instant now = Instant.now();
|
||||
|
||||
DummyEntity first = createDummyEntity();
|
||||
Instant earlier = now.minusSeconds(1000L);
|
||||
OffsetDateTime earlierPlus3 = earlier.atOffset(ZoneOffset.ofHours(3));
|
||||
first.setPointInTime(earlier);
|
||||
first.offsetDateTime = earlierPlus3;
|
||||
|
||||
first.setName("first");
|
||||
|
||||
DummyEntity second = createDummyEntity();
|
||||
Instant later = now.plusSeconds(1000L);
|
||||
OffsetDateTime laterPlus3 = later.atOffset(ZoneOffset.ofHours(3));
|
||||
second.setPointInTime(later);
|
||||
second.offsetDateTime = laterPlus3;
|
||||
second.setName("second");
|
||||
|
||||
repository.saveAll(asList(first, second));
|
||||
return now;
|
||||
}
|
||||
|
||||
interface DummyEntityRepository extends CrudRepository<DummyEntity, Long> {
|
||||
|
||||
List<DummyEntity> findAllByNamedQuery();
|
||||
@@ -525,6 +554,9 @@ public class JdbcRepositoryIntegrationTests {
|
||||
Page<DummyProjection> findPageProjectionByName(String name, Pageable pageable);
|
||||
|
||||
Slice<DummyEntity> findSliceByNameContains(String name, Pageable pageable);
|
||||
|
||||
@Query("SELECT * FROM DUMMY_ENTITY WHERE OFFSET_DATE_TIME > :threshhold")
|
||||
List<DummyEntity> findByOffsetDateTime(@Param("threshhold") OffsetDateTime threshhold);
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@@ -573,6 +605,7 @@ public class JdbcRepositoryIntegrationTests {
|
||||
static class DummyEntity {
|
||||
String name;
|
||||
Instant pointInTime;
|
||||
OffsetDateTime offsetDateTime;
|
||||
@Id private Long idProp;
|
||||
|
||||
public DummyEntity(String name) {
|
||||
|
||||
@@ -110,7 +110,7 @@ public class AbstractJdbcConfigurationIntegrationTests {
|
||||
}
|
||||
|
||||
@Override
|
||||
public JdbcCustomConversions jdbcCustomConversions() {
|
||||
public JdbcCustomConversions jdbcCustomConversions(Dialect dialect) {
|
||||
return new JdbcCustomConversions(Collections.singletonList(Blah2BlubbConverter.INSTANCE));
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
package org.springframework.data.jdbc.support;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
|
||||
import java.sql.Types;
|
||||
import java.time.OffsetDateTime;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Tests for {@link JdbcUtil}.
|
||||
*
|
||||
* @author Jens Schauder
|
||||
*/
|
||||
class JdbcUtilTests {
|
||||
|
||||
@Test
|
||||
void test() {
|
||||
assertThat(JdbcUtil.sqlTypeFor(OffsetDateTime.class)).isEqualTo(Types.TIMESTAMP_WITH_TIMEZONE);
|
||||
}
|
||||
}
|
||||
@@ -77,15 +77,4 @@ class MySqlDataSourceConfiguration extends DataSourceConfiguration implements In
|
||||
new ByteArrayResource("DROP DATABASE test;CREATE DATABASE test;".getBytes()));
|
||||
}
|
||||
}
|
||||
|
||||
private DataSource createRootDataSource() {
|
||||
|
||||
MysqlDataSource dataSource = new MysqlDataSource();
|
||||
dataSource.setUrl(MYSQL_CONTAINER.getJdbcUrl());
|
||||
dataSource.setUser("root");
|
||||
dataSource.setPassword(MYSQL_CONTAINER.getPassword());
|
||||
dataSource.setDatabaseName(MYSQL_CONTAINER.getDatabaseName());
|
||||
|
||||
return dataSource;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,10 @@
|
||||
*/
|
||||
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;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
@@ -38,6 +42,7 @@ 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.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.relational.core.dialect.Dialect;
|
||||
@@ -108,8 +113,17 @@ public class TestConfiguration {
|
||||
}
|
||||
|
||||
@Bean
|
||||
CustomConversions jdbcCustomConversions() {
|
||||
return new JdbcCustomConversions();
|
||||
CustomConversions jdbcCustomConversions(Dialect dialect) {
|
||||
return new JdbcCustomConversions(CustomConversions.StoreConversions.of(JdbcSimpleTypes.HOLDER,
|
||||
storeConverters(dialect)), Collections.emptyList());
|
||||
}
|
||||
|
||||
private List<Object> storeConverters(Dialect dialect) {
|
||||
|
||||
List<Object> converters = new ArrayList<>();
|
||||
converters.addAll(dialect.getConverters());
|
||||
converters.addAll(JdbcCustomConversions.STORE_CONVERTERS);
|
||||
return converters;
|
||||
}
|
||||
|
||||
@Bean
|
||||
|
||||
@@ -4,5 +4,6 @@ CREATE TABLE dummy_entity
|
||||
(
|
||||
id_Prop BIGINT GENERATED BY DEFAULT AS IDENTITY ( START WITH 1 ) PRIMARY KEY,
|
||||
NAME VARCHAR(100),
|
||||
POINT_IN_TIME TIMESTAMP
|
||||
POINT_IN_TIME TIMESTAMP,
|
||||
OFFSET_DATE_TIME TIMESTAMP -- with time zone is only supported with z/OS
|
||||
);
|
||||
|
||||
@@ -2,5 +2,6 @@ CREATE TABLE dummy_entity
|
||||
(
|
||||
id_Prop BIGINT GENERATED BY DEFAULT AS IDENTITY ( START WITH 1 ) PRIMARY KEY,
|
||||
NAME VARCHAR(100),
|
||||
POINT_IN_TIME TIMESTAMP
|
||||
POINT_IN_TIME TIMESTAMP,
|
||||
OFFSET_DATE_TIME TIMESTAMP WITH TIME ZONE
|
||||
);
|
||||
|
||||
@@ -2,5 +2,6 @@ CREATE TABLE dummy_entity
|
||||
(
|
||||
id_Prop BIGINT GENERATED BY DEFAULT AS IDENTITY ( START WITH 1 ) PRIMARY KEY,
|
||||
NAME VARCHAR(100),
|
||||
POINT_IN_TIME TIMESTAMP
|
||||
POINT_IN_TIME TIMESTAMP,
|
||||
OFFSET_DATE_TIME TIMESTAMP WITH TIME ZONE
|
||||
);
|
||||
|
||||
@@ -2,5 +2,6 @@ CREATE TABLE dummy_entity
|
||||
(
|
||||
id_Prop BIGINT AUTO_INCREMENT PRIMARY KEY,
|
||||
NAME VARCHAR(100),
|
||||
POINT_IN_TIME TIMESTAMP(3)
|
||||
POINT_IN_TIME TIMESTAMP(3),
|
||||
OFFSET_DATE_TIME TIMESTAMP(3)
|
||||
);
|
||||
|
||||
@@ -3,5 +3,6 @@ CREATE TABLE dummy_entity
|
||||
(
|
||||
id_Prop BIGINT IDENTITY PRIMARY KEY,
|
||||
NAME VARCHAR(100),
|
||||
POINT_IN_TIME DATETIME
|
||||
POINT_IN_TIME DATETIME,
|
||||
OFFSET_DATE_TIME DATETIMEOFFSET
|
||||
);
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
SET SQL_MODE='ALLOW_INVALID_DATES';
|
||||
|
||||
CREATE TABLE dummy_entity
|
||||
(
|
||||
id_Prop BIGINT AUTO_INCREMENT PRIMARY KEY,
|
||||
NAME VARCHAR(100),
|
||||
POINT_IN_TIME TIMESTAMP(3)
|
||||
POINT_IN_TIME TIMESTAMP(3) default null,
|
||||
OFFSET_DATE_TIME TIMESTAMP(3) default null
|
||||
);
|
||||
|
||||
@@ -4,5 +4,6 @@ CREATE TABLE DUMMY_ENTITY
|
||||
(
|
||||
ID_PROP NUMBER GENERATED BY DEFAULT ON NULL AS IDENTITY PRIMARY KEY,
|
||||
NAME VARCHAR2(100),
|
||||
POINT_IN_TIME TIMESTAMP
|
||||
POINT_IN_TIME TIMESTAMP,
|
||||
OFFSET_DATE_TIME TIMESTAMP WITH TIME ZONE
|
||||
);
|
||||
|
||||
@@ -3,5 +3,6 @@ CREATE TABLE dummy_entity
|
||||
(
|
||||
id_Prop SERIAL PRIMARY KEY,
|
||||
NAME VARCHAR(100),
|
||||
POINT_IN_TIME TIMESTAMP
|
||||
POINT_IN_TIME TIMESTAMP,
|
||||
OFFSET_DATE_TIME TIMESTAMP WITH TIME ZONE
|
||||
);
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
CREATE TABLE ENTITY_WITH_COLUMNS_REQUIRING_CONVERSIONS ( id_Timestamp DATETIME PRIMARY KEY, bool boolean, SOME_ENUM VARCHAR(100), big_Decimal DECIMAL(65), big_Integer DECIMAL(20), date DATETIME, local_Date_Time DATETIME, zoned_Date_Time VARCHAR(30));
|
||||
CREATE TABLE ENTITY_WITH_COLUMNS_REQUIRING_CONVERSIONS_RELATION ( id_Timestamp DATETIME NOT NULL PRIMARY KEY, data VARCHAR(100));
|
||||
CREATE TABLE ENTITY_WITH_COLUMNS_REQUIRING_CONVERSIONS ( id_Timestamp TIMESTAMP PRIMARY KEY, bool boolean, SOME_ENUM VARCHAR(100), big_Decimal DECIMAL(65), big_Integer DECIMAL(20), date DATETIME, local_Date_Time DATETIME, zoned_Date_Time VARCHAR(30));
|
||||
CREATE TABLE ENTITY_WITH_COLUMNS_REQUIRING_CONVERSIONS_RELATION ( id_Timestamp TIMESTAMP NOT NULL PRIMARY KEY, data VARCHAR(100));
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
CREATE TABLE ENTITY_WITH_COLUMNS_REQUIRING_CONVERSIONS ( id_Timestamp DATETIME PRIMARY KEY, bool boolean, SOME_ENUM VARCHAR(100), big_Decimal DECIMAL(65), big_Integer DECIMAL(20), date DATETIME, local_Date_Time DATETIME, zoned_Date_Time VARCHAR(30));
|
||||
CREATE TABLE ENTITY_WITH_COLUMNS_REQUIRING_CONVERSIONS_RELATION ( id_Timestamp DATETIME NOT NULL PRIMARY KEY, data VARCHAR(100));
|
||||
CREATE TABLE ENTITY_WITH_COLUMNS_REQUIRING_CONVERSIONS ( id_Timestamp TIMESTAMP PRIMARY KEY, bool boolean, SOME_ENUM VARCHAR(100), big_Decimal DECIMAL(65), big_Integer DECIMAL(20), date DATETIME, local_Date_Time DATETIME, zoned_Date_Time VARCHAR(30));
|
||||
CREATE TABLE ENTITY_WITH_COLUMNS_REQUIRING_CONVERSIONS_RELATION ( id_Timestamp TIMESTAMP NOT NULL PRIMARY KEY, data VARCHAR(100));
|
||||
|
||||
@@ -15,6 +15,9 @@
|
||||
*/
|
||||
package org.springframework.data.relational.core.dialect;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
||||
import org.springframework.data.relational.core.sql.IdentifierProcessing;
|
||||
import org.springframework.data.relational.core.sql.LockOptions;
|
||||
|
||||
@@ -110,4 +113,9 @@ public class Db2Dialect extends AbstractDialect {
|
||||
public IdentifierProcessing getIdentifierProcessing() {
|
||||
return IdentifierProcessing.ANSI;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Object> getConverters() {
|
||||
return Collections.singletonList(Timestamp2OffsetDateTimeConverter.INSTANCE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,9 @@
|
||||
*/
|
||||
package org.springframework.data.relational.core.dialect;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
||||
import org.springframework.data.relational.core.sql.IdentifierProcessing;
|
||||
import org.springframework.data.relational.core.sql.SqlIdentifier;
|
||||
import org.springframework.data.relational.core.sql.render.SelectRenderContext;
|
||||
@@ -82,7 +85,16 @@ public interface Dialect {
|
||||
return Escaper.DEFAULT;
|
||||
}
|
||||
|
||||
default IdGeneration getIdGeneration(){
|
||||
default IdGeneration getIdGeneration() {
|
||||
return IdGeneration.DEFAULT;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a collection of converters for this dialect.
|
||||
*
|
||||
* @return a collection of converters for this dialect.
|
||||
*/
|
||||
default Collection<Object> getConverters() {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright 2019-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.relational.core.dialect;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
||||
import org.springframework.data.relational.core.sql.IdentifierProcessing;
|
||||
|
||||
/**
|
||||
* A SQL dialect for MariaDb.
|
||||
*
|
||||
* @author Jens Schauder
|
||||
* @since 2.3
|
||||
*/
|
||||
public class MariaDbDialect extends MySqlDialect {
|
||||
|
||||
public MariaDbDialect(IdentifierProcessing identifierProcessing) {
|
||||
super(identifierProcessing);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Object> getConverters() {
|
||||
return Collections.singletonList(Timestamp2OffsetDateTimeConverter.INSTANCE);
|
||||
}
|
||||
}
|
||||
@@ -15,11 +15,14 @@
|
||||
*/
|
||||
package org.springframework.data.relational.core.dialect;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
||||
import org.springframework.data.relational.core.sql.IdentifierProcessing;
|
||||
import org.springframework.data.relational.core.sql.LockOptions;
|
||||
import org.springframework.data.relational.core.sql.IdentifierProcessing.LetterCasing;
|
||||
import org.springframework.data.relational.core.sql.IdentifierProcessing.Quoting;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.data.relational.core.sql.LockOptions;
|
||||
|
||||
/**
|
||||
* A SQL dialect for MySQL.
|
||||
@@ -161,4 +164,9 @@ public class MySqlDialect extends AbstractDialect {
|
||||
public IdentifierProcessing getIdentifierProcessing() {
|
||||
return identifierProcessing;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Object> getConverters() {
|
||||
return Collections.singletonList(Timestamp2OffsetDateTimeConverter.INSTANCE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,15 +15,8 @@
|
||||
*/
|
||||
package org.springframework.data.relational.core.dialect;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.data.relational.core.sql.IdentifierProcessing;
|
||||
import org.springframework.data.relational.core.sql.LockOptions;
|
||||
import org.springframework.data.relational.core.sql.Table;
|
||||
import org.springframework.data.relational.core.sql.IdentifierProcessing.LetterCasing;
|
||||
import org.springframework.data.relational.core.sql.IdentifierProcessing.Quoting;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
||||
/**
|
||||
* An SQL dialect for Oracle.
|
||||
@@ -51,4 +44,10 @@ public class OracleDialect extends AnsiDialect {
|
||||
public IdGeneration getIdGeneration() {
|
||||
return ID_GENERATION;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Object> getConverters() {
|
||||
return Collections.singletonList(Timestamp2OffsetDateTimeConverter.INSTANCE);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -15,14 +15,16 @@
|
||||
*/
|
||||
package org.springframework.data.relational.core.dialect;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
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;
|
||||
import org.springframework.data.relational.core.sql.LockOptions;
|
||||
import org.springframework.data.relational.core.sql.SqlIdentifier;
|
||||
import org.springframework.data.relational.core.sql.Table;
|
||||
import org.springframework.data.relational.core.sql.IdentifierProcessing.LetterCasing;
|
||||
import org.springframework.data.relational.core.sql.IdentifierProcessing.Quoting;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
@@ -113,6 +115,11 @@ public class PostgresDialect extends AbstractDialect {
|
||||
return ARRAY_COLUMNS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Object> getConverters() {
|
||||
return Collections.singletonList(Timestamp2OffsetDateTimeConverter.INSTANCE);
|
||||
}
|
||||
|
||||
static class PostgresLockClause implements LockClause {
|
||||
|
||||
private final IdentifierProcessing identifierProcessing;
|
||||
@@ -165,7 +172,7 @@ public class PostgresDialect extends AbstractDialect {
|
||||
public Position getClausePosition() {
|
||||
return Position.AFTER_ORDER_BY;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
static class PostgresArrayColumns implements ArrayColumns {
|
||||
|
||||
@@ -195,4 +202,5 @@ public class PostgresDialect extends AbstractDialect {
|
||||
public IdentifierProcessing getIdentifierProcessing() {
|
||||
return IdentifierProcessing.create(Quoting.ANSI, LetterCasing.LOWER_CASE);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* 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.relational.core.dialect;
|
||||
|
||||
import java.sql.Timestamp;
|
||||
import java.time.OffsetDateTime;
|
||||
import java.time.ZoneId;
|
||||
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
import org.springframework.data.convert.ReadingConverter;
|
||||
|
||||
/**
|
||||
* A reading convert to convert {@link Timestamp} to {@link OffsetDateTime}. For the conversion the {@link Timestamp}
|
||||
* gets considered to be at UTC and the result of the conversion will have an offset of 0 and represent the same
|
||||
* instant.
|
||||
*
|
||||
* @author Jens Schauder
|
||||
* @since 2.3
|
||||
*/
|
||||
@ReadingConverter
|
||||
enum Timestamp2OffsetDateTimeConverter implements Converter<Timestamp, OffsetDateTime> {
|
||||
|
||||
INSTANCE;
|
||||
|
||||
@Override
|
||||
public OffsetDateTime convert(Timestamp timestamp) {
|
||||
return OffsetDateTime.ofInstant(timestamp.toInstant(), ZoneId.of("UTC"));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package org.springframework.data.relational.core.dialect;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.sql.Timestamp;
|
||||
import java.time.Instant;
|
||||
import java.time.OffsetDateTime;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Tests {@link Timestamp2OffsetDateTimeConverter}.
|
||||
*
|
||||
* @author Jens Schauder
|
||||
*/
|
||||
class Timestamp2OffsetDateTimeConverterUnitTests {
|
||||
|
||||
@Test
|
||||
void conversionMaintainsInstant() {
|
||||
|
||||
Timestamp timestamp = Timestamp.from(Instant.now());
|
||||
OffsetDateTime converted = Timestamp2OffsetDateTimeConverter.INSTANCE.convert(timestamp);
|
||||
|
||||
assertThat(converted.toInstant()).isEqualTo(timestamp.toInstant());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user