From 3eb8448ac2ad16922dc64b1fb450b554ebd86770 Mon Sep 17 00:00:00 2001 From: Mikhail2048 Date: Sun, 2 Jan 2022 22:03:31 +0300 Subject: [PATCH 1/4] change ZonedDateTime conversion mechanism --- .../jdbc/core/convert/BasicJdbcConverter.java | 27 ++---- .../convert/DefaultDataAccessStrategy.java | 5 +- .../jdbc/core/convert/EntityRowMapper.java | 4 + .../jdbc/core/convert/JdbcColumnTypes.java | 3 +- .../data/jdbc/core/convert/JdbcConverter.java | 2 + ...WithTimeZoneToOffsetDateTimeConverter.java | 3 +- ...pWithTimeZoneToZonedDateTimeConverter.java | 46 +++++++++ .../data/jdbc/core/dialect/JdbcH2Dialect.java | 7 +- .../data/jdbc/support/JdbcUtil.java | 3 + .../convert/BasicJdbcConverterUnitTests.java | 2 +- ...itoryCustomConversionIntegrationTests.java | 96 ++++++++++++++++--- ...ryCustomConversionIntegrationTests-db2.sql | 2 + ...oryCustomConversionIntegrationTests-h2.sql | 1 + ...yCustomConversionIntegrationTests-hsql.sql | 2 +- ...stomConversionIntegrationTests-mariadb.sql | 1 + ...CustomConversionIntegrationTests-mssql.sql | 2 + ...CustomConversionIntegrationTests-mysql.sql | 1 + ...ustomConversionIntegrationTests-oracle.sql | 5 + ...tomConversionIntegrationTests-postgres.sql | 1 + 19 files changed, 175 insertions(+), 38 deletions(-) create mode 100644 spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/H2TimestampWithTimeZoneToZonedDateTimeConverter.java diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/BasicJdbcConverter.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/BasicJdbcConverter.java index dfefccd358..333855a5e5 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/BasicJdbcConverter.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/BasicJdbcConverter.java @@ -51,6 +51,7 @@ import org.springframework.data.relational.core.sql.IdentifierProcessing; import org.springframework.data.util.ClassTypeInformation; import org.springframework.data.util.TypeInformation; +import org.springframework.lang.NonNull; import org.springframework.lang.Nullable; import org.springframework.util.Assert; @@ -64,6 +65,8 @@ * @author Jens Schauder * @author Christoph Strobl * @author Myeonghyeon Lee + * @author Mikhail Polivakha + * * @see MappingContext * @see SimpleTypeHolder * @see CustomConversions @@ -240,21 +243,6 @@ public Object readValue(@Nullable Object value, TypeInformation type) { return super.readValue(value, type); } - /* - * (non-Javadoc) - * @see org.springframework.data.relational.core.conversion.RelationalConverter#writeValue(java.lang.Object, org.springframework.data.util.TypeInformation) - */ - @Override - @Nullable - public Object writeValue(@Nullable Object value, TypeInformation type) { - - if (value == null) { - return null; - } - - return super.writeValue(value, type); - } - private boolean canWriteAsJdbcValue(@Nullable Object value) { if (value == null) { @@ -285,6 +273,7 @@ private boolean canWriteAsJdbcValue(@Nullable Object value) { * (non-Javadoc) * @see org.springframework.data.jdbc.core.convert.JdbcConverter#writeValue(java.lang.Object, java.lang.Class, int) */ + @NonNull @Override public JdbcValue writeJdbcValue(@Nullable Object value, Class columnType, int sqlType) { @@ -329,8 +318,12 @@ private JdbcValue tryToConvertToJdbcValue(@Nullable Object value) { @Override public T mapRow(RelationalPersistentEntity entity, ResultSet resultSet, Object key) { - return new ReadingContext(new PersistentPropertyPathExtension(getMappingContext(), entity), - new ResultSetAccessor(resultSet), Identifier.empty(), key).mapRow(); + return new ReadingContext( + new PersistentPropertyPathExtension(getMappingContext(), entity), + new ResultSetAccessor(resultSet), + Identifier.empty(), + key + ).mapRow(); } @Override diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/DefaultDataAccessStrategy.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/DefaultDataAccessStrategy.java index 33b82a675f..e964b16c6f 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/DefaultDataAccessStrategy.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/DefaultDataAccessStrategy.java @@ -67,6 +67,8 @@ * @author Myeonghyeon Lee * @author Yunyoung LEE * @author Radim Tlusty + * @author Mikhail Polivakha + * * @since 1.1 */ public class DefaultDataAccessStrategy implements DataAccessStrategy { @@ -545,7 +547,8 @@ private IdentifierProcessing getIdentifierProcessing() { private void addConvertedPropertyValue(SqlIdentifierParameterSource parameterSource, RelationalPersistentProperty property, @Nullable Object value, SqlIdentifier name) { - addConvertedValue(parameterSource, value, name, converter.getColumnType(property), converter.getSqlType(property)); + final Class javaColumnType = converter.getColumnType(property); + addConvertedValue(parameterSource, value, name, javaColumnType, JdbcUtil.sqlTypeFor(javaColumnType)); } private void addConvertedPropertyValue(SqlIdentifierParameterSource parameterSource, SqlIdentifier name, Object value, diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/EntityRowMapper.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/EntityRowMapper.java index 1a04450957..e7986fae49 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/EntityRowMapper.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/EntityRowMapper.java @@ -20,6 +20,7 @@ import org.springframework.data.relational.core.mapping.PersistentPropertyPathExtension; import org.springframework.data.relational.core.mapping.RelationalPersistentEntity; import org.springframework.jdbc.core.RowMapper; +import org.springframework.util.Assert; /** * Maps a {@link ResultSet} to an entity of type {@code T}, including entities referenced. This {@link RowMapper} might @@ -50,6 +51,9 @@ public EntityRowMapper(PersistentPropertyPathExtension path, JdbcConverter conve public EntityRowMapper(RelationalPersistentEntity entity, JdbcConverter converter) { + Assert.notNull(entity, "relationalPersistentEntity must not be null!"); + Assert.notNull(converter, "jdbcConverter must not be null!"); + this.entity = entity; this.path = null; this.converter = converter; diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/JdbcColumnTypes.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/JdbcColumnTypes.java index a5f2cc9136..87feb6eeab 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/JdbcColumnTypes.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/JdbcColumnTypes.java @@ -31,6 +31,7 @@ * compatible with JDBC drivers. * * @author Jens Schauder + * @author Mikhail Polivakha * @since 2.0 */ public enum JdbcColumnTypes { @@ -53,7 +54,7 @@ public Class resolvePrimitiveType(Class type) { static { javaToDbType.put(Enum.class, String.class); - javaToDbType.put(ZonedDateTime.class, String.class); + javaToDbType.put(ZonedDateTime.class, ZonedDateTime.class); javaToDbType.put(OffsetDateTime.class, OffsetDateTime.class); javaToDbType.put(LocalDateTime.class, LocalDateTime.class); javaToDbType.put(Temporal.class, Timestamp.class); diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/JdbcConverter.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/JdbcConverter.java index b0a835ca65..a5ed27de0f 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/JdbcConverter.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/JdbcConverter.java @@ -22,6 +22,7 @@ import org.springframework.data.relational.core.mapping.RelationalPersistentEntity; import org.springframework.data.relational.core.mapping.RelationalPersistentProperty; import org.springframework.data.util.TypeInformation; +import org.springframework.lang.NonNull; import org.springframework.lang.Nullable; /** @@ -42,6 +43,7 @@ public interface JdbcConverter extends RelationalConverter { * @param sqlType the type constant from {@link java.sql.Types} to be used if non is specified by a converter. * @return The converted value wrapped in a {@link JdbcValue}. Guaranteed to be not {@literal null}. */ + @NonNull JdbcValue writeJdbcValue(@Nullable Object value, Class type, int sqlType); /** diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/H2TimestampWithTimeZoneToOffsetDateTimeConverter.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/H2TimestampWithTimeZoneToOffsetDateTimeConverter.java index 7687676462..586c4db18d 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/H2TimestampWithTimeZoneToOffsetDateTimeConverter.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/H2TimestampWithTimeZoneToOffsetDateTimeConverter.java @@ -31,8 +31,7 @@ * @since 2.7 */ @ReadingConverter -public enum H2TimestampWithTimeZoneToOffsetDateTimeConverter - implements Converter { +public enum H2TimestampWithTimeZoneToOffsetDateTimeConverter implements Converter { INSTANCE; diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/H2TimestampWithTimeZoneToZonedDateTimeConverter.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/H2TimestampWithTimeZoneToZonedDateTimeConverter.java new file mode 100644 index 0000000000..1f6f5c1eef --- /dev/null +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/H2TimestampWithTimeZoneToZonedDateTimeConverter.java @@ -0,0 +1,46 @@ +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 java.time.ZoneOffset; +import java.time.ZonedDateTime; + +/** + * Converter converting from an H2 internal representation of a timestamp with time zone to an {@link java.time.ZonedDateTime}. + * + * Only required for H2 versions < 2.0 + * + * @author Mikhail Polivakha + */ +@ReadingConverter +public enum H2TimestampWithTimeZoneToZonedDateTimeConverter implements Converter { + + INSTANCE; + + @Override + public ZonedDateTime 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 ZonedDateTime.of(source.getYear(), source.getMonth(), source.getDay(), (int) hours, (int) minutes, + (int) seconds, (int) nanosLeft, offset); + } +} diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/JdbcH2Dialect.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/JdbcH2Dialect.java index 15807ff4e1..20067d370a 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/JdbcH2Dialect.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/JdbcH2Dialect.java @@ -27,6 +27,8 @@ * * @author Jens Schauder * @author Christoph Strobl + * @author Mikhail Polivakha + * * @since 2.3 */ public class JdbcH2Dialect extends H2Dialect { @@ -40,17 +42,18 @@ public Collection getConverters() { final Collection originalConverters = super.getConverters(); - if (isH2belowVersion2()) { + if (doesH2TimestampWithTimeZoneClassPresent()) { List converters = new ArrayList<>(originalConverters); converters.add(H2TimestampWithTimeZoneToOffsetDateTimeConverter.INSTANCE); + converters.add(H2TimestampWithTimeZoneToZonedDateTimeConverter.INSTANCE); return converters; } return originalConverters; } - static boolean isH2belowVersion2() { + static boolean doesH2TimestampWithTimeZoneClassPresent() { try { diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/support/JdbcUtil.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/support/JdbcUtil.java index e5d0c291f4..8e32e7ca18 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/support/JdbcUtil.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/support/JdbcUtil.java @@ -23,6 +23,7 @@ import java.sql.Timestamp; import java.sql.Types; import java.time.OffsetDateTime; +import java.time.ZonedDateTime; import java.util.HashMap; import java.util.Map; @@ -35,6 +36,7 @@ * * @author Jens Schauder * @author Thomas Lang + * @author Mikhail Polivakha */ public final class JdbcUtil { @@ -64,6 +66,7 @@ public final class JdbcUtil { sqlTypeMappings.put(Time.class, Types.TIME); sqlTypeMappings.put(Timestamp.class, Types.TIMESTAMP); sqlTypeMappings.put(OffsetDateTime.class, Types.TIMESTAMP_WITH_TIMEZONE); + sqlTypeMappings.put(ZonedDateTime.class, Types.TIMESTAMP_WITH_TIMEZONE); } private JdbcUtil() { diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/BasicJdbcConverterUnitTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/BasicJdbcConverterUnitTests.java index cb60ed38fa..339889b721 100644 --- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/BasicJdbcConverterUnitTests.java +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/BasicJdbcConverterUnitTests.java @@ -74,7 +74,7 @@ public void testTargetTypesForPropertyType() { checkTargetType(softly, entity, "localDateTime", LocalDateTime.class); checkTargetType(softly, entity, "localDate", Timestamp.class); checkTargetType(softly, entity, "localTime", Timestamp.class); - checkTargetType(softly, entity, "zonedDateTime", String.class); + checkTargetType(softly, entity, "zonedDateTime", ZonedDateTime.class); checkTargetType(softly, entity, "offsetDateTime", OffsetDateTime.class); checkTargetType(softly, entity, "instant", Timestamp.class); checkTargetType(softly, entity, "date", Date.class); diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryCustomConversionIntegrationTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryCustomConversionIntegrationTests.java index 790154e7dd..308f9ccce5 100644 --- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryCustomConversionIntegrationTests.java +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryCustomConversionIntegrationTests.java @@ -15,16 +15,8 @@ */ package org.springframework.data.jdbc.repository; -import static java.util.Arrays.*; -import static org.assertj.core.api.Assertions.*; -import static org.assertj.core.api.SoftAssertions.*; -import static org.springframework.test.context.TestExecutionListeners.MergeMode.*; - -import java.math.BigDecimal; -import java.sql.JDBCType; -import java.util.Date; - import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.EnabledIfSystemProperty; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; @@ -36,6 +28,7 @@ import org.springframework.data.convert.WritingConverter; import org.springframework.data.jdbc.core.convert.JdbcCustomConversions; import org.springframework.data.jdbc.core.convert.JdbcValue; +import org.springframework.data.jdbc.core.dialect.H2TimestampWithTimeZoneToZonedDateTimeConverter; import org.springframework.data.jdbc.repository.support.JdbcRepositoryFactory; import org.springframework.data.jdbc.testing.AssumeFeatureTestExecutionListener; import org.springframework.data.jdbc.testing.TestConfiguration; @@ -45,11 +38,25 @@ import org.springframework.test.context.junit.jupiter.SpringExtension; import org.springframework.transaction.annotation.Transactional; +import java.math.BigDecimal; +import java.sql.JDBCType; +import java.sql.Timestamp; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; +import java.util.Date; +import java.util.Optional; + +import static java.util.Arrays.asList; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.SoftAssertions.assertSoftly; +import static org.springframework.test.context.TestExecutionListeners.MergeMode.MERGE_WITH_DEFAULTS; + /** * Tests storing and retrieving data types that get processed by custom conversions. * * @author Jens Schauder * @author Sanghyuk Jung + * @author Mikhail Polivakha */ @ContextConfiguration @Transactional @@ -69,21 +76,37 @@ Class testClass() { } @Bean - EntityWithBooleanRepository repository() { + EntityWithBooleanRepository entityWithBooleanRepository() { return factory.getRepository(EntityWithBooleanRepository.class); } + @Bean + EntityWithZonedDateTimeRepository entityWithZonedDateTimeRepository() { + return factory.getRepository(EntityWithZonedDateTimeRepository.class); + } + @Bean JdbcCustomConversions jdbcCustomConversions() { - return new JdbcCustomConversions(asList(StringToBigDecimalConverter.INSTANCE, BigDecimalToString.INSTANCE, - CustomIdReadingConverter.INSTANCE, CustomIdWritingConverter.INSTANCE)); + return new JdbcCustomConversions( + asList( + StringToBigDecimalConverter.INSTANCE, + BigDecimalToString.INSTANCE, + CustomIdReadingConverter.INSTANCE, + CustomIdWritingConverter.INSTANCE, + ZonedDateTimeToTimestampWritingConverter.INSTANCE, + ZonedDateTimeToTimestampReadingConverter.INSTANCE, + H2TimestampWithTimeZoneToZonedDateTimeConverter.INSTANCE + ) + ); } } @Autowired EntityWithBooleanRepository repository; + @Autowired EntityWithZonedDateTimeRepository entityWithZonedDateTimeRepository; + /** - * In PostrgreSQL this fails if a simple converter like the following is used. + * In PostgreSQL this fails if a simple converter like the following is used. * *
 	 *
@@ -143,8 +166,25 @@ public void saveAndLoadAnEntityWithReference() {
 		});
 	}
 
+	/**
+	 * DATAJDBC-1089
+	 */
+	@Test
+	public void testZonedDateTimeToTimestampConversion() {
+		EntityWithZonedDateTime entity = new EntityWithZonedDateTime();
+		entity.createdAt = ZonedDateTime.now(ZoneOffset.ofHours(3));
+
+		final EntityWithZonedDateTime persistedEntity = entityWithZonedDateTimeRepository.save(entity);
+		final Optional foundEntity = entityWithZonedDateTimeRepository.findById(persistedEntity.id);
+
+		assertThat(foundEntity).isPresent();
+		assertThat(persistedEntity.createdAt).isEqualTo(foundEntity.get().createdAt);
+	}
+
 	interface EntityWithBooleanRepository extends CrudRepository {}
 
+	interface EntityWithZonedDateTimeRepository extends CrudRepository {};
+
 	private static class EntityWithStringyBigDecimal {
 
 		@Id CustomId id;
@@ -152,6 +192,12 @@ private static class EntityWithStringyBigDecimal {
 		OtherEntity reference;
 	}
 
+	private static class EntityWithZonedDateTime {
+
+		@Id private Long id;
+		private ZonedDateTime createdAt;
+	}
+
 	private static class CustomId {
 
 		private final Long value;
@@ -214,4 +260,28 @@ public CustomId convert(Number source) {
 		}
 	}
 
+	@WritingConverter
+	enum ZonedDateTimeToTimestampWritingConverter implements Converter {
+
+		INSTANCE;
+
+		@Override
+		public Timestamp convert(ZonedDateTime source) {
+			return Timestamp.from(source.toInstant());
+		}
+
+	}
+
+	@ReadingConverter
+	enum ZonedDateTimeToTimestampReadingConverter implements Converter {
+
+		INSTANCE;
+
+		@Override
+		public ZonedDateTime convert(Timestamp source) {
+			return ZonedDateTime.ofInstant(source.toInstant(), ZoneOffset.ofHours(0)); // Because source.toInstant() already represents appropraite value
+		}
+
+	}
+	
 }
diff --git a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryCustomConversionIntegrationTests-db2.sql b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryCustomConversionIntegrationTests-db2.sql
index e75592d0bc..80cc288e98 100644
--- a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryCustomConversionIntegrationTests-db2.sql
+++ b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryCustomConversionIntegrationTests-db2.sql
@@ -1,5 +1,7 @@
 DROP TABLE ENTITY_WITH_STRINGY_BIG_DECIMAL;
 DROP TABLE OTHER_ENTITY;
+DROP TABLE ENTITY_WITH_ZONED_DATE_TIME;
 
 CREATE TABLE ENTITY_WITH_STRINGY_BIG_DECIMAL ( id BIGINT GENERATED BY DEFAULT AS IDENTITY(START WITH 1) PRIMARY KEY, Stringy_number DECIMAL(20,10));
 CREATE TABLE OTHER_ENTITY ( ID  BIGINT GENERATED BY DEFAULT AS IDENTITY(START WITH 1) PRIMARY KEY, CREATED DATE, ENTITY_WITH_STRINGY_BIG_DECIMAL INTEGER);
+CREATE TABLE ENTITY_WITH_ZONED_DATE_TIME (ID BIGINT GENERATED BY DEFAULT AS IDENTITY (START WITH 1) PRIMARY KEY, CREATED_AT TIMESTAMP);
\ No newline at end of file
diff --git a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryCustomConversionIntegrationTests-h2.sql b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryCustomConversionIntegrationTests-h2.sql
index d383545694..f7a744422f 100644
--- a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryCustomConversionIntegrationTests-h2.sql
+++ b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryCustomConversionIntegrationTests-h2.sql
@@ -1,2 +1,3 @@
 CREATE TABLE ENTITY_WITH_STRINGY_BIG_DECIMAL ( id IDENTITY PRIMARY KEY, Stringy_number DECIMAL(20,10));
 CREATE TABLE OTHER_ENTITY ( ID IDENTITY PRIMARY KEY, CREATED DATE, ENTITY_WITH_STRINGY_BIG_DECIMAL INTEGER);
+CREATE TABLE ENTITY_WITH_ZONED_DATE_TIME (ID BIGINT AUTO_INCREMENT PRIMARY KEY, CREATED_AT TIMESTAMP WITH TIME ZONE);
\ No newline at end of file
diff --git a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryCustomConversionIntegrationTests-hsql.sql b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryCustomConversionIntegrationTests-hsql.sql
index 78d9c930b7..6117a8bd15 100644
--- a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryCustomConversionIntegrationTests-hsql.sql
+++ b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryCustomConversionIntegrationTests-hsql.sql
@@ -1,3 +1,3 @@
 CREATE TABLE ENTITY_WITH_STRINGY_BIG_DECIMAL ( id IDENTITY PRIMARY KEY, Stringy_number DECIMAL(20,10));
 CREATE TABLE OTHER_ENTITY ( ID IDENTITY PRIMARY KEY, CREATED DATE, ENTITY_WITH_STRINGY_BIG_DECIMAL INTEGER);
-
+CREATE TABLE ENTITY_WITH_ZONED_DATE_TIME (id IDENTITY PRIMARY KEY, CREATED_AT TIMESTAMP WITH TIME ZONE);
\ No newline at end of file
diff --git a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryCustomConversionIntegrationTests-mariadb.sql b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryCustomConversionIntegrationTests-mariadb.sql
index e89bb0d951..709330fa81 100644
--- a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryCustomConversionIntegrationTests-mariadb.sql
+++ b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryCustomConversionIntegrationTests-mariadb.sql
@@ -1,2 +1,3 @@
 CREATE TABLE ENTITY_WITH_STRINGY_BIG_DECIMAL ( id BIGINT AUTO_INCREMENT PRIMARY KEY, Stringy_number DECIMAL(20,10));
 CREATE TABLE OTHER_ENTITY ( ID BIGINT AUTO_INCREMENT PRIMARY KEY, CREATED DATE, ENTITY_WITH_STRINGY_BIG_DECIMAL INTEGER);
+CREATE TABLE ENTITY_WITH_ZONED_DATE_TIME (ID BIGINT AUTO_INCREMENT PRIMARY KEY, CREATED_AT TIMESTAMP(3));
\ No newline at end of file
diff --git a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryCustomConversionIntegrationTests-mssql.sql b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryCustomConversionIntegrationTests-mssql.sql
index 34b2982692..1c366e6947 100644
--- a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryCustomConversionIntegrationTests-mssql.sql
+++ b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryCustomConversionIntegrationTests-mssql.sql
@@ -1,5 +1,7 @@
 DROP TABLE OTHER_ENTITY;
 DROP TABLE ENTITY_WITH_STRINGY_BIG_DECIMAL;
+DROP TABLE ENTITY_WITH_ZONED_DATE_TIME;
 
 CREATE TABLE ENTITY_WITH_STRINGY_BIG_DECIMAL ( id BIGINT IDENTITY PRIMARY KEY, Stringy_number DECIMAL(20,10));
 CREATE TABLE OTHER_ENTITY ( ID BIGINT IDENTITY PRIMARY KEY, CREATED DATE, ENTITY_WITH_STRINGY_BIG_DECIMAL INTEGER);
+CREATE TABLE ENTITY_WITH_ZONED_DATE_TIME (id BIGINT IDENTITY PRIMARY KEY, CREATED_AT DATETIMEOFFSET);
\ No newline at end of file
diff --git a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryCustomConversionIntegrationTests-mysql.sql b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryCustomConversionIntegrationTests-mysql.sql
index 34776fc5a8..89d55cab07 100644
--- a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryCustomConversionIntegrationTests-mysql.sql
+++ b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryCustomConversionIntegrationTests-mysql.sql
@@ -1,2 +1,3 @@
 CREATE TABLE ENTITY_WITH_STRINGY_BIG_DECIMAL ( ID BIGINT AUTO_INCREMENT PRIMARY KEY, Stringy_number DECIMAL(20,10));
 CREATE TABLE OTHER_ENTITY ( ID BIGINT AUTO_INCREMENT PRIMARY KEY, CREATED DATE, ENTITY_WITH_STRINGY_BIG_DECIMAL INTEGER);
+CREATE TABLE ENTITY_WITH_ZONED_DATE_TIME (id BIGINT AUTO_INCREMENT PRIMARY KEY, CREATED_AT TIMESTAMP(3));
\ No newline at end of file
diff --git a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryCustomConversionIntegrationTests-oracle.sql b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryCustomConversionIntegrationTests-oracle.sql
index 1b02ef7214..7d99636876 100644
--- a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryCustomConversionIntegrationTests-oracle.sql
+++ b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryCustomConversionIntegrationTests-oracle.sql
@@ -1,5 +1,6 @@
 DROP TABLE ENTITY_WITH_STRINGY_BIG_DECIMAL CASCADE CONSTRAINTS PURGE;
 DROP TABLE OTHER_ENTITY CASCADE CONSTRAINTS PURGE;
+DROP TABLE ENTITY_WITH_ZONED_DATE_TIME CASCADE CONSTRAINTS PURGE;
 
 CREATE TABLE ENTITY_WITH_STRINGY_BIG_DECIMAL (
     ID NUMBER GENERATED BY DEFAULT ON NULL AS IDENTITY PRIMARY KEY,
@@ -12,3 +13,7 @@ CREATE TABLE OTHER_ENTITY (
     ENTITY_WITH_STRINGY_BIG_DECIMAL INTEGER
 );
 
+CREATE TABLE ENTITY_WITH_ZONED_DATE_TIME (
+    id NUMBER GENERATED BY DEFAULT ON NULL AS IDENTITY PRIMARY KEY,
+    CREATED_AT TIMESTAMP WITH TIME ZONE
+);
\ No newline at end of file
diff --git a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryCustomConversionIntegrationTests-postgres.sql b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryCustomConversionIntegrationTests-postgres.sql
index c376dcf03f..d681e81761 100644
--- a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryCustomConversionIntegrationTests-postgres.sql
+++ b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryCustomConversionIntegrationTests-postgres.sql
@@ -1,2 +1,3 @@
 CREATE TABLE ENTITY_WITH_STRINGY_BIG_DECIMAL ( id SERIAL PRIMARY KEY, Stringy_number DECIMAL(20,10));
 CREATE TABLE OTHER_ENTITY ( ID SERIAL PRIMARY KEY, CREATED DATE, ENTITY_WITH_STRINGY_BIG_DECIMAL INTEGER);
+CREATE TABLE ENTITY_WITH_ZONED_DATE_TIME (id SERIAL PRIMARY KEY, CREATED_AT TIMESTAMPTZ);
\ No newline at end of file

From 95d2593d5c8685a66c0ffdd04748d5af1a8214e8 Mon Sep 17 00:00:00 2001
From: Mikhail2048 
Date: Wed, 5 Jan 2022 00:09:27 +0300
Subject: [PATCH 2/4] DATAJDBC-1089 introduce custom int sql code mappings for
 some dialects

---
 .../jdbc/core/convert/BasicJdbcConverter.java |  10 +-
 .../convert/DefaultDataAccessStrategy.java    |  17 ++-
 .../core/convert/DefaultJdbcTypeFactory.java  |  10 +-
 .../data/jdbc/core/convert/JdbcConverter.java |   3 +-
 .../repository/query/AbstractJdbcQuery.java   |   2 +-
 .../query/StringBasedJdbcQuery.java           |   9 +-
 .../data/jdbc/support/JdbcUtil.java           | 112 ++++++++++++++----
 .../DefaultDataAccessStrategyUnitTests.java   |   6 +
 ...itoryCustomConversionIntegrationTests.java |  40 ++++++-
 .../SimpleJdbcRepositoryEventsUnitTests.java  |  18 ++-
 .../data/relational/core/dialect/Dialect.java |  11 ++
 .../relational/core/dialect/MySqlDialect.java |  18 +++
 12 files changed, 216 insertions(+), 40 deletions(-)

diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/BasicJdbcConverter.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/BasicJdbcConverter.java
index 333855a5e5..5f041abdca 100644
--- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/BasicJdbcConverter.java
+++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/BasicJdbcConverter.java
@@ -32,6 +32,7 @@
 import org.springframework.core.convert.converter.Converter;
 import org.springframework.data.convert.CustomConversions;
 import org.springframework.data.jdbc.core.mapping.AggregateReference;
+import org.springframework.data.jdbc.repository.config.DialectResolver;
 import org.springframework.data.jdbc.support.JdbcUtil;
 import org.springframework.data.mapping.PersistentPropertyAccessor;
 import org.springframework.data.mapping.PersistentPropertyPath;
@@ -173,7 +174,14 @@ private Class getReferenceColumnType(RelationalPersistentProperty property) {
 	 */
 	@Override
 	public int getSqlType(RelationalPersistentProperty property) {
-		return JdbcUtil.sqlTypeFor(getColumnType(property));
+		if (typeFactory instanceof DefaultJdbcTypeFactory) {
+			return JdbcUtil.sqlTypeFor(
+					getColumnType(property),
+					DialectResolver.getDialect(((DefaultJdbcTypeFactory) typeFactory).getOperations())
+			);
+		} else {
+			return JdbcUtil.sqlTypeFor(getColumnType(property));
+		}
 	}
 
 	/*
diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/DefaultDataAccessStrategy.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/DefaultDataAccessStrategy.java
index e964b16c6f..95b4bc375a 100644
--- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/DefaultDataAccessStrategy.java
+++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/DefaultDataAccessStrategy.java
@@ -32,6 +32,7 @@
 import org.springframework.dao.OptimisticLockingFailureException;
 import org.springframework.data.domain.Pageable;
 import org.springframework.data.domain.Sort;
+import org.springframework.data.jdbc.repository.config.DialectResolver;
 import org.springframework.data.jdbc.support.JdbcUtil;
 import org.springframework.data.mapping.PersistentProperty;
 import org.springframework.data.mapping.PersistentPropertyAccessor;
@@ -548,13 +549,25 @@ private void addConvertedPropertyValue(SqlIdentifierParameterSource parameterSou
 			RelationalPersistentProperty property, @Nullable Object value, SqlIdentifier name) {
 
 		final Class javaColumnType = converter.getColumnType(property);
-		addConvertedValue(parameterSource, value, name, javaColumnType, JdbcUtil.sqlTypeFor(javaColumnType));
+		addConvertedValue(
+				parameterSource,
+				value,
+				name,
+				javaColumnType,
+				JdbcUtil.sqlTypeFor(javaColumnType, DialectResolver.getDialect(operations.getJdbcOperations()))
+		);
 	}
 
 	private void addConvertedPropertyValue(SqlIdentifierParameterSource parameterSource, SqlIdentifier name, Object value,
 			Class javaType) {
 
-		addConvertedValue(parameterSource, value, name, javaType, JdbcUtil.sqlTypeFor(javaType));
+		addConvertedValue(
+				parameterSource,
+				value,
+				name,
+				javaType,
+				JdbcUtil.sqlTypeFor(javaType, DialectResolver.getDialect(operations.getJdbcOperations()))
+		);
 	}
 
 	private void addConvertedValue(SqlIdentifierParameterSource parameterSource, @Nullable Object value,
diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/DefaultJdbcTypeFactory.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/DefaultJdbcTypeFactory.java
index c70287d1ba..95ac39fade 100644
--- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/DefaultJdbcTypeFactory.java
+++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/DefaultJdbcTypeFactory.java
@@ -19,9 +19,11 @@
 import java.sql.JDBCType;
 
 import org.springframework.data.jdbc.core.dialect.JdbcArrayColumns;
+import org.springframework.data.jdbc.repository.config.DialectResolver;
 import org.springframework.data.jdbc.support.JdbcUtil;
 import org.springframework.jdbc.core.ConnectionCallback;
 import org.springframework.jdbc.core.JdbcOperations;
+import org.springframework.lang.NonNull;
 import org.springframework.util.Assert;
 
 /**
@@ -30,6 +32,8 @@
  *
  * @author Jens Schauder
  * @author Mark Paluch
+ * @author Mikhail Polivakha
+ *
  * @since 1.1
  */
 public class DefaultJdbcTypeFactory implements JdbcTypeFactory {
@@ -68,11 +72,15 @@ public Array createArray(Object[] value) {
 
 		Class componentType = arrayColumns.getArrayType(value.getClass());
 
-		JDBCType jdbcType = JdbcUtil.jdbcTypeFor(componentType);
+		JDBCType jdbcType = JdbcUtil.jdbcTypeFor(componentType, DialectResolver.getDialect(operations));
 		Assert.notNull(jdbcType, () -> String.format("Couldn't determine JDBCType for %s", componentType));
 		String typeName = arrayColumns.getArrayTypeName(jdbcType);
 
 		return operations.execute((ConnectionCallback) c -> c.createArrayOf(typeName, value));
 	}
 
+	@NonNull
+	public JdbcOperations getOperations() {
+		return operations;
+	}
 }
diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/JdbcConverter.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/JdbcConverter.java
index a5ed27de0f..34d5228369 100644
--- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/JdbcConverter.java
+++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/JdbcConverter.java
@@ -18,6 +18,7 @@
 import java.sql.ResultSet;
 
 import org.springframework.data.relational.core.conversion.RelationalConverter;
+import org.springframework.data.relational.core.dialect.Dialect;
 import org.springframework.data.relational.core.mapping.PersistentPropertyPathExtension;
 import org.springframework.data.relational.core.mapping.RelationalPersistentEntity;
 import org.springframework.data.relational.core.mapping.RelationalPersistentProperty;
@@ -74,7 +75,7 @@ public interface JdbcConverter extends RelationalConverter {
 	 * top-level array type (e.g. {@code String[][]} returns {@code String[]}).
 	 *
 	 * @return a {@link Class} that is suitable for usage with JDBC drivers.
-	 * @see org.springframework.data.jdbc.support.JdbcUtil#sqlTypeFor(Class)
+	 * @see org.springframework.data.jdbc.support.JdbcUtil#sqlTypeFor(Class, Dialect)
 	 * @since 2.0
 	 */
 	Class getColumnType(RelationalPersistentProperty property);
diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/AbstractJdbcQuery.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/AbstractJdbcQuery.java
index 2d7df924ed..cb049b166c 100644
--- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/AbstractJdbcQuery.java
+++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/AbstractJdbcQuery.java
@@ -47,7 +47,7 @@
 public abstract class AbstractJdbcQuery implements RepositoryQuery {
 
 	private final JdbcQueryMethod queryMethod;
-	private final NamedParameterJdbcOperations operations;
+	protected final NamedParameterJdbcOperations operations;
 
 	/**
 	 * Creates a new {@link AbstractJdbcQuery} for the given {@link JdbcQueryMethod} and
diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/StringBasedJdbcQuery.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/StringBasedJdbcQuery.java
index 6f832525df..14038a6675 100644
--- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/StringBasedJdbcQuery.java
+++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/StringBasedJdbcQuery.java
@@ -26,7 +26,9 @@
 import org.springframework.data.jdbc.core.convert.JdbcColumnTypes;
 import org.springframework.data.jdbc.core.convert.JdbcConverter;
 import org.springframework.data.jdbc.core.convert.JdbcValue;
+import org.springframework.data.jdbc.repository.config.DialectResolver;
 import org.springframework.data.jdbc.support.JdbcUtil;
+import org.springframework.data.relational.core.dialect.Dialect;
 import org.springframework.data.relational.core.mapping.RelationalMappingContext;
 import org.springframework.data.relational.repository.query.RelationalParameterAccessor;
 import org.springframework.data.relational.repository.query.RelationalParametersParameterAccessor;
@@ -160,8 +162,11 @@ private void convertAndAddParameter(MapSqlParameterSource parameters, Parameter
 		Class parameterType = queryMethod.getParameters().getParameter(p.getIndex()).getType();
 		Class conversionTargetType = JdbcColumnTypes.INSTANCE.resolvePrimitiveType(parameterType);
 
-		JdbcValue jdbcValue = converter.writeJdbcValue(value, conversionTargetType,
-				JdbcUtil.sqlTypeFor(conversionTargetType));
+		JdbcValue jdbcValue = converter.writeJdbcValue(
+				value,
+				conversionTargetType,
+				JdbcUtil.sqlTypeFor(conversionTargetType, DialectResolver.getDialect(operations.getJdbcOperations()))
+		);
 
 		JDBCType jdbcType = jdbcValue.getJdbcType();
 		if (jdbcType == null) {
diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/support/JdbcUtil.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/support/JdbcUtil.java
index 8e32e7ca18..d8865b22af 100644
--- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/support/JdbcUtil.java
+++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/support/JdbcUtil.java
@@ -26,7 +26,9 @@
 import java.time.ZonedDateTime;
 import java.util.HashMap;
 import java.util.Map;
+import java.util.Optional;
 
+import org.springframework.data.relational.core.dialect.Dialect;
 import org.springframework.jdbc.support.JdbcUtils;
 import org.springframework.lang.Nullable;
 import org.springframework.util.Assert;
@@ -40,33 +42,33 @@
  */
 public final class JdbcUtil {
 
-	private static final Map, Integer> sqlTypeMappings = new HashMap<>();
+	private static final Map, Integer> commonSqlTypeMappings = new HashMap<>();
 
 	static {
 
-		sqlTypeMappings.put(String.class, Types.VARCHAR);
-		sqlTypeMappings.put(BigInteger.class, Types.BIGINT);
-		sqlTypeMappings.put(BigDecimal.class, Types.DECIMAL);
-		sqlTypeMappings.put(Byte.class, Types.TINYINT);
-		sqlTypeMappings.put(byte.class, Types.TINYINT);
-		sqlTypeMappings.put(Short.class, Types.SMALLINT);
-		sqlTypeMappings.put(short.class, Types.SMALLINT);
-		sqlTypeMappings.put(Integer.class, Types.INTEGER);
-		sqlTypeMappings.put(int.class, Types.INTEGER);
-		sqlTypeMappings.put(Long.class, Types.BIGINT);
-		sqlTypeMappings.put(long.class, Types.BIGINT);
-		sqlTypeMappings.put(Double.class, Types.DOUBLE);
-		sqlTypeMappings.put(double.class, Types.DOUBLE);
-		sqlTypeMappings.put(Float.class, Types.REAL);
-		sqlTypeMappings.put(float.class, Types.REAL);
-		sqlTypeMappings.put(Boolean.class, Types.BIT);
-		sqlTypeMappings.put(boolean.class, Types.BIT);
-		sqlTypeMappings.put(byte[].class, Types.VARBINARY);
-		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);
-		sqlTypeMappings.put(ZonedDateTime.class, Types.TIMESTAMP_WITH_TIMEZONE);
+		commonSqlTypeMappings.put(String.class, Types.VARCHAR);
+		commonSqlTypeMappings.put(BigInteger.class, Types.BIGINT);
+		commonSqlTypeMappings.put(BigDecimal.class, Types.DECIMAL);
+		commonSqlTypeMappings.put(Byte.class, Types.TINYINT);
+		commonSqlTypeMappings.put(byte.class, Types.TINYINT);
+		commonSqlTypeMappings.put(Short.class, Types.SMALLINT);
+		commonSqlTypeMappings.put(short.class, Types.SMALLINT);
+		commonSqlTypeMappings.put(Integer.class, Types.INTEGER);
+		commonSqlTypeMappings.put(int.class, Types.INTEGER);
+		commonSqlTypeMappings.put(Long.class, Types.BIGINT);
+		commonSqlTypeMappings.put(long.class, Types.BIGINT);
+		commonSqlTypeMappings.put(Double.class, Types.DOUBLE);
+		commonSqlTypeMappings.put(double.class, Types.DOUBLE);
+		commonSqlTypeMappings.put(Float.class, Types.REAL);
+		commonSqlTypeMappings.put(float.class, Types.REAL);
+		commonSqlTypeMappings.put(Boolean.class, Types.BIT);
+		commonSqlTypeMappings.put(boolean.class, Types.BIT);
+		commonSqlTypeMappings.put(byte[].class, Types.VARBINARY);
+		commonSqlTypeMappings.put(Date.class, Types.DATE);
+		commonSqlTypeMappings.put(Time.class, Types.TIME);
+		commonSqlTypeMappings.put(Timestamp.class, Types.TIMESTAMP);
+		commonSqlTypeMappings.put(OffsetDateTime.class, Types.TIMESTAMP_WITH_TIMEZONE);
+		commonSqlTypeMappings.put(ZonedDateTime.class, Types.TIMESTAMP_WITH_TIMEZONE);
 	}
 
 	private JdbcUtil() {
@@ -79,18 +81,52 @@ private JdbcUtil() {
 	 *
 	 * @param type The type of value to be bound to a {@link java.sql.PreparedStatement}.
 	 * @return One of the values defined in {@link Types} or {@link JdbcUtils#TYPE_UNKNOWN}.
+	 * @deprecated because this method will make lookup only in common sql types map and therefore
+	 * 			   will not take into account dialect specific sql types codes mappings. When possible,
+	 * 			   please, use {@link #sqlTypeFor(Class, Dialect)}
 	 */
+	@Deprecated
 	public static int sqlTypeFor(Class type) {
 
 		Assert.notNull(type, "Type must not be null.");
 
-		return sqlTypeMappings.keySet().stream() //
+		return commonSqlTypeMappings.keySet().stream() //
 				.filter(k -> k.isAssignableFrom(type)) //
 				.findFirst() //
-				.map(sqlTypeMappings::get) //
+				.map(commonSqlTypeMappings::get) //
 				.orElse(JdbcUtils.TYPE_UNKNOWN);
 	}
 
+	/**
+	 * Returns the {@link Types} value suitable for passing a value of the provided type to a
+	 * {@link java.sql.PreparedStatement} giving regards to passed dialect specific sql codes
+	 * mappings
+	 *
+	 * The main motivation for this method is, in general, the fact that we cannot assign the same int code for the same java class for
+	 * each dialect. For example, currently, there MySQL Driver cannot handle {@link Types#TIMESTAMP_WITH_TIMEZONE}
+	 * to be set by the means of {@link java.sql.PreparedStatement#setObject(int, Object, int)}. If we We need to instead set
+	 * it be the mean
+	 *
+	 * @param type The type of value to be bound to a {@link java.sql.PreparedStatement}.
+	 * @param dialect represents the dialect, in the context of which the int sql code must be derived
+	 * @return the int code of sql type in regards to custom dialect mappings
+	 *
+	 * @see Types
+	 */
+	public static int sqlTypeFor(Class type, Dialect dialect) {
+
+		Assert.notNull(type, "Type must not be null.");
+
+		return Optional
+				.ofNullable(dialect.getCustomSqlCodesMappings().get(type))
+				.orElse(commonSqlTypeMappings.keySet().stream()
+						.filter(k -> k.isAssignableFrom(type))
+						.findFirst()
+						.map(commonSqlTypeMappings::get)
+						.orElse(JdbcUtils.TYPE_UNKNOWN)
+				);
+	}
+
 	/**
 	 * Converts a {@link JDBCType} to an {@code int} value as defined in {@link Types}.
 	 *
@@ -124,9 +160,33 @@ public static JDBCType jdbcTypeFor(int sqlType) {
 	 *
 	 * @param type The type of value to be bound to a {@link java.sql.PreparedStatement}.
 	 * @return a matching {@link JDBCType} instance or {@literal null}.
+	 * @deprecated because this method will make lookup only in common sql types map and therefore
+	 * 			   will not take into account dialect specific sql types codes mappings. When possible,
+	 * 			   please, use {@link #jdbcTypeFor(Class, Dialect)}
 	 */
 	@Nullable
+	@Deprecated
 	public static JDBCType jdbcTypeFor(Class type) {
 		return jdbcTypeFor(sqlTypeFor(type));
 	}
+
+
+	/**
+	 * Returns the {@link JDBCType} suitable for passing a value of the provided type to a
+	 * {@link java.sql.PreparedStatement} giving regards to passed dialect specific sql codes
+	 * mappings
+	 *
+	 * The main motivation for this method is, in general, the fact that we cannot assign the same int code for the same java class for
+	 * each dialect. For example, currently, there MySQL Driver cannot handle {@link Types#TIMESTAMP_WITH_TIMEZONE}
+	 * to be set by the means of {@link java.sql.PreparedStatement#setObject(int, Object, int)}. If we We need to instead set
+	 * it be the mean
+	 *
+	 * @param type The type of value to be bound to a {@link java.sql.PreparedStatement}.
+	 * @param dialect represents the dialect, in the context of which the int sql code must be derived
+	 * @return a matching {@link JDBCType} instance or {@literal null}.
+	 */
+	@Nullable
+	public static JDBCType jdbcTypeFor(Class type, Dialect dialect) {
+		return jdbcTypeFor(sqlTypeFor(type, dialect));
+	}
 }
diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/DefaultDataAccessStrategyUnitTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/DefaultDataAccessStrategyUnitTests.java
index 5098823c29..fc11792bc0 100644
--- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/DefaultDataAccessStrategyUnitTests.java
+++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/DefaultDataAccessStrategyUnitTests.java
@@ -45,6 +45,7 @@
 import org.springframework.data.relational.core.mapping.RelationalMappingContext;
 import org.springframework.data.relational.core.mapping.RelationalPersistentProperty;
 import org.springframework.data.relational.core.sql.SqlIdentifier;
+import org.springframework.jdbc.core.ConnectionCallback;
 import org.springframework.jdbc.core.JdbcOperations;
 import org.springframework.jdbc.core.RowMapper;
 import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations;
@@ -84,6 +85,11 @@ public void before() {
 
 		converter = new BasicJdbcConverter(context, relationResolver, new JdbcCustomConversions(),
 				new DefaultJdbcTypeFactory(jdbcOperations), dialect.getIdentifierProcessing());
+
+		when(jdbcOperations.execute(any(ConnectionCallback.class))).thenReturn(dialect);
+
+		when(namedJdbcOperations.getJdbcOperations()).thenReturn(jdbcOperations);
+
 		accessStrategy = new DefaultDataAccessStrategy( //
 				new SqlGeneratorSource(context, converter, dialect), //
 				context, //
diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryCustomConversionIntegrationTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryCustomConversionIntegrationTests.java
index 308f9ccce5..6d7445cbe8 100644
--- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryCustomConversionIntegrationTests.java
+++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryCustomConversionIntegrationTests.java
@@ -15,8 +15,8 @@
  */
 package org.springframework.data.jdbc.repository;
 
+import org.junit.jupiter.api.Disabled;
 import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.condition.EnabledIfSystemProperty;
 import org.junit.jupiter.api.extension.ExtendWith;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.context.annotation.Bean;
@@ -28,10 +28,12 @@
 import org.springframework.data.convert.WritingConverter;
 import org.springframework.data.jdbc.core.convert.JdbcCustomConversions;
 import org.springframework.data.jdbc.core.convert.JdbcValue;
+import org.springframework.data.jdbc.core.dialect.H2TimestampWithTimeZoneToOffsetDateTimeConverter;
 import org.springframework.data.jdbc.core.dialect.H2TimestampWithTimeZoneToZonedDateTimeConverter;
 import org.springframework.data.jdbc.repository.support.JdbcRepositoryFactory;
 import org.springframework.data.jdbc.testing.AssumeFeatureTestExecutionListener;
 import org.springframework.data.jdbc.testing.TestConfiguration;
+import org.springframework.data.relational.core.mapping.Table;
 import org.springframework.data.repository.CrudRepository;
 import org.springframework.test.context.ContextConfiguration;
 import org.springframework.test.context.TestExecutionListeners;
@@ -41,6 +43,7 @@
 import java.math.BigDecimal;
 import java.sql.JDBCType;
 import java.sql.Timestamp;
+import java.time.OffsetDateTime;
 import java.time.ZoneOffset;
 import java.time.ZonedDateTime;
 import java.util.Date;
@@ -85,6 +88,11 @@ EntityWithZonedDateTimeRepository entityWithZonedDateTimeRepository() {
 			return factory.getRepository(EntityWithZonedDateTimeRepository.class);
 		}
 
+		@Bean
+		EntityWithOffsetDateTimeRepository entityWithOffsetDateTimeRepository() {
+			return factory.getRepository(EntityWithOffsetDateTimeRepository.class);
+		}
+
 		@Bean
 		JdbcCustomConversions jdbcCustomConversions() {
 			return new JdbcCustomConversions(
@@ -95,7 +103,8 @@ JdbcCustomConversions jdbcCustomConversions() {
 							CustomIdWritingConverter.INSTANCE,
 							ZonedDateTimeToTimestampWritingConverter.INSTANCE,
 							ZonedDateTimeToTimestampReadingConverter.INSTANCE,
-							H2TimestampWithTimeZoneToZonedDateTimeConverter.INSTANCE
+							H2TimestampWithTimeZoneToZonedDateTimeConverter.INSTANCE,
+							H2TimestampWithTimeZoneToOffsetDateTimeConverter.INSTANCE
 					)
 			);
 		}
@@ -105,6 +114,8 @@ JdbcCustomConversions jdbcCustomConversions() {
 
 	@Autowired EntityWithZonedDateTimeRepository entityWithZonedDateTimeRepository;
 
+	@Autowired EntityWithOffsetDateTimeRepository entityWithOffsetDateTimeRepository;
+
 	/**
 	 * In PostgreSQL this fails if a simple converter like the following is used.
 	 *
@@ -181,23 +192,44 @@ public void testZonedDateTimeToTimestampConversion() {
 		assertThat(persistedEntity.createdAt).isEqualTo(foundEntity.get().createdAt);
 	}
 
+	/**
+	 * DATAJDBC-1089
+	 */
+	@Test
+	public void testOffsetDateTimeToTimestampConversion() {
+		EntityWithOffsetDateTime entity = new EntityWithOffsetDateTime();
+		entity.createdAt = OffsetDateTime.now(ZoneOffset.ofHours(3));
+
+		final EntityWithOffsetDateTime persistedEntity = entityWithOffsetDateTimeRepository.save(entity);
+		final Optional foundEntity = entityWithOffsetDateTimeRepository.findById(persistedEntity.id);
+
+		assertThat(foundEntity).isPresent();
+		assertThat(persistedEntity.createdAt).isEqualTo(foundEntity.get().createdAt);
+	}
+
 	interface EntityWithBooleanRepository extends CrudRepository {}
 
 	interface EntityWithZonedDateTimeRepository extends CrudRepository {};
 
-	private static class EntityWithStringyBigDecimal {
+	interface EntityWithOffsetDateTimeRepository extends CrudRepository {};
 
+	private static class EntityWithStringyBigDecimal {
 		@Id CustomId id;
 		String stringyNumber;
 		OtherEntity reference;
 	}
 
 	private static class EntityWithZonedDateTime {
-
 		@Id private Long id;
 		private ZonedDateTime createdAt;
 	}
 
+	@Table(name = "ENTITY_WITH_ZONED_DATE_TIME")
+	private static class EntityWithOffsetDateTime {
+		@Id private Long id;
+		private OffsetDateTime createdAt;
+	}
+
 	private static class CustomId {
 
 		private final Long value;
diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/SimpleJdbcRepositoryEventsUnitTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/SimpleJdbcRepositoryEventsUnitTests.java
index 31d51e8663..2fd71cbfae 100644
--- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/SimpleJdbcRepositoryEventsUnitTests.java
+++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/SimpleJdbcRepositoryEventsUnitTests.java
@@ -27,6 +27,8 @@
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
+import java.util.Optional;
+import java.util.function.BiFunction;
 
 import org.assertj.core.groups.Tuple;
 import org.junit.jupiter.api.BeforeEach;
@@ -45,14 +47,18 @@
 import org.springframework.data.jdbc.core.convert.JdbcCustomConversions;
 import org.springframework.data.jdbc.core.convert.SqlGeneratorSource;
 import org.springframework.data.jdbc.core.mapping.JdbcMappingContext;
+import org.springframework.data.jdbc.repository.config.DialectResolver;
+import org.springframework.data.jdbc.repository.config.DialectResolver.DefaultDialectProvider;
 import org.springframework.data.jdbc.repository.support.JdbcRepositoryFactory;
 import org.springframework.data.jdbc.repository.support.SimpleJdbcRepository;
 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.PostgresDialect;
 import org.springframework.data.relational.core.mapping.RelationalMappingContext;
 import org.springframework.data.relational.core.mapping.event.*;
 import org.springframework.data.repository.PagingAndSortingRepository;
+import org.springframework.jdbc.core.ConnectionCallback;
 import org.springframework.jdbc.core.JdbcOperations;
 import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations;
 import org.springframework.jdbc.core.namedparam.SqlParameterSource;
@@ -80,11 +86,17 @@ public class SimpleJdbcRepositoryEventsUnitTests {
 	public void before() {
 
 		RelationalMappingContext context = new JdbcMappingContext();
+
 		NamedParameterJdbcOperations operations = createIdGeneratingOperations();
+
+		JdbcOperations mockJdbcOperations = operations.getJdbcOperations();
+
+		when(mockJdbcOperations.execute(any(ConnectionCallback.class))).thenReturn(PostgresDialect.INSTANCE);
+
 		DelegatingDataAccessStrategy delegatingDataAccessStrategy = new DelegatingDataAccessStrategy();
 		Dialect dialect = HsqlDbDialect.INSTANCE;
 		JdbcConverter converter = new BasicJdbcConverter(context, delegatingDataAccessStrategy, new JdbcCustomConversions(),
-				new DefaultJdbcTypeFactory(operations.getJdbcOperations()), dialect.getIdentifierProcessing());
+				new DefaultJdbcTypeFactory(mockJdbcOperations), dialect.getIdentifierProcessing());
 		SqlGeneratorSource generatorSource = new SqlGeneratorSource(context, converter, dialect);
 
 		this.dataAccessStrategy = spy(new DefaultDataAccessStrategy(generatorSource, context, converter, operations));
@@ -290,10 +302,12 @@ private static NamedParameterJdbcOperations createIdGeneratingOperations() {
 			return 1;
 		};
 
+		JdbcOperations mockJdbcOperations = mock(JdbcOperations.class);
+
 		NamedParameterJdbcOperations operations = mock(NamedParameterJdbcOperations.class);
 		when(operations.update(anyString(), any(SqlParameterSource.class), any(KeyHolder.class)))
 				.thenAnswer(setIdInKeyHolder);
-		when(operations.getJdbcOperations()).thenReturn(mock(JdbcOperations.class));
+		when(operations.getJdbcOperations()).thenReturn(mockJdbcOperations);
 		return operations;
 	}
 
diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/Dialect.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/Dialect.java
index 5febb8c52f..00a1b39d6c 100644
--- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/Dialect.java
+++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/Dialect.java
@@ -17,11 +17,15 @@
 
 import java.util.Collection;
 import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
 import java.util.Set;
 
+import org.jetbrains.annotations.NotNull;
 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;
+import org.springframework.lang.NonNull;
 
 /**
  * Represents a dialect that is implemented by a particular database. Please note that not all features are supported by
@@ -120,4 +124,11 @@ default Set> simpleTypes() {
 	default InsertRenderContext getInsertRenderContext() {
 		return InsertRenderContexts.DEFAULT;
 	}
+
+	/**
+	 * @return the map of custom mappings from java classes to sql codes (integer sql codes present in {@link java.sql.Types})
+	 * @since 3.0
+	 */
+	@NonNull
+	default Map, Integer> getCustomSqlCodesMappings() { return new HashMap<>(); }
 }
diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/MySqlDialect.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/MySqlDialect.java
index 6032582186..944a74195d 100644
--- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/MySqlDialect.java
+++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/MySqlDialect.java
@@ -15,13 +15,18 @@
  */
 package org.springframework.data.relational.core.dialect;
 
+import java.sql.Types;
+import java.time.OffsetDateTime;
+import java.time.ZonedDateTime;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.Map;
 
 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.lang.NonNull;
 import org.springframework.util.Assert;
 
 /**
@@ -169,4 +174,17 @@ public IdentifierProcessing getIdentifierProcessing() {
 	public Collection getConverters() {
 		return Collections.singletonList(TimestampAtUtcToOffsetDateTimeConverter.INSTANCE);
 	}
+
+	/*
+	 * (non-Javadoc)
+	 * @see org.springframework.data.relational.core.dialect.Dialect#getCustomSqlCodesMappings()
+	 */
+	@NonNull
+    @Override
+    public Map, Integer> getCustomSqlCodesMappings() {
+		Map, Integer> customSqlCodesMappings = super.getCustomSqlCodesMappings();
+		customSqlCodesMappings.put(OffsetDateTime.class, Types.TIMESTAMP);
+		customSqlCodesMappings.put(ZonedDateTime.class, Types.TIMESTAMP);
+		return customSqlCodesMappings;
+    }
 }

From 5ae37da54aa494332d43c67387ed5a6cf361a06b Mon Sep 17 00:00:00 2001
From: Mikhail2048 
Date: Wed, 5 Jan 2022 00:09:27 +0300
Subject: [PATCH 3/4] DATAJDBC-1089 introduce custom int sql code mappings for
 some dialects

---
 .../jdbc/core/convert/BasicJdbcConverter.java |  10 +-
 .../convert/DefaultDataAccessStrategy.java    |  17 ++-
 .../core/convert/DefaultJdbcTypeFactory.java  |  10 +-
 .../jdbc/core/convert/JdbcColumnTypes.java    |   2 +-
 .../data/jdbc/core/convert/JdbcConverter.java |   3 +-
 .../Jsr310TimestampBasedConverters.java       |   1 +
 .../repository/query/AbstractJdbcQuery.java   |   2 +-
 .../query/StringBasedJdbcQuery.java           |   9 +-
 .../data/jdbc/support/JdbcUtil.java           | 112 ++++++++++++++----
 .../convert/BasicJdbcConverterUnitTests.java  |   2 +-
 .../DefaultDataAccessStrategyUnitTests.java   |   6 +
 ...itoryCustomConversionIntegrationTests.java |  63 +++++++---
 .../SimpleJdbcRepositoryEventsUnitTests.java  |  18 ++-
 .../testing/MySqlDataSourceConfiguration.java |   3 +-
 ...ryCustomConversionIntegrationTests-db2.sql |   4 +-
 ...oryCustomConversionIntegrationTests-h2.sql |   3 +-
 ...yCustomConversionIntegrationTests-hsql.sql |   3 +-
 ...stomConversionIntegrationTests-mariadb.sql |   3 +-
 ...CustomConversionIntegrationTests-mssql.sql |   4 +-
 ...CustomConversionIntegrationTests-mysql.sql |   3 +-
 ...ustomConversionIntegrationTests-oracle.sql |   6 +
 ...tomConversionIntegrationTests-postgres.sql |   3 +-
 .../data/relational/core/dialect/Dialect.java |  11 ++
 .../relational/core/dialect/MySqlDialect.java |  18 +++
 24 files changed, 251 insertions(+), 65 deletions(-)

diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/BasicJdbcConverter.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/BasicJdbcConverter.java
index 333855a5e5..5f041abdca 100644
--- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/BasicJdbcConverter.java
+++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/BasicJdbcConverter.java
@@ -32,6 +32,7 @@
 import org.springframework.core.convert.converter.Converter;
 import org.springframework.data.convert.CustomConversions;
 import org.springframework.data.jdbc.core.mapping.AggregateReference;
+import org.springframework.data.jdbc.repository.config.DialectResolver;
 import org.springframework.data.jdbc.support.JdbcUtil;
 import org.springframework.data.mapping.PersistentPropertyAccessor;
 import org.springframework.data.mapping.PersistentPropertyPath;
@@ -173,7 +174,14 @@ private Class getReferenceColumnType(RelationalPersistentProperty property) {
 	 */
 	@Override
 	public int getSqlType(RelationalPersistentProperty property) {
-		return JdbcUtil.sqlTypeFor(getColumnType(property));
+		if (typeFactory instanceof DefaultJdbcTypeFactory) {
+			return JdbcUtil.sqlTypeFor(
+					getColumnType(property),
+					DialectResolver.getDialect(((DefaultJdbcTypeFactory) typeFactory).getOperations())
+			);
+		} else {
+			return JdbcUtil.sqlTypeFor(getColumnType(property));
+		}
 	}
 
 	/*
diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/DefaultDataAccessStrategy.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/DefaultDataAccessStrategy.java
index e964b16c6f..95b4bc375a 100644
--- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/DefaultDataAccessStrategy.java
+++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/DefaultDataAccessStrategy.java
@@ -32,6 +32,7 @@
 import org.springframework.dao.OptimisticLockingFailureException;
 import org.springframework.data.domain.Pageable;
 import org.springframework.data.domain.Sort;
+import org.springframework.data.jdbc.repository.config.DialectResolver;
 import org.springframework.data.jdbc.support.JdbcUtil;
 import org.springframework.data.mapping.PersistentProperty;
 import org.springframework.data.mapping.PersistentPropertyAccessor;
@@ -548,13 +549,25 @@ private void addConvertedPropertyValue(SqlIdentifierParameterSource parameterSou
 			RelationalPersistentProperty property, @Nullable Object value, SqlIdentifier name) {
 
 		final Class javaColumnType = converter.getColumnType(property);
-		addConvertedValue(parameterSource, value, name, javaColumnType, JdbcUtil.sqlTypeFor(javaColumnType));
+		addConvertedValue(
+				parameterSource,
+				value,
+				name,
+				javaColumnType,
+				JdbcUtil.sqlTypeFor(javaColumnType, DialectResolver.getDialect(operations.getJdbcOperations()))
+		);
 	}
 
 	private void addConvertedPropertyValue(SqlIdentifierParameterSource parameterSource, SqlIdentifier name, Object value,
 			Class javaType) {
 
-		addConvertedValue(parameterSource, value, name, javaType, JdbcUtil.sqlTypeFor(javaType));
+		addConvertedValue(
+				parameterSource,
+				value,
+				name,
+				javaType,
+				JdbcUtil.sqlTypeFor(javaType, DialectResolver.getDialect(operations.getJdbcOperations()))
+		);
 	}
 
 	private void addConvertedValue(SqlIdentifierParameterSource parameterSource, @Nullable Object value,
diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/DefaultJdbcTypeFactory.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/DefaultJdbcTypeFactory.java
index c70287d1ba..95ac39fade 100644
--- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/DefaultJdbcTypeFactory.java
+++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/DefaultJdbcTypeFactory.java
@@ -19,9 +19,11 @@
 import java.sql.JDBCType;
 
 import org.springframework.data.jdbc.core.dialect.JdbcArrayColumns;
+import org.springframework.data.jdbc.repository.config.DialectResolver;
 import org.springframework.data.jdbc.support.JdbcUtil;
 import org.springframework.jdbc.core.ConnectionCallback;
 import org.springframework.jdbc.core.JdbcOperations;
+import org.springframework.lang.NonNull;
 import org.springframework.util.Assert;
 
 /**
@@ -30,6 +32,8 @@
  *
  * @author Jens Schauder
  * @author Mark Paluch
+ * @author Mikhail Polivakha
+ *
  * @since 1.1
  */
 public class DefaultJdbcTypeFactory implements JdbcTypeFactory {
@@ -68,11 +72,15 @@ public Array createArray(Object[] value) {
 
 		Class componentType = arrayColumns.getArrayType(value.getClass());
 
-		JDBCType jdbcType = JdbcUtil.jdbcTypeFor(componentType);
+		JDBCType jdbcType = JdbcUtil.jdbcTypeFor(componentType, DialectResolver.getDialect(operations));
 		Assert.notNull(jdbcType, () -> String.format("Couldn't determine JDBCType for %s", componentType));
 		String typeName = arrayColumns.getArrayTypeName(jdbcType);
 
 		return operations.execute((ConnectionCallback) c -> c.createArrayOf(typeName, value));
 	}
 
+	@NonNull
+	public JdbcOperations getOperations() {
+		return operations;
+	}
 }
diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/JdbcColumnTypes.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/JdbcColumnTypes.java
index 87feb6eeab..e4a7eed0c4 100644
--- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/JdbcColumnTypes.java
+++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/JdbcColumnTypes.java
@@ -54,7 +54,7 @@ public Class resolvePrimitiveType(Class type) {
 	static {
 
 		javaToDbType.put(Enum.class, String.class);
-		javaToDbType.put(ZonedDateTime.class, ZonedDateTime.class);
+		javaToDbType.put(ZonedDateTime.class, OffsetDateTime.class);
 		javaToDbType.put(OffsetDateTime.class, OffsetDateTime.class);
 		javaToDbType.put(LocalDateTime.class, LocalDateTime.class);
 		javaToDbType.put(Temporal.class, Timestamp.class);
diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/JdbcConverter.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/JdbcConverter.java
index a5ed27de0f..34d5228369 100644
--- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/JdbcConverter.java
+++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/JdbcConverter.java
@@ -18,6 +18,7 @@
 import java.sql.ResultSet;
 
 import org.springframework.data.relational.core.conversion.RelationalConverter;
+import org.springframework.data.relational.core.dialect.Dialect;
 import org.springframework.data.relational.core.mapping.PersistentPropertyPathExtension;
 import org.springframework.data.relational.core.mapping.RelationalPersistentEntity;
 import org.springframework.data.relational.core.mapping.RelationalPersistentProperty;
@@ -74,7 +75,7 @@ public interface JdbcConverter extends RelationalConverter {
 	 * top-level array type (e.g. {@code String[][]} returns {@code String[]}).
 	 *
 	 * @return a {@link Class} that is suitable for usage with JDBC drivers.
-	 * @see org.springframework.data.jdbc.support.JdbcUtil#sqlTypeFor(Class)
+	 * @see org.springframework.data.jdbc.support.JdbcUtil#sqlTypeFor(Class, Dialect)
 	 * @since 2.0
 	 */
 	Class getColumnType(RelationalPersistentProperty property);
diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/Jsr310TimestampBasedConverters.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/Jsr310TimestampBasedConverters.java
index a331c8183d..91bec34183 100644
--- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/Jsr310TimestampBasedConverters.java
+++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/Jsr310TimestampBasedConverters.java
@@ -26,6 +26,7 @@
 import java.time.LocalTime;
 import java.time.Period;
 import java.time.ZoneId;
+import java.time.ZonedDateTime;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/AbstractJdbcQuery.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/AbstractJdbcQuery.java
index 2d7df924ed..cb049b166c 100644
--- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/AbstractJdbcQuery.java
+++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/AbstractJdbcQuery.java
@@ -47,7 +47,7 @@
 public abstract class AbstractJdbcQuery implements RepositoryQuery {
 
 	private final JdbcQueryMethod queryMethod;
-	private final NamedParameterJdbcOperations operations;
+	protected final NamedParameterJdbcOperations operations;
 
 	/**
 	 * Creates a new {@link AbstractJdbcQuery} for the given {@link JdbcQueryMethod} and
diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/StringBasedJdbcQuery.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/StringBasedJdbcQuery.java
index 6f832525df..14038a6675 100644
--- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/StringBasedJdbcQuery.java
+++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/StringBasedJdbcQuery.java
@@ -26,7 +26,9 @@
 import org.springframework.data.jdbc.core.convert.JdbcColumnTypes;
 import org.springframework.data.jdbc.core.convert.JdbcConverter;
 import org.springframework.data.jdbc.core.convert.JdbcValue;
+import org.springframework.data.jdbc.repository.config.DialectResolver;
 import org.springframework.data.jdbc.support.JdbcUtil;
+import org.springframework.data.relational.core.dialect.Dialect;
 import org.springframework.data.relational.core.mapping.RelationalMappingContext;
 import org.springframework.data.relational.repository.query.RelationalParameterAccessor;
 import org.springframework.data.relational.repository.query.RelationalParametersParameterAccessor;
@@ -160,8 +162,11 @@ private void convertAndAddParameter(MapSqlParameterSource parameters, Parameter
 		Class parameterType = queryMethod.getParameters().getParameter(p.getIndex()).getType();
 		Class conversionTargetType = JdbcColumnTypes.INSTANCE.resolvePrimitiveType(parameterType);
 
-		JdbcValue jdbcValue = converter.writeJdbcValue(value, conversionTargetType,
-				JdbcUtil.sqlTypeFor(conversionTargetType));
+		JdbcValue jdbcValue = converter.writeJdbcValue(
+				value,
+				conversionTargetType,
+				JdbcUtil.sqlTypeFor(conversionTargetType, DialectResolver.getDialect(operations.getJdbcOperations()))
+		);
 
 		JDBCType jdbcType = jdbcValue.getJdbcType();
 		if (jdbcType == null) {
diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/support/JdbcUtil.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/support/JdbcUtil.java
index 8e32e7ca18..d8865b22af 100644
--- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/support/JdbcUtil.java
+++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/support/JdbcUtil.java
@@ -26,7 +26,9 @@
 import java.time.ZonedDateTime;
 import java.util.HashMap;
 import java.util.Map;
+import java.util.Optional;
 
+import org.springframework.data.relational.core.dialect.Dialect;
 import org.springframework.jdbc.support.JdbcUtils;
 import org.springframework.lang.Nullable;
 import org.springframework.util.Assert;
@@ -40,33 +42,33 @@
  */
 public final class JdbcUtil {
 
-	private static final Map, Integer> sqlTypeMappings = new HashMap<>();
+	private static final Map, Integer> commonSqlTypeMappings = new HashMap<>();
 
 	static {
 
-		sqlTypeMappings.put(String.class, Types.VARCHAR);
-		sqlTypeMappings.put(BigInteger.class, Types.BIGINT);
-		sqlTypeMappings.put(BigDecimal.class, Types.DECIMAL);
-		sqlTypeMappings.put(Byte.class, Types.TINYINT);
-		sqlTypeMappings.put(byte.class, Types.TINYINT);
-		sqlTypeMappings.put(Short.class, Types.SMALLINT);
-		sqlTypeMappings.put(short.class, Types.SMALLINT);
-		sqlTypeMappings.put(Integer.class, Types.INTEGER);
-		sqlTypeMappings.put(int.class, Types.INTEGER);
-		sqlTypeMappings.put(Long.class, Types.BIGINT);
-		sqlTypeMappings.put(long.class, Types.BIGINT);
-		sqlTypeMappings.put(Double.class, Types.DOUBLE);
-		sqlTypeMappings.put(double.class, Types.DOUBLE);
-		sqlTypeMappings.put(Float.class, Types.REAL);
-		sqlTypeMappings.put(float.class, Types.REAL);
-		sqlTypeMappings.put(Boolean.class, Types.BIT);
-		sqlTypeMappings.put(boolean.class, Types.BIT);
-		sqlTypeMappings.put(byte[].class, Types.VARBINARY);
-		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);
-		sqlTypeMappings.put(ZonedDateTime.class, Types.TIMESTAMP_WITH_TIMEZONE);
+		commonSqlTypeMappings.put(String.class, Types.VARCHAR);
+		commonSqlTypeMappings.put(BigInteger.class, Types.BIGINT);
+		commonSqlTypeMappings.put(BigDecimal.class, Types.DECIMAL);
+		commonSqlTypeMappings.put(Byte.class, Types.TINYINT);
+		commonSqlTypeMappings.put(byte.class, Types.TINYINT);
+		commonSqlTypeMappings.put(Short.class, Types.SMALLINT);
+		commonSqlTypeMappings.put(short.class, Types.SMALLINT);
+		commonSqlTypeMappings.put(Integer.class, Types.INTEGER);
+		commonSqlTypeMappings.put(int.class, Types.INTEGER);
+		commonSqlTypeMappings.put(Long.class, Types.BIGINT);
+		commonSqlTypeMappings.put(long.class, Types.BIGINT);
+		commonSqlTypeMappings.put(Double.class, Types.DOUBLE);
+		commonSqlTypeMappings.put(double.class, Types.DOUBLE);
+		commonSqlTypeMappings.put(Float.class, Types.REAL);
+		commonSqlTypeMappings.put(float.class, Types.REAL);
+		commonSqlTypeMappings.put(Boolean.class, Types.BIT);
+		commonSqlTypeMappings.put(boolean.class, Types.BIT);
+		commonSqlTypeMappings.put(byte[].class, Types.VARBINARY);
+		commonSqlTypeMappings.put(Date.class, Types.DATE);
+		commonSqlTypeMappings.put(Time.class, Types.TIME);
+		commonSqlTypeMappings.put(Timestamp.class, Types.TIMESTAMP);
+		commonSqlTypeMappings.put(OffsetDateTime.class, Types.TIMESTAMP_WITH_TIMEZONE);
+		commonSqlTypeMappings.put(ZonedDateTime.class, Types.TIMESTAMP_WITH_TIMEZONE);
 	}
 
 	private JdbcUtil() {
@@ -79,18 +81,52 @@ private JdbcUtil() {
 	 *
 	 * @param type The type of value to be bound to a {@link java.sql.PreparedStatement}.
 	 * @return One of the values defined in {@link Types} or {@link JdbcUtils#TYPE_UNKNOWN}.
+	 * @deprecated because this method will make lookup only in common sql types map and therefore
+	 * 			   will not take into account dialect specific sql types codes mappings. When possible,
+	 * 			   please, use {@link #sqlTypeFor(Class, Dialect)}
 	 */
+	@Deprecated
 	public static int sqlTypeFor(Class type) {
 
 		Assert.notNull(type, "Type must not be null.");
 
-		return sqlTypeMappings.keySet().stream() //
+		return commonSqlTypeMappings.keySet().stream() //
 				.filter(k -> k.isAssignableFrom(type)) //
 				.findFirst() //
-				.map(sqlTypeMappings::get) //
+				.map(commonSqlTypeMappings::get) //
 				.orElse(JdbcUtils.TYPE_UNKNOWN);
 	}
 
+	/**
+	 * Returns the {@link Types} value suitable for passing a value of the provided type to a
+	 * {@link java.sql.PreparedStatement} giving regards to passed dialect specific sql codes
+	 * mappings
+	 *
+	 * The main motivation for this method is, in general, the fact that we cannot assign the same int code for the same java class for
+	 * each dialect. For example, currently, there MySQL Driver cannot handle {@link Types#TIMESTAMP_WITH_TIMEZONE}
+	 * to be set by the means of {@link java.sql.PreparedStatement#setObject(int, Object, int)}. If we We need to instead set
+	 * it be the mean
+	 *
+	 * @param type The type of value to be bound to a {@link java.sql.PreparedStatement}.
+	 * @param dialect represents the dialect, in the context of which the int sql code must be derived
+	 * @return the int code of sql type in regards to custom dialect mappings
+	 *
+	 * @see Types
+	 */
+	public static int sqlTypeFor(Class type, Dialect dialect) {
+
+		Assert.notNull(type, "Type must not be null.");
+
+		return Optional
+				.ofNullable(dialect.getCustomSqlCodesMappings().get(type))
+				.orElse(commonSqlTypeMappings.keySet().stream()
+						.filter(k -> k.isAssignableFrom(type))
+						.findFirst()
+						.map(commonSqlTypeMappings::get)
+						.orElse(JdbcUtils.TYPE_UNKNOWN)
+				);
+	}
+
 	/**
 	 * Converts a {@link JDBCType} to an {@code int} value as defined in {@link Types}.
 	 *
@@ -124,9 +160,33 @@ public static JDBCType jdbcTypeFor(int sqlType) {
 	 *
 	 * @param type The type of value to be bound to a {@link java.sql.PreparedStatement}.
 	 * @return a matching {@link JDBCType} instance or {@literal null}.
+	 * @deprecated because this method will make lookup only in common sql types map and therefore
+	 * 			   will not take into account dialect specific sql types codes mappings. When possible,
+	 * 			   please, use {@link #jdbcTypeFor(Class, Dialect)}
 	 */
 	@Nullable
+	@Deprecated
 	public static JDBCType jdbcTypeFor(Class type) {
 		return jdbcTypeFor(sqlTypeFor(type));
 	}
+
+
+	/**
+	 * Returns the {@link JDBCType} suitable for passing a value of the provided type to a
+	 * {@link java.sql.PreparedStatement} giving regards to passed dialect specific sql codes
+	 * mappings
+	 *
+	 * The main motivation for this method is, in general, the fact that we cannot assign the same int code for the same java class for
+	 * each dialect. For example, currently, there MySQL Driver cannot handle {@link Types#TIMESTAMP_WITH_TIMEZONE}
+	 * to be set by the means of {@link java.sql.PreparedStatement#setObject(int, Object, int)}. If we We need to instead set
+	 * it be the mean
+	 *
+	 * @param type The type of value to be bound to a {@link java.sql.PreparedStatement}.
+	 * @param dialect represents the dialect, in the context of which the int sql code must be derived
+	 * @return a matching {@link JDBCType} instance or {@literal null}.
+	 */
+	@Nullable
+	public static JDBCType jdbcTypeFor(Class type, Dialect dialect) {
+		return jdbcTypeFor(sqlTypeFor(type, dialect));
+	}
 }
diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/BasicJdbcConverterUnitTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/BasicJdbcConverterUnitTests.java
index 339889b721..e16a7d7719 100644
--- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/BasicJdbcConverterUnitTests.java
+++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/BasicJdbcConverterUnitTests.java
@@ -74,7 +74,7 @@ public void testTargetTypesForPropertyType() {
 		checkTargetType(softly, entity, "localDateTime", LocalDateTime.class);
 		checkTargetType(softly, entity, "localDate", Timestamp.class);
 		checkTargetType(softly, entity, "localTime", Timestamp.class);
-		checkTargetType(softly, entity, "zonedDateTime", ZonedDateTime.class);
+		checkTargetType(softly, entity, "zonedDateTime", OffsetDateTime.class);
 		checkTargetType(softly, entity, "offsetDateTime", OffsetDateTime.class);
 		checkTargetType(softly, entity, "instant", Timestamp.class);
 		checkTargetType(softly, entity, "date", Date.class);
diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/DefaultDataAccessStrategyUnitTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/DefaultDataAccessStrategyUnitTests.java
index 5098823c29..fc11792bc0 100644
--- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/DefaultDataAccessStrategyUnitTests.java
+++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/DefaultDataAccessStrategyUnitTests.java
@@ -45,6 +45,7 @@
 import org.springframework.data.relational.core.mapping.RelationalMappingContext;
 import org.springframework.data.relational.core.mapping.RelationalPersistentProperty;
 import org.springframework.data.relational.core.sql.SqlIdentifier;
+import org.springframework.jdbc.core.ConnectionCallback;
 import org.springframework.jdbc.core.JdbcOperations;
 import org.springframework.jdbc.core.RowMapper;
 import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations;
@@ -84,6 +85,11 @@ public void before() {
 
 		converter = new BasicJdbcConverter(context, relationResolver, new JdbcCustomConversions(),
 				new DefaultJdbcTypeFactory(jdbcOperations), dialect.getIdentifierProcessing());
+
+		when(jdbcOperations.execute(any(ConnectionCallback.class))).thenReturn(dialect);
+
+		when(namedJdbcOperations.getJdbcOperations()).thenReturn(jdbcOperations);
+
 		accessStrategy = new DefaultDataAccessStrategy( //
 				new SqlGeneratorSource(context, converter, dialect), //
 				context, //
diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryCustomConversionIntegrationTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryCustomConversionIntegrationTests.java
index 308f9ccce5..3767b28dd1 100644
--- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryCustomConversionIntegrationTests.java
+++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryCustomConversionIntegrationTests.java
@@ -16,7 +16,6 @@
 package org.springframework.data.jdbc.repository;
 
 import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.condition.EnabledIfSystemProperty;
 import org.junit.jupiter.api.extension.ExtendWith;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.context.annotation.Bean;
@@ -28,10 +27,12 @@
 import org.springframework.data.convert.WritingConverter;
 import org.springframework.data.jdbc.core.convert.JdbcCustomConversions;
 import org.springframework.data.jdbc.core.convert.JdbcValue;
+import org.springframework.data.jdbc.core.dialect.H2TimestampWithTimeZoneToOffsetDateTimeConverter;
 import org.springframework.data.jdbc.core.dialect.H2TimestampWithTimeZoneToZonedDateTimeConverter;
 import org.springframework.data.jdbc.repository.support.JdbcRepositoryFactory;
 import org.springframework.data.jdbc.testing.AssumeFeatureTestExecutionListener;
 import org.springframework.data.jdbc.testing.TestConfiguration;
+import org.springframework.data.relational.core.mapping.Table;
 import org.springframework.data.repository.CrudRepository;
 import org.springframework.test.context.ContextConfiguration;
 import org.springframework.test.context.TestExecutionListeners;
@@ -41,6 +42,7 @@
 import java.math.BigDecimal;
 import java.sql.JDBCType;
 import java.sql.Timestamp;
+import java.time.OffsetDateTime;
 import java.time.ZoneOffset;
 import java.time.ZonedDateTime;
 import java.util.Date;
@@ -85,6 +87,11 @@ EntityWithZonedDateTimeRepository entityWithZonedDateTimeRepository() {
 			return factory.getRepository(EntityWithZonedDateTimeRepository.class);
 		}
 
+		@Bean
+		EntityWithOffsetDateTimeRepository entityWithOffsetDateTimeRepository() {
+			return factory.getRepository(EntityWithOffsetDateTimeRepository.class);
+		}
+
 		@Bean
 		JdbcCustomConversions jdbcCustomConversions() {
 			return new JdbcCustomConversions(
@@ -93,9 +100,10 @@ JdbcCustomConversions jdbcCustomConversions() {
 							BigDecimalToString.INSTANCE,
 							CustomIdReadingConverter.INSTANCE,
 							CustomIdWritingConverter.INSTANCE,
-							ZonedDateTimeToTimestampWritingConverter.INSTANCE,
-							ZonedDateTimeToTimestampReadingConverter.INSTANCE,
-							H2TimestampWithTimeZoneToZonedDateTimeConverter.INSTANCE
+							TimestampToOffsetDateTimeConverter.INSTANCE,
+							TimestampToZonedDateTimeConverter.INSTANCE,
+							H2TimestampWithTimeZoneToZonedDateTimeConverter.INSTANCE,
+							H2TimestampWithTimeZoneToOffsetDateTimeConverter.INSTANCE
 					)
 			);
 		}
@@ -105,6 +113,8 @@ JdbcCustomConversions jdbcCustomConversions() {
 
 	@Autowired EntityWithZonedDateTimeRepository entityWithZonedDateTimeRepository;
 
+	@Autowired EntityWithOffsetDateTimeRepository entityWithOffsetDateTimeRepository;
+
 	/**
 	 * In PostgreSQL this fails if a simple converter like the following is used.
 	 *
@@ -178,26 +188,46 @@ public void testZonedDateTimeToTimestampConversion() {
 		final Optional foundEntity = entityWithZonedDateTimeRepository.findById(persistedEntity.id);
 
 		assertThat(foundEntity).isPresent();
-		assertThat(persistedEntity.createdAt).isEqualTo(foundEntity.get().createdAt);
+		assertThat(persistedEntity.createdAt.toEpochSecond()).isEqualTo(foundEntity.get().createdAt.toEpochSecond());
+	}
+
+	/**
+	 * DATAJDBC-1089
+	 */
+	@Test
+	public void testOffsetDateTimeToTimestampConversion() {
+		EntityWithOffsetDateTime entity = new EntityWithOffsetDateTime();
+		entity.createdAt = OffsetDateTime.now(ZoneOffset.ofHours(3));
+
+		final EntityWithOffsetDateTime persistedEntity = entityWithOffsetDateTimeRepository.save(entity);
+		final Optional foundEntity = entityWithOffsetDateTimeRepository.findById(persistedEntity.id);
+
+		assertThat(foundEntity).isPresent();
+		assertThat(persistedEntity.createdAt.toEpochSecond()).isEqualTo(foundEntity.get().createdAt.toEpochSecond());
 	}
 
 	interface EntityWithBooleanRepository extends CrudRepository {}
 
 	interface EntityWithZonedDateTimeRepository extends CrudRepository {};
 
-	private static class EntityWithStringyBigDecimal {
+	interface EntityWithOffsetDateTimeRepository extends CrudRepository {};
 
+	private static class EntityWithStringyBigDecimal {
 		@Id CustomId id;
 		String stringyNumber;
 		OtherEntity reference;
 	}
 
 	private static class EntityWithZonedDateTime {
-
 		@Id private Long id;
 		private ZonedDateTime createdAt;
 	}
 
+	private static class EntityWithOffsetDateTime {
+		@Id private Long id;
+		private OffsetDateTime createdAt;
+	}
+
 	private static class CustomId {
 
 		private final Long value;
@@ -260,28 +290,25 @@ public CustomId convert(Number source) {
 		}
 	}
 
-	@WritingConverter
-	enum ZonedDateTimeToTimestampWritingConverter implements Converter {
+	@ReadingConverter
+	public enum TimestampToZonedDateTimeConverter implements Converter {
 
 		INSTANCE;
 
 		@Override
-		public Timestamp convert(ZonedDateTime source) {
-			return Timestamp.from(source.toInstant());
+		public ZonedDateTime convert(Timestamp source) {
+			return ZonedDateTime.ofInstant(source.toInstant(), ZoneOffset.ofHours(3));
 		}
-
 	}
 
 	@ReadingConverter
-	enum ZonedDateTimeToTimestampReadingConverter implements Converter {
+	public enum TimestampToOffsetDateTimeConverter implements Converter {
 
 		INSTANCE;
 
 		@Override
-		public ZonedDateTime convert(Timestamp source) {
-			return ZonedDateTime.ofInstant(source.toInstant(), ZoneOffset.ofHours(0)); // Because source.toInstant() already represents appropraite value
+		public OffsetDateTime convert(Timestamp source) {
+			return OffsetDateTime.ofInstant(source.toInstant(), ZoneOffset.ofHours(3));
 		}
-
 	}
-	
-}
+}
\ No newline at end of file
diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/SimpleJdbcRepositoryEventsUnitTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/SimpleJdbcRepositoryEventsUnitTests.java
index 31d51e8663..2fd71cbfae 100644
--- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/SimpleJdbcRepositoryEventsUnitTests.java
+++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/SimpleJdbcRepositoryEventsUnitTests.java
@@ -27,6 +27,8 @@
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
+import java.util.Optional;
+import java.util.function.BiFunction;
 
 import org.assertj.core.groups.Tuple;
 import org.junit.jupiter.api.BeforeEach;
@@ -45,14 +47,18 @@
 import org.springframework.data.jdbc.core.convert.JdbcCustomConversions;
 import org.springframework.data.jdbc.core.convert.SqlGeneratorSource;
 import org.springframework.data.jdbc.core.mapping.JdbcMappingContext;
+import org.springframework.data.jdbc.repository.config.DialectResolver;
+import org.springframework.data.jdbc.repository.config.DialectResolver.DefaultDialectProvider;
 import org.springframework.data.jdbc.repository.support.JdbcRepositoryFactory;
 import org.springframework.data.jdbc.repository.support.SimpleJdbcRepository;
 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.PostgresDialect;
 import org.springframework.data.relational.core.mapping.RelationalMappingContext;
 import org.springframework.data.relational.core.mapping.event.*;
 import org.springframework.data.repository.PagingAndSortingRepository;
+import org.springframework.jdbc.core.ConnectionCallback;
 import org.springframework.jdbc.core.JdbcOperations;
 import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations;
 import org.springframework.jdbc.core.namedparam.SqlParameterSource;
@@ -80,11 +86,17 @@ public class SimpleJdbcRepositoryEventsUnitTests {
 	public void before() {
 
 		RelationalMappingContext context = new JdbcMappingContext();
+
 		NamedParameterJdbcOperations operations = createIdGeneratingOperations();
+
+		JdbcOperations mockJdbcOperations = operations.getJdbcOperations();
+
+		when(mockJdbcOperations.execute(any(ConnectionCallback.class))).thenReturn(PostgresDialect.INSTANCE);
+
 		DelegatingDataAccessStrategy delegatingDataAccessStrategy = new DelegatingDataAccessStrategy();
 		Dialect dialect = HsqlDbDialect.INSTANCE;
 		JdbcConverter converter = new BasicJdbcConverter(context, delegatingDataAccessStrategy, new JdbcCustomConversions(),
-				new DefaultJdbcTypeFactory(operations.getJdbcOperations()), dialect.getIdentifierProcessing());
+				new DefaultJdbcTypeFactory(mockJdbcOperations), dialect.getIdentifierProcessing());
 		SqlGeneratorSource generatorSource = new SqlGeneratorSource(context, converter, dialect);
 
 		this.dataAccessStrategy = spy(new DefaultDataAccessStrategy(generatorSource, context, converter, operations));
@@ -290,10 +302,12 @@ private static NamedParameterJdbcOperations createIdGeneratingOperations() {
 			return 1;
 		};
 
+		JdbcOperations mockJdbcOperations = mock(JdbcOperations.class);
+
 		NamedParameterJdbcOperations operations = mock(NamedParameterJdbcOperations.class);
 		when(operations.update(anyString(), any(SqlParameterSource.class), any(KeyHolder.class)))
 				.thenAnswer(setIdInKeyHolder);
-		when(operations.getJdbcOperations()).thenReturn(mock(JdbcOperations.class));
+		when(operations.getJdbcOperations()).thenReturn(mockJdbcOperations);
 		return operations;
 	}
 
diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/testing/MySqlDataSourceConfiguration.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/testing/MySqlDataSourceConfiguration.java
index 8da00a7744..75a20351be 100644
--- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/testing/MySqlDataSourceConfiguration.java
+++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/testing/MySqlDataSourceConfiguration.java
@@ -73,8 +73,7 @@ protected DataSource createDataSource() {
 	public void afterPropertiesSet() throws Exception {
 
 		try (Connection connection = createDataSource().getConnection()) {
-			ScriptUtils.executeSqlScript(connection,
-					new ByteArrayResource("DROP DATABASE test;CREATE DATABASE test;".getBytes()));
+			ScriptUtils.executeSqlScript(connection, new ByteArrayResource("DROP DATABASE IF EXISTS test; CREATE DATABASE IF NOT EXISTS test;".getBytes()));
 		}
 	}
 
diff --git a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryCustomConversionIntegrationTests-db2.sql b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryCustomConversionIntegrationTests-db2.sql
index 80cc288e98..6885aa63a0 100644
--- a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryCustomConversionIntegrationTests-db2.sql
+++ b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryCustomConversionIntegrationTests-db2.sql
@@ -1,7 +1,9 @@
 DROP TABLE ENTITY_WITH_STRINGY_BIG_DECIMAL;
 DROP TABLE OTHER_ENTITY;
 DROP TABLE ENTITY_WITH_ZONED_DATE_TIME;
+DROP TABLE ENTITY_WITH_OFFSET_DATE_TIME;
 
 CREATE TABLE ENTITY_WITH_STRINGY_BIG_DECIMAL ( id BIGINT GENERATED BY DEFAULT AS IDENTITY(START WITH 1) PRIMARY KEY, Stringy_number DECIMAL(20,10));
 CREATE TABLE OTHER_ENTITY ( ID  BIGINT GENERATED BY DEFAULT AS IDENTITY(START WITH 1) PRIMARY KEY, CREATED DATE, ENTITY_WITH_STRINGY_BIG_DECIMAL INTEGER);
-CREATE TABLE ENTITY_WITH_ZONED_DATE_TIME (ID BIGINT GENERATED BY DEFAULT AS IDENTITY (START WITH 1) PRIMARY KEY, CREATED_AT TIMESTAMP);
\ No newline at end of file
+CREATE TABLE ENTITY_WITH_ZONED_DATE_TIME (ID BIGINT GENERATED BY DEFAULT AS IDENTITY (START WITH 1) PRIMARY KEY, CREATED_AT TIMESTAMP);
+CREATE TABLE ENTITY_WITH_OFFSET_DATE_TIME (ID BIGINT GENERATED BY DEFAULT AS IDENTITY (START WITH 1) PRIMARY KEY, CREATED_AT TIMESTAMP);
\ No newline at end of file
diff --git a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryCustomConversionIntegrationTests-h2.sql b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryCustomConversionIntegrationTests-h2.sql
index f7a744422f..186ac82e69 100644
--- a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryCustomConversionIntegrationTests-h2.sql
+++ b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryCustomConversionIntegrationTests-h2.sql
@@ -1,3 +1,4 @@
 CREATE TABLE ENTITY_WITH_STRINGY_BIG_DECIMAL ( id IDENTITY PRIMARY KEY, Stringy_number DECIMAL(20,10));
 CREATE TABLE OTHER_ENTITY ( ID IDENTITY PRIMARY KEY, CREATED DATE, ENTITY_WITH_STRINGY_BIG_DECIMAL INTEGER);
-CREATE TABLE ENTITY_WITH_ZONED_DATE_TIME (ID BIGINT AUTO_INCREMENT PRIMARY KEY, CREATED_AT TIMESTAMP WITH TIME ZONE);
\ No newline at end of file
+CREATE TABLE ENTITY_WITH_ZONED_DATE_TIME (ID BIGINT AUTO_INCREMENT PRIMARY KEY, CREATED_AT TIMESTAMP WITH TIME ZONE);
+CREATE TABLE ENTITY_WITH_OFFSET_DATE_TIME (ID BIGINT AUTO_INCREMENT PRIMARY KEY, CREATED_AT TIMESTAMP WITH TIME ZONE);
\ No newline at end of file
diff --git a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryCustomConversionIntegrationTests-hsql.sql b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryCustomConversionIntegrationTests-hsql.sql
index 6117a8bd15..55473aa1bb 100644
--- a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryCustomConversionIntegrationTests-hsql.sql
+++ b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryCustomConversionIntegrationTests-hsql.sql
@@ -1,3 +1,4 @@
 CREATE TABLE ENTITY_WITH_STRINGY_BIG_DECIMAL ( id IDENTITY PRIMARY KEY, Stringy_number DECIMAL(20,10));
 CREATE TABLE OTHER_ENTITY ( ID IDENTITY PRIMARY KEY, CREATED DATE, ENTITY_WITH_STRINGY_BIG_DECIMAL INTEGER);
-CREATE TABLE ENTITY_WITH_ZONED_DATE_TIME (id IDENTITY PRIMARY KEY, CREATED_AT TIMESTAMP WITH TIME ZONE);
\ No newline at end of file
+CREATE TABLE ENTITY_WITH_ZONED_DATE_TIME (id IDENTITY PRIMARY KEY, CREATED_AT TIMESTAMP WITH TIME ZONE);
+CREATE TABLE ENTITY_WITH_OFFSET_DATE_TIME (id IDENTITY PRIMARY KEY, CREATED_AT TIMESTAMP WITH TIME ZONE);
\ No newline at end of file
diff --git a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryCustomConversionIntegrationTests-mariadb.sql b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryCustomConversionIntegrationTests-mariadb.sql
index 709330fa81..65cb053003 100644
--- a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryCustomConversionIntegrationTests-mariadb.sql
+++ b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryCustomConversionIntegrationTests-mariadb.sql
@@ -1,3 +1,4 @@
 CREATE TABLE ENTITY_WITH_STRINGY_BIG_DECIMAL ( id BIGINT AUTO_INCREMENT PRIMARY KEY, Stringy_number DECIMAL(20,10));
 CREATE TABLE OTHER_ENTITY ( ID BIGINT AUTO_INCREMENT PRIMARY KEY, CREATED DATE, ENTITY_WITH_STRINGY_BIG_DECIMAL INTEGER);
-CREATE TABLE ENTITY_WITH_ZONED_DATE_TIME (ID BIGINT AUTO_INCREMENT PRIMARY KEY, CREATED_AT TIMESTAMP(3));
\ No newline at end of file
+CREATE TABLE ENTITY_WITH_ZONED_DATE_TIME (ID BIGINT AUTO_INCREMENT PRIMARY KEY, CREATED_AT TIMESTAMP(3));
+CREATE TABLE ENTITY_WITH_OFFSET_DATE_TIME (ID BIGINT AUTO_INCREMENT PRIMARY KEY, CREATED_AT TIMESTAMP(3));
\ No newline at end of file
diff --git a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryCustomConversionIntegrationTests-mssql.sql b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryCustomConversionIntegrationTests-mssql.sql
index 1c366e6947..608e41495d 100644
--- a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryCustomConversionIntegrationTests-mssql.sql
+++ b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryCustomConversionIntegrationTests-mssql.sql
@@ -1,7 +1,9 @@
 DROP TABLE OTHER_ENTITY;
 DROP TABLE ENTITY_WITH_STRINGY_BIG_DECIMAL;
 DROP TABLE ENTITY_WITH_ZONED_DATE_TIME;
+DROP TABLE ENTITY_WITH_OFFSET_DATE_TIME;
 
 CREATE TABLE ENTITY_WITH_STRINGY_BIG_DECIMAL ( id BIGINT IDENTITY PRIMARY KEY, Stringy_number DECIMAL(20,10));
 CREATE TABLE OTHER_ENTITY ( ID BIGINT IDENTITY PRIMARY KEY, CREATED DATE, ENTITY_WITH_STRINGY_BIG_DECIMAL INTEGER);
-CREATE TABLE ENTITY_WITH_ZONED_DATE_TIME (id BIGINT IDENTITY PRIMARY KEY, CREATED_AT DATETIMEOFFSET);
\ No newline at end of file
+CREATE TABLE ENTITY_WITH_ZONED_DATE_TIME (id BIGINT IDENTITY PRIMARY KEY, CREATED_AT DATETIMEOFFSET);
+CREATE TABLE ENTITY_WITH_OFFSET_DATE_TIME (id BIGINT IDENTITY PRIMARY KEY, CREATED_AT DATETIMEOFFSET);
\ No newline at end of file
diff --git a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryCustomConversionIntegrationTests-mysql.sql b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryCustomConversionIntegrationTests-mysql.sql
index 89d55cab07..adc4d34488 100644
--- a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryCustomConversionIntegrationTests-mysql.sql
+++ b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryCustomConversionIntegrationTests-mysql.sql
@@ -1,3 +1,4 @@
 CREATE TABLE ENTITY_WITH_STRINGY_BIG_DECIMAL ( ID BIGINT AUTO_INCREMENT PRIMARY KEY, Stringy_number DECIMAL(20,10));
 CREATE TABLE OTHER_ENTITY ( ID BIGINT AUTO_INCREMENT PRIMARY KEY, CREATED DATE, ENTITY_WITH_STRINGY_BIG_DECIMAL INTEGER);
-CREATE TABLE ENTITY_WITH_ZONED_DATE_TIME (id BIGINT AUTO_INCREMENT PRIMARY KEY, CREATED_AT TIMESTAMP(3));
\ No newline at end of file
+CREATE TABLE ENTITY_WITH_ZONED_DATE_TIME (id BIGINT AUTO_INCREMENT PRIMARY KEY, CREATED_AT TIMESTAMP(3));
+CREATE TABLE ENTITY_WITH_OFFSET_DATE_TIME (id BIGINT AUTO_INCREMENT PRIMARY KEY, CREATED_AT TIMESTAMP(3));
\ No newline at end of file
diff --git a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryCustomConversionIntegrationTests-oracle.sql b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryCustomConversionIntegrationTests-oracle.sql
index 7d99636876..3c0f93e5a3 100644
--- a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryCustomConversionIntegrationTests-oracle.sql
+++ b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryCustomConversionIntegrationTests-oracle.sql
@@ -1,6 +1,7 @@
 DROP TABLE ENTITY_WITH_STRINGY_BIG_DECIMAL CASCADE CONSTRAINTS PURGE;
 DROP TABLE OTHER_ENTITY CASCADE CONSTRAINTS PURGE;
 DROP TABLE ENTITY_WITH_ZONED_DATE_TIME CASCADE CONSTRAINTS PURGE;
+DROP TABLE ENTITY_WITH_OFFSET_DATE_TIME CASCADE CONSTRAINTS PURGE;
 
 CREATE TABLE ENTITY_WITH_STRINGY_BIG_DECIMAL (
     ID NUMBER GENERATED BY DEFAULT ON NULL AS IDENTITY PRIMARY KEY,
@@ -16,4 +17,9 @@ CREATE TABLE OTHER_ENTITY (
 CREATE TABLE ENTITY_WITH_ZONED_DATE_TIME (
     id NUMBER GENERATED BY DEFAULT ON NULL AS IDENTITY PRIMARY KEY,
     CREATED_AT TIMESTAMP WITH TIME ZONE
+);
+
+CREATE TABLE ENTITY_WITH_OFFSET_DATE_TIME (
+    id NUMBER GENERATED BY DEFAULT ON NULL AS IDENTITY PRIMARY KEY,
+    CREATED_AT TIMESTAMP WITH TIME ZONE
 );
\ No newline at end of file
diff --git a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryCustomConversionIntegrationTests-postgres.sql b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryCustomConversionIntegrationTests-postgres.sql
index d681e81761..b9f938d27e 100644
--- a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryCustomConversionIntegrationTests-postgres.sql
+++ b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryCustomConversionIntegrationTests-postgres.sql
@@ -1,3 +1,4 @@
 CREATE TABLE ENTITY_WITH_STRINGY_BIG_DECIMAL ( id SERIAL PRIMARY KEY, Stringy_number DECIMAL(20,10));
 CREATE TABLE OTHER_ENTITY ( ID SERIAL PRIMARY KEY, CREATED DATE, ENTITY_WITH_STRINGY_BIG_DECIMAL INTEGER);
-CREATE TABLE ENTITY_WITH_ZONED_DATE_TIME (id SERIAL PRIMARY KEY, CREATED_AT TIMESTAMPTZ);
\ No newline at end of file
+CREATE TABLE ENTITY_WITH_ZONED_DATE_TIME (id SERIAL PRIMARY KEY, CREATED_AT TIMESTAMPTZ);
+CREATE TABLE ENTITY_WITH_OFFSET_DATE_TIME (id SERIAL PRIMARY KEY, CREATED_AT TIMESTAMPTZ);
\ No newline at end of file
diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/Dialect.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/Dialect.java
index 5febb8c52f..00a1b39d6c 100644
--- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/Dialect.java
+++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/Dialect.java
@@ -17,11 +17,15 @@
 
 import java.util.Collection;
 import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
 import java.util.Set;
 
+import org.jetbrains.annotations.NotNull;
 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;
+import org.springframework.lang.NonNull;
 
 /**
  * Represents a dialect that is implemented by a particular database. Please note that not all features are supported by
@@ -120,4 +124,11 @@ default Set> simpleTypes() {
 	default InsertRenderContext getInsertRenderContext() {
 		return InsertRenderContexts.DEFAULT;
 	}
+
+	/**
+	 * @return the map of custom mappings from java classes to sql codes (integer sql codes present in {@link java.sql.Types})
+	 * @since 3.0
+	 */
+	@NonNull
+	default Map, Integer> getCustomSqlCodesMappings() { return new HashMap<>(); }
 }
diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/MySqlDialect.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/MySqlDialect.java
index 6032582186..944a74195d 100644
--- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/MySqlDialect.java
+++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/MySqlDialect.java
@@ -15,13 +15,18 @@
  */
 package org.springframework.data.relational.core.dialect;
 
+import java.sql.Types;
+import java.time.OffsetDateTime;
+import java.time.ZonedDateTime;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.Map;
 
 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.lang.NonNull;
 import org.springframework.util.Assert;
 
 /**
@@ -169,4 +174,17 @@ public IdentifierProcessing getIdentifierProcessing() {
 	public Collection getConverters() {
 		return Collections.singletonList(TimestampAtUtcToOffsetDateTimeConverter.INSTANCE);
 	}
+
+	/*
+	 * (non-Javadoc)
+	 * @see org.springframework.data.relational.core.dialect.Dialect#getCustomSqlCodesMappings()
+	 */
+	@NonNull
+    @Override
+    public Map, Integer> getCustomSqlCodesMappings() {
+		Map, Integer> customSqlCodesMappings = super.getCustomSqlCodesMappings();
+		customSqlCodesMappings.put(OffsetDateTime.class, Types.TIMESTAMP);
+		customSqlCodesMappings.put(ZonedDateTime.class, Types.TIMESTAMP);
+		return customSqlCodesMappings;
+    }
 }

From b9419212ed8b80afad649f47aee87378a059147d Mon Sep 17 00:00:00 2001
From: Mikhail2048 
Date: Fri, 7 Jan 2022 11:49:09 +0300
Subject: [PATCH 4/4] Add built-in support for ZonedDateTime and OffsetDateTime
 in spring-data-jdbc

---
 .../jdbc/core/convert/BasicJdbcConverter.java |  10 +-
 .../jdbc/core/convert/JdbcColumnTypes.java    |  25 ++++-
 .../Jsr310TimestampBasedConverters.java       |  27 +++++
 .../jdbc/core/dialect/JdbcDb2Dialect.java     |  59 +++++++++-
 .../core/dialect/JdbcPostgresDialect.java     |  31 ++++++
 .../core/dialect/JdbcSqlServerDialect.java    |  35 +++++-
 .../query/StringBasedJdbcQuery.java           |  14 +--
 .../convert/BasicJdbcConverterUnitTests.java  |   2 +-
 ...itoryCustomConversionIntegrationTests.java | 105 +++---------------
 .../JdbcRepositoryIntegrationTests.java       |  62 +++++++++++
 .../MariaDBDataSourceConfiguration.java       |   4 +-
 ...ryCustomConversionIntegrationTests-db2.sql |   6 +-
 ...oryCustomConversionIntegrationTests-h2.sql |   4 +-
 ...yCustomConversionIntegrationTests-hsql.sql |   4 +-
 ...stomConversionIntegrationTests-mariadb.sql |   4 +-
 ...CustomConversionIntegrationTests-mssql.sql |   6 +-
 ...CustomConversionIntegrationTests-mysql.sql |   4 +-
 ...ustomConversionIntegrationTests-oracle.sql |  12 --
 ...tomConversionIntegrationTests-postgres.sql |   4 +-
 .../JdbcRepositoryIntegrationTests-db2.sql    |   5 +
 .../JdbcRepositoryIntegrationTests-h2.sql     |   3 +
 .../JdbcRepositoryIntegrationTests-hsql.sql   |   2 +
 ...JdbcRepositoryIntegrationTests-mariadb.sql |   3 +
 .../JdbcRepositoryIntegrationTests-mssql.sql  |   6 +
 .../JdbcRepositoryIntegrationTests-mysql.sql  |   3 +
 .../JdbcRepositoryIntegrationTests-oracle.sql |  12 ++
 ...dbcRepositoryIntegrationTests-postgres.sql |   5 +
 .../data/relational/core/dialect/Dialect.java |  15 ++-
 .../core/dialect/HsqlDbDialect.java           |  31 ++++++
 29 files changed, 358 insertions(+), 145 deletions(-)

diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/BasicJdbcConverter.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/BasicJdbcConverter.java
index 5f041abdca..df1a10b27b 100644
--- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/BasicJdbcConverter.java
+++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/BasicJdbcConverter.java
@@ -207,7 +207,7 @@ private Class doGetColumnType(RelationalPersistentProperty property) {
 			}
 		}
 
-		Class componentColumnType = JdbcColumnTypes.INSTANCE.resolvePrimitiveType(property.getActualType());
+		Class componentColumnType = resolvePropertyPrimitiveType(property);
 
 		while (componentColumnType.isArray()) {
 			componentColumnType = componentColumnType.getComponentType();
@@ -220,6 +220,14 @@ private Class doGetColumnType(RelationalPersistentProperty property) {
 		return componentColumnType;
 	}
 
+	private Class resolvePropertyPrimitiveType(RelationalPersistentProperty property) {
+		if (typeFactory instanceof DefaultJdbcTypeFactory) {
+			return JdbcColumnTypes.INSTANCE.resolvePrimitiveType(property.getActualType(), DialectResolver.getDialect(((DefaultJdbcTypeFactory) typeFactory).getOperations()));
+		} else {
+			return JdbcColumnTypes.INSTANCE.resolvePrimitiveType(property.getActualType());
+		}
+	}
+
 	/*
 	 * (non-Javadoc)
 	 * @see org.springframework.data.relational.core.conversion.RelationalConverter#readValue(java.lang.Object, org.springframework.data.util.TypeInformation)
diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/JdbcColumnTypes.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/JdbcColumnTypes.java
index e4a7eed0c4..c849f13cde 100644
--- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/JdbcColumnTypes.java
+++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/JdbcColumnTypes.java
@@ -23,7 +23,9 @@
 import java.util.Date;
 import java.util.LinkedHashMap;
 import java.util.Map;
+import java.util.Optional;
 
+import org.springframework.data.relational.core.dialect.Dialect;
 import org.springframework.util.ClassUtils;
 
 /**
@@ -38,6 +40,12 @@ public enum JdbcColumnTypes {
 
 	INSTANCE {
 
+		/**
+		 * @deprecated because this method will resolve the target column type regardless of the
+		 * 			   current client {@link Dialect} - it will search only in common types mappings.
+		 * 			   When possible, please, use {@link #resolvePrimitiveType(Class, Dialect)}
+		 */
+		@Deprecated
 		@SuppressWarnings({ "unchecked", "rawtypes" })
 		public Class resolvePrimitiveType(Class type) {
 
@@ -47,6 +55,13 @@ public Class resolvePrimitiveType(Class type) {
 					.findFirst() //
 					.orElseGet(() -> (Class) ClassUtils.resolvePrimitiveIfNecessary(type));
 		}
+
+		@SuppressWarnings({ "unchecked", "rawtypes", "deprecation" })
+		public Class resolvePrimitiveType(Class type, Dialect dialect) {
+			return Optional
+					.ofNullable(dialect.getCustomJdbcColumnsMappings().get(type))
+					.orElse((Class) this.resolvePrimitiveType(type));
+		}
 	};
 
 	private static final Map, Class> javaToDbType = new LinkedHashMap<>();
@@ -54,11 +69,19 @@ public Class resolvePrimitiveType(Class type) {
 	static {
 
 		javaToDbType.put(Enum.class, String.class);
-		javaToDbType.put(ZonedDateTime.class, OffsetDateTime.class);
+		javaToDbType.put(ZonedDateTime.class, ZonedDateTime.class);
 		javaToDbType.put(OffsetDateTime.class, OffsetDateTime.class);
 		javaToDbType.put(LocalDateTime.class, LocalDateTime.class);
 		javaToDbType.put(Temporal.class, Timestamp.class);
 	}
 
+	/**
+	 * @deprecated because this method will resolve the target column type regardless of the
+	 * 			   current client {@link Dialect} - it will search only in common types mappings.
+	 * 			   When possible, please, use {@link #resolvePrimitiveType(Class, Dialect)}
+	 */
+	@Deprecated
 	public abstract Class resolvePrimitiveType(Class type);
+
+	public abstract Class resolvePrimitiveType(Class type, Dialect dialect);
 }
diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/Jsr310TimestampBasedConverters.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/Jsr310TimestampBasedConverters.java
index 91bec34183..0ea81bb526 100644
--- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/Jsr310TimestampBasedConverters.java
+++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/Jsr310TimestampBasedConverters.java
@@ -24,8 +24,10 @@
 import java.time.LocalDate;
 import java.time.LocalDateTime;
 import java.time.LocalTime;
+import java.time.OffsetDateTime;
 import java.time.Period;
 import java.time.ZoneId;
+import java.time.ZoneOffset;
 import java.time.ZonedDateTime;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -44,6 +46,7 @@
  *
  * @see org.springframework.data.convert.Jsr310Converters
  * @author Jens Schauder
+ * @author Mikhail Polivakha
  * @since 2.2
  */
 public abstract class Jsr310TimestampBasedConverters {
@@ -67,6 +70,8 @@ public abstract class Jsr310TimestampBasedConverters {
 		converters.add(LocalTimeToTimestampConverter.INSTANCE);
 		converters.add(TimestampToInstantConverter.INSTANCE);
 		converters.add(InstantToTimestampConverter.INSTANCE);
+		converters.add(TimestampToZonedDateTimeConverter.INSTANCE);
+		converters.add(TimestampToOffsetDateTimeConverter.INSTANCE);
 
 		return converters;
 	}
@@ -166,4 +171,26 @@ public Timestamp convert(Instant source) {
 			return Timestamp.from(source);
 		}
 	}
+
+	@ReadingConverter
+	public enum TimestampToZonedDateTimeConverter implements Converter {
+
+		INSTANCE;
+
+		@Override
+		public ZonedDateTime convert(Timestamp source) {
+			return ZonedDateTime.ofInstant(source.toInstant(), ZoneId.of("UTC"));
+		}
+	}
+
+	@ReadingConverter
+	public enum TimestampToOffsetDateTimeConverter implements Converter {
+
+		INSTANCE;
+
+		@Override
+		public OffsetDateTime convert(Timestamp source) {
+			return OffsetDateTime.ofInstant(source.toInstant(), ZoneId.of("UTC"));
+		}
+	}
 }
diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/JdbcDb2Dialect.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/JdbcDb2Dialect.java
index 45a8c58eca..789cd572aa 100644
--- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/JdbcDb2Dialect.java
+++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/JdbcDb2Dialect.java
@@ -16,21 +16,26 @@
 package org.springframework.data.jdbc.core.dialect;
 
 import java.sql.Timestamp;
+import java.sql.Types;
 import java.time.OffsetDateTime;
+import java.time.ZonedDateTime;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
+import java.util.Map;
 
 import org.springframework.core.convert.converter.Converter;
 import org.springframework.data.convert.WritingConverter;
 import org.springframework.data.jdbc.core.convert.Jsr310TimestampBasedConverters;
 import org.springframework.data.relational.core.dialect.Db2Dialect;
+import org.springframework.lang.NonNull;
 
 /**
  * {@link Db2Dialect} that registers JDBC specific converters.
  *
  * @author Jens Schauder
  * @author Christoph Strobl
+ * @author Mikhail Polivakha
  * @since 2.3
  */
 public class JdbcDb2Dialect extends Db2Dialect {
@@ -39,16 +44,48 @@ public class JdbcDb2Dialect extends Db2Dialect {
 
 	protected JdbcDb2Dialect() {}
 
+	@NonNull
 	@Override
 	public Collection getConverters() {
-
 		List converters = new ArrayList<>(super.getConverters());
 		converters.add(OffsetDateTimeToTimestampConverter.INSTANCE);
+		converters.add(ZonedDateTimeToTimestampConverter.INSTANCE);
 		converters.add(Jsr310TimestampBasedConverters.LocalDateTimeToTimestampConverter.INSTANCE);
-
 		return converters;
 	}
 
+	/**
+	 * Unfortunately, DB2 jdbc driver does not support Js310 (new Java date/time API) classes. Therefore for this
+	 * dialect we need to convert these classes into {@link Timestamp}.
+	 *
+	 * @see OffsetDateTimeToTimestampConverter
+	 * @see ZonedDateTimeToTimestampConverter
+	 */
+	@NonNull
+    @Override
+    public Map, Class> getCustomJdbcColumnsMappings() {
+		final Map, Class> db2CustomJdbcColumnsMappings = super.getCustomJdbcColumnsMappings();
+		db2CustomJdbcColumnsMappings.put(ZonedDateTime.class, Timestamp.class);
+		db2CustomJdbcColumnsMappings.put(OffsetDateTime.class, Timestamp.class);
+		return db2CustomJdbcColumnsMappings;
+	}
+
+	/**
+	 * Unfortunately, DB2 jdbc driver does not support {@link Types#TIMESTAMP_WITH_TIMEZONE} as a type. Therefore for this
+	 * dialect we need to use {@link Types#TIMESTAMP} and pass {@link Timestamp} instead of Jsr310 classes
+	 *
+	 * @see OffsetDateTimeToTimestampConverter
+	 * @see ZonedDateTimeToTimestampConverter
+	 */
+	@NonNull
+	@Override
+	public Map, Integer> getCustomSqlCodesMappings() {
+		final Map, Integer> db2CustomSqlCodesMappings = super.getCustomSqlCodesMappings();
+		db2CustomSqlCodesMappings.put(ZonedDateTime.class, Types.TIMESTAMP);
+		db2CustomSqlCodesMappings.put(OffsetDateTime.class, Types.TIMESTAMP);
+		return db2CustomSqlCodesMappings;
+	}
+
 	/**
 	 * {@link WritingConverter} from {@link OffsetDateTime} to {@link Timestamp}. The conversion preserves the
 	 * {@link java.time.Instant} represented by {@link OffsetDateTime}
@@ -66,4 +103,22 @@ public Timestamp convert(OffsetDateTime source) {
 			return Timestamp.from(source.toInstant());
 		}
 	}
+
+	/**
+	 * {@link WritingConverter} from {@link ZonedDateTime} to {@link Timestamp}. The conversion preserves the
+	 * {@link java.time.Instant} represented by {@link ZonedDateTime}
+	 *
+	 * @author Mikhail Polivakha
+	 * @since 3.0
+	 */
+	@WritingConverter
+	enum ZonedDateTimeToTimestampConverter implements Converter {
+
+		INSTANCE;
+
+		@Override
+		public Timestamp convert(ZonedDateTime source) {
+			return Timestamp.from(source.toInstant());
+		}
+	}
 }
diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/JdbcPostgresDialect.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/JdbcPostgresDialect.java
index 6c7bc82945..ed1a39c454 100644
--- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/JdbcPostgresDialect.java
+++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/JdbcPostgresDialect.java
@@ -17,13 +17,21 @@
 
 import java.sql.JDBCType;
 import java.sql.SQLType;
+import java.time.OffsetDateTime;
+import java.time.ZonedDateTime;
+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.relational.core.dialect.PostgresDialect;
+import org.springframework.lang.NonNull;
 
 /**
  * JDBC specific Postgres Dialect.
  *
  * @author Jens Schauder
+ * @author Mikhail Polivakha
  * @since 2.3
  */
 public class JdbcPostgresDialect extends PostgresDialect implements JdbcDialect {
@@ -37,6 +45,14 @@ public JdbcArrayColumns getArraySupport() {
 		return ARRAY_COLUMNS;
 	}
 
+	@NonNull
+	@Override
+	public Collection getConverters() {
+		final Collection converters = new ArrayList<>(super.getConverters());
+		converters.add(ZonedDateTimeToOffsetDateTimeWritingConverter.INSTANCE);
+		return converters;
+	}
+
 	static class JdbcPostgresArrayColumns extends PostgresArrayColumns implements JdbcArrayColumns {
 
 		@Override
@@ -57,4 +73,19 @@ public String getArrayTypeName(SQLType jdbcType) {
 			return jdbcType.getName();
 		}
 	}
+
+	/**
+	 * Unfortunately, PostgresSQL jdbc driver can accept {@link OffsetDateTime} as
+	 * {@link java.sql.Types#TIMESTAMP_WITH_TIMEZONE}, but not {@link ZonedDateTime}
+	 */
+	@WritingConverter
+	enum ZonedDateTimeToOffsetDateTimeWritingConverter implements Converter {
+
+		INSTANCE;
+
+		@Override
+		public OffsetDateTime convert(ZonedDateTime source) {
+			return source.toOffsetDateTime();
+		}
+	}
 }
diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/JdbcSqlServerDialect.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/JdbcSqlServerDialect.java
index 9883c23a46..c4e4a72665 100644
--- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/JdbcSqlServerDialect.java
+++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/JdbcSqlServerDialect.java
@@ -18,30 +18,37 @@
 import microsoft.sql.DateTimeOffset;
 
 import java.time.OffsetDateTime;
+import java.time.ZonedDateTime;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
 
 import org.springframework.core.convert.converter.Converter;
 import org.springframework.data.convert.ReadingConverter;
+import org.springframework.data.convert.WritingConverter;
 import org.springframework.data.relational.core.dialect.SqlServerDialect;
+import org.springframework.lang.NonNull;
 
 /**
  * {@link SqlServerDialect} that registers JDBC specific converters.
  *
  * @author Jens Schauder
  * @author Christoph Strobl
+ * @author Mikhail Polivakha
+ *
  * @since 2.3
  */
 public class JdbcSqlServerDialect extends SqlServerDialect {
 
 	public static JdbcSqlServerDialect INSTANCE = new JdbcSqlServerDialect();
 
+	@NonNull
 	@Override
 	public Collection getConverters() {
-
 		List converters = new ArrayList<>(super.getConverters());
 		converters.add(DateTimeOffsetToOffsetDateTimeConverter.INSTANCE);
+		converters.add(ZonedDateTimeToOffsetDateTimeConverter.INSTANCE);
+		converters.add(DateTimeOffsetToZonedDateTimeConverter.INSTANCE);
 		return converters;
 	}
 
@@ -55,4 +62,30 @@ public OffsetDateTime convert(DateTimeOffset source) {
 			return source.getOffsetDateTime();
 		}
 	}
+
+	@ReadingConverter
+	enum DateTimeOffsetToZonedDateTimeConverter implements Converter {
+
+		INSTANCE;
+
+		@Override
+		public ZonedDateTime convert(DateTimeOffset source) {
+			return source.getOffsetDateTime().toZonedDateTime();
+		}
+	}
+
+	/**
+	 * Unfortunately, SQl Server jdbc driver can accept {@link OffsetDateTime} as
+	 * {@link java.sql.Types#TIMESTAMP_WITH_TIMEZONE}, but not {@link ZonedDateTime}
+	 */
+	@WritingConverter
+	enum ZonedDateTimeToOffsetDateTimeConverter implements Converter {
+
+		INSTANCE;
+
+		@Override
+		public OffsetDateTime convert(ZonedDateTime source) {
+			return source.toOffsetDateTime();
+		}
+	}
 }
diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/StringBasedJdbcQuery.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/StringBasedJdbcQuery.java
index 14038a6675..de4063796e 100644
--- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/StringBasedJdbcQuery.java
+++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/StringBasedJdbcQuery.java
@@ -15,11 +15,6 @@
  */
 package org.springframework.data.jdbc.repository.query;
 
-import static org.springframework.data.jdbc.repository.query.JdbcQueryExecution.*;
-
-import java.lang.reflect.Constructor;
-import java.sql.JDBCType;
-
 import org.springframework.beans.BeanUtils;
 import org.springframework.beans.factory.BeanFactory;
 import org.springframework.core.convert.converter.Converter;
@@ -28,7 +23,6 @@
 import org.springframework.data.jdbc.core.convert.JdbcValue;
 import org.springframework.data.jdbc.repository.config.DialectResolver;
 import org.springframework.data.jdbc.support.JdbcUtil;
-import org.springframework.data.relational.core.dialect.Dialect;
 import org.springframework.data.relational.core.mapping.RelationalMappingContext;
 import org.springframework.data.relational.repository.query.RelationalParameterAccessor;
 import org.springframework.data.relational.repository.query.RelationalParametersParameterAccessor;
@@ -45,6 +39,11 @@
 import org.springframework.util.ObjectUtils;
 import org.springframework.util.StringUtils;
 
+import java.lang.reflect.Constructor;
+import java.sql.JDBCType;
+
+import static org.springframework.data.jdbc.repository.query.JdbcQueryExecution.ResultProcessingConverter;
+
 /**
  * A query to be executed based on a repository method, it's annotated SQL query and the arguments provided to the
  * method.
@@ -55,6 +54,7 @@
  * @author Maciej Walkowiak
  * @author Mark Paluch
  * @author Hebert Coelho
+ * @author Mikhail Polivakha
  * @since 2.0
  */
 public class StringBasedJdbcQuery extends AbstractJdbcQuery {
@@ -160,7 +160,7 @@ private void convertAndAddParameter(MapSqlParameterSource parameters, Parameter
 		String parameterName = p.getName().orElseThrow(() -> new IllegalStateException(PARAMETER_NEEDS_TO_BE_NAMED));
 
 		Class parameterType = queryMethod.getParameters().getParameter(p.getIndex()).getType();
-		Class conversionTargetType = JdbcColumnTypes.INSTANCE.resolvePrimitiveType(parameterType);
+		Class conversionTargetType = JdbcColumnTypes.INSTANCE.resolvePrimitiveType(parameterType, DialectResolver.getDialect(operations.getJdbcOperations()));
 
 		JdbcValue jdbcValue = converter.writeJdbcValue(
 				value,
diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/BasicJdbcConverterUnitTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/BasicJdbcConverterUnitTests.java
index e16a7d7719..339889b721 100644
--- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/BasicJdbcConverterUnitTests.java
+++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/BasicJdbcConverterUnitTests.java
@@ -74,7 +74,7 @@ public void testTargetTypesForPropertyType() {
 		checkTargetType(softly, entity, "localDateTime", LocalDateTime.class);
 		checkTargetType(softly, entity, "localDate", Timestamp.class);
 		checkTargetType(softly, entity, "localTime", Timestamp.class);
-		checkTargetType(softly, entity, "zonedDateTime", OffsetDateTime.class);
+		checkTargetType(softly, entity, "zonedDateTime", ZonedDateTime.class);
 		checkTargetType(softly, entity, "offsetDateTime", OffsetDateTime.class);
 		checkTargetType(softly, entity, "instant", Timestamp.class);
 		checkTargetType(softly, entity, "date", Date.class);
diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryCustomConversionIntegrationTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryCustomConversionIntegrationTests.java
index 3767b28dd1..1f688cdcc3 100644
--- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryCustomConversionIntegrationTests.java
+++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryCustomConversionIntegrationTests.java
@@ -15,7 +15,10 @@
  */
 package org.springframework.data.jdbc.repository;
 
+import org.apache.commons.compress.utils.Lists;
+import org.junit.jupiter.api.Disabled;
 import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.condition.EnabledIfSystemProperty;
 import org.junit.jupiter.api.extension.ExtendWith;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.context.annotation.Bean;
@@ -29,6 +32,7 @@
 import org.springframework.data.jdbc.core.convert.JdbcValue;
 import org.springframework.data.jdbc.core.dialect.H2TimestampWithTimeZoneToOffsetDateTimeConverter;
 import org.springframework.data.jdbc.core.dialect.H2TimestampWithTimeZoneToZonedDateTimeConverter;
+import org.springframework.data.jdbc.core.dialect.JdbcDb2Dialect;
 import org.springframework.data.jdbc.repository.support.JdbcRepositoryFactory;
 import org.springframework.data.jdbc.testing.AssumeFeatureTestExecutionListener;
 import org.springframework.data.jdbc.testing.TestConfiguration;
@@ -45,7 +49,10 @@
 import java.time.OffsetDateTime;
 import java.time.ZoneOffset;
 import java.time.ZonedDateTime;
+import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Date;
+import java.util.List;
 import java.util.Optional;
 
 import static java.util.Arrays.asList;
@@ -83,38 +90,20 @@ EntityWithBooleanRepository entityWithBooleanRepository() {
 		}
 
 		@Bean
-		EntityWithZonedDateTimeRepository entityWithZonedDateTimeRepository() {
-			return factory.getRepository(EntityWithZonedDateTimeRepository.class);
-		}
+		JdbcCustomConversions jdbcCustomConversions() {
+			List converters = Lists.newArrayList();
 
-		@Bean
-		EntityWithOffsetDateTimeRepository entityWithOffsetDateTimeRepository() {
-			return factory.getRepository(EntityWithOffsetDateTimeRepository.class);
-		}
+			converters.add(StringToBigDecimalConverter.INSTANCE);
+			converters.add(BigDecimalToString.INSTANCE);
+			converters.add(CustomIdReadingConverter.INSTANCE);
+			converters.add(CustomIdWritingConverter.INSTANCE);
 
-		@Bean
-		JdbcCustomConversions jdbcCustomConversions() {
-			return new JdbcCustomConversions(
-					asList(
-							StringToBigDecimalConverter.INSTANCE,
-							BigDecimalToString.INSTANCE,
-							CustomIdReadingConverter.INSTANCE,
-							CustomIdWritingConverter.INSTANCE,
-							TimestampToOffsetDateTimeConverter.INSTANCE,
-							TimestampToZonedDateTimeConverter.INSTANCE,
-							H2TimestampWithTimeZoneToZonedDateTimeConverter.INSTANCE,
-							H2TimestampWithTimeZoneToOffsetDateTimeConverter.INSTANCE
-					)
-			);
+			return new JdbcCustomConversions(converters);
 		}
 	}
 
 	@Autowired EntityWithBooleanRepository repository;
 
-	@Autowired EntityWithZonedDateTimeRepository entityWithZonedDateTimeRepository;
-
-	@Autowired EntityWithOffsetDateTimeRepository entityWithOffsetDateTimeRepository;
-
 	/**
 	 * In PostgreSQL this fails if a simple converter like the following is used.
 	 *
@@ -176,58 +165,14 @@ public void saveAndLoadAnEntityWithReference() {
 		});
 	}
 
-	/**
-	 * DATAJDBC-1089
-	 */
-	@Test
-	public void testZonedDateTimeToTimestampConversion() {
-		EntityWithZonedDateTime entity = new EntityWithZonedDateTime();
-		entity.createdAt = ZonedDateTime.now(ZoneOffset.ofHours(3));
-
-		final EntityWithZonedDateTime persistedEntity = entityWithZonedDateTimeRepository.save(entity);
-		final Optional foundEntity = entityWithZonedDateTimeRepository.findById(persistedEntity.id);
-
-		assertThat(foundEntity).isPresent();
-		assertThat(persistedEntity.createdAt.toEpochSecond()).isEqualTo(foundEntity.get().createdAt.toEpochSecond());
-	}
-
-	/**
-	 * DATAJDBC-1089
-	 */
-	@Test
-	public void testOffsetDateTimeToTimestampConversion() {
-		EntityWithOffsetDateTime entity = new EntityWithOffsetDateTime();
-		entity.createdAt = OffsetDateTime.now(ZoneOffset.ofHours(3));
-
-		final EntityWithOffsetDateTime persistedEntity = entityWithOffsetDateTimeRepository.save(entity);
-		final Optional foundEntity = entityWithOffsetDateTimeRepository.findById(persistedEntity.id);
-
-		assertThat(foundEntity).isPresent();
-		assertThat(persistedEntity.createdAt.toEpochSecond()).isEqualTo(foundEntity.get().createdAt.toEpochSecond());
-	}
-
 	interface EntityWithBooleanRepository extends CrudRepository {}
 
-	interface EntityWithZonedDateTimeRepository extends CrudRepository {};
-
-	interface EntityWithOffsetDateTimeRepository extends CrudRepository {};
-
 	private static class EntityWithStringyBigDecimal {
 		@Id CustomId id;
 		String stringyNumber;
 		OtherEntity reference;
 	}
 
-	private static class EntityWithZonedDateTime {
-		@Id private Long id;
-		private ZonedDateTime createdAt;
-	}
-
-	private static class EntityWithOffsetDateTime {
-		@Id private Long id;
-		private OffsetDateTime createdAt;
-	}
-
 	private static class CustomId {
 
 		private final Long value;
@@ -289,26 +234,4 @@ public CustomId convert(Number source) {
 			return new CustomId(source.longValue());
 		}
 	}
-
-	@ReadingConverter
-	public enum TimestampToZonedDateTimeConverter implements Converter {
-
-		INSTANCE;
-
-		@Override
-		public ZonedDateTime convert(Timestamp source) {
-			return ZonedDateTime.ofInstant(source.toInstant(), ZoneOffset.ofHours(3));
-		}
-	}
-
-	@ReadingConverter
-	public enum TimestampToOffsetDateTimeConverter implements Converter {
-
-		INSTANCE;
-
-		@Override
-		public OffsetDateTime convert(Timestamp source) {
-			return OffsetDateTime.ofInstant(source.toInstant(), ZoneOffset.ofHours(3));
-		}
-	}
 }
\ No newline at end of file
diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryIntegrationTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryIntegrationTests.java
index 66201877e3..2e79a61b1a 100644
--- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryIntegrationTests.java
+++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryIntegrationTests.java
@@ -30,12 +30,16 @@
 import java.time.LocalDateTime;
 import java.time.OffsetDateTime;
 import java.time.ZoneOffset;
+import java.time.ZonedDateTime;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
+import java.util.Optional;
 
 import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Disabled;
 import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.condition.EnabledIfSystemProperty;
 import org.junit.jupiter.api.extension.ExtendWith;
 
 import org.springframework.beans.factory.annotation.Autowired;
@@ -87,6 +91,8 @@ public class JdbcRepositoryIntegrationTests {
 	@Autowired NamedParameterJdbcTemplate template;
 	@Autowired DummyEntityRepository repository;
 	@Autowired MyEventListener eventListener;
+	@Autowired EntityWithZonedDateTimeRepository entityWithZonedDateTimeRepository;
+	@Autowired EntityWithOffsetDateTimeRepository entityWithOffsetDateTimeRepository;
 
 	private static DummyEntity createDummyEntity() {
 
@@ -96,6 +102,17 @@ private static DummyEntity createDummyEntity() {
 		return entity;
 	}
 
+	private static class EntityWithZonedDateTime {
+		@Id private Long id;
+		private ZonedDateTime createdAt;
+	}
+
+	private static class EntityWithOffsetDateTime {
+		@Id private Long id;
+		private OffsetDateTime createdAt;
+	}
+
+
 	@BeforeEach
 	public void before() {
 
@@ -549,6 +566,37 @@ void queryByAggregateReference() {
 		assertThat(result).extracting(e -> e.idProp).containsExactly(two.idProp);
 	}
 
+	/**
+	 * DATAJDBC-1089
+	 */
+	@Test
+	public void testZonedDateTimeToTimestampConversion() {
+		EntityWithZonedDateTime entity = new EntityWithZonedDateTime();
+		entity.createdAt = ZonedDateTime.now(ZoneOffset.ofHours(3));
+
+		final EntityWithZonedDateTime persistedEntity = entityWithZonedDateTimeRepository.save(entity);
+		final Optional foundEntity = entityWithZonedDateTimeRepository.findById(persistedEntity.id);
+
+		assertThat(foundEntity).isPresent();
+		assertThat(persistedEntity.createdAt.toEpochSecond()).isEqualTo(foundEntity.get().createdAt.toEpochSecond());
+	}
+
+	/**
+	 * DATAJDBC-1089
+	 */
+	@Test
+	public void testOffsetDateTimeToTimestampConversion() {
+		EntityWithOffsetDateTime entity = new EntityWithOffsetDateTime();
+		entity.createdAt = OffsetDateTime.now(ZoneOffset.ofHours(3));
+
+		final EntityWithOffsetDateTime persistedEntity = entityWithOffsetDateTimeRepository.save(entity);
+		final Optional foundEntity = entityWithOffsetDateTimeRepository.findById(persistedEntity.id);
+
+		assertThat(foundEntity).isPresent();
+		assertThat(persistedEntity.createdAt.toEpochSecond()).isEqualTo(foundEntity.get().createdAt.toEpochSecond());
+	}
+
+
 	private Instant createDummyBeforeAndAfterNow() {
 
 		Instant now = Instant.now();
@@ -572,6 +620,10 @@ private Instant createDummyBeforeAndAfterNow() {
 		return now;
 	}
 
+	interface EntityWithZonedDateTimeRepository extends CrudRepository {};
+
+	interface EntityWithOffsetDateTimeRepository extends CrudRepository {};
+
 	interface DummyEntityRepository extends CrudRepository {
 
 		List findAllByNamedQuery();
@@ -643,6 +695,16 @@ DummyEntityRepository dummyEntityRepository() {
 			return factory.getRepository(DummyEntityRepository.class);
 		}
 
+		@Bean
+		EntityWithZonedDateTimeRepository entityWithZonedDateTimeRepository() {
+			return factory.getRepository(EntityWithZonedDateTimeRepository.class);
+		}
+
+		@Bean
+		EntityWithOffsetDateTimeRepository entityWithOffsetDateTimeRepository() {
+			return factory.getRepository(EntityWithOffsetDateTimeRepository.class);
+		}
+
 		@Bean
 		NamedQueries namedQueries() throws IOException {
 
diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/testing/MariaDBDataSourceConfiguration.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/testing/MariaDBDataSourceConfiguration.java
index d703bf68c4..2c7c74db8e 100644
--- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/testing/MariaDBDataSourceConfiguration.java
+++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/testing/MariaDBDataSourceConfiguration.java
@@ -71,10 +71,8 @@ protected DataSource createDataSource() {
 
 	@Override
 	public void afterPropertiesSet() throws Exception {
-
 		try (Connection connection = createDataSource().getConnection()) {
-			ScriptUtils.executeSqlScript(connection,
-					new ByteArrayResource("DROP DATABASE test;CREATE DATABASE test;".getBytes()));
+			ScriptUtils.executeSqlScript(connection, new ByteArrayResource("DROP DATABASE IF EXISTS test;CREATE DATABASE IF NOT EXISTS test;".getBytes()));
 		}
 	}
 }
diff --git a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryCustomConversionIntegrationTests-db2.sql b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryCustomConversionIntegrationTests-db2.sql
index 6885aa63a0..328e3b1d42 100644
--- a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryCustomConversionIntegrationTests-db2.sql
+++ b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryCustomConversionIntegrationTests-db2.sql
@@ -1,9 +1,5 @@
 DROP TABLE ENTITY_WITH_STRINGY_BIG_DECIMAL;
 DROP TABLE OTHER_ENTITY;
-DROP TABLE ENTITY_WITH_ZONED_DATE_TIME;
-DROP TABLE ENTITY_WITH_OFFSET_DATE_TIME;
 
 CREATE TABLE ENTITY_WITH_STRINGY_BIG_DECIMAL ( id BIGINT GENERATED BY DEFAULT AS IDENTITY(START WITH 1) PRIMARY KEY, Stringy_number DECIMAL(20,10));
-CREATE TABLE OTHER_ENTITY ( ID  BIGINT GENERATED BY DEFAULT AS IDENTITY(START WITH 1) PRIMARY KEY, CREATED DATE, ENTITY_WITH_STRINGY_BIG_DECIMAL INTEGER);
-CREATE TABLE ENTITY_WITH_ZONED_DATE_TIME (ID BIGINT GENERATED BY DEFAULT AS IDENTITY (START WITH 1) PRIMARY KEY, CREATED_AT TIMESTAMP);
-CREATE TABLE ENTITY_WITH_OFFSET_DATE_TIME (ID BIGINT GENERATED BY DEFAULT AS IDENTITY (START WITH 1) PRIMARY KEY, CREATED_AT TIMESTAMP);
\ No newline at end of file
+CREATE TABLE OTHER_ENTITY ( ID  BIGINT GENERATED BY DEFAULT AS IDENTITY(START WITH 1) PRIMARY KEY, CREATED DATE, ENTITY_WITH_STRINGY_BIG_DECIMAL INTEGER);
\ No newline at end of file
diff --git a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryCustomConversionIntegrationTests-h2.sql b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryCustomConversionIntegrationTests-h2.sql
index 186ac82e69..7eece207e8 100644
--- a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryCustomConversionIntegrationTests-h2.sql
+++ b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryCustomConversionIntegrationTests-h2.sql
@@ -1,4 +1,2 @@
 CREATE TABLE ENTITY_WITH_STRINGY_BIG_DECIMAL ( id IDENTITY PRIMARY KEY, Stringy_number DECIMAL(20,10));
-CREATE TABLE OTHER_ENTITY ( ID IDENTITY PRIMARY KEY, CREATED DATE, ENTITY_WITH_STRINGY_BIG_DECIMAL INTEGER);
-CREATE TABLE ENTITY_WITH_ZONED_DATE_TIME (ID BIGINT AUTO_INCREMENT PRIMARY KEY, CREATED_AT TIMESTAMP WITH TIME ZONE);
-CREATE TABLE ENTITY_WITH_OFFSET_DATE_TIME (ID BIGINT AUTO_INCREMENT PRIMARY KEY, CREATED_AT TIMESTAMP WITH TIME ZONE);
\ No newline at end of file
+CREATE TABLE OTHER_ENTITY ( ID IDENTITY PRIMARY KEY, CREATED DATE, ENTITY_WITH_STRINGY_BIG_DECIMAL INTEGER);
\ No newline at end of file
diff --git a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryCustomConversionIntegrationTests-hsql.sql b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryCustomConversionIntegrationTests-hsql.sql
index 55473aa1bb..7eece207e8 100644
--- a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryCustomConversionIntegrationTests-hsql.sql
+++ b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryCustomConversionIntegrationTests-hsql.sql
@@ -1,4 +1,2 @@
 CREATE TABLE ENTITY_WITH_STRINGY_BIG_DECIMAL ( id IDENTITY PRIMARY KEY, Stringy_number DECIMAL(20,10));
-CREATE TABLE OTHER_ENTITY ( ID IDENTITY PRIMARY KEY, CREATED DATE, ENTITY_WITH_STRINGY_BIG_DECIMAL INTEGER);
-CREATE TABLE ENTITY_WITH_ZONED_DATE_TIME (id IDENTITY PRIMARY KEY, CREATED_AT TIMESTAMP WITH TIME ZONE);
-CREATE TABLE ENTITY_WITH_OFFSET_DATE_TIME (id IDENTITY PRIMARY KEY, CREATED_AT TIMESTAMP WITH TIME ZONE);
\ No newline at end of file
+CREATE TABLE OTHER_ENTITY ( ID IDENTITY PRIMARY KEY, CREATED DATE, ENTITY_WITH_STRINGY_BIG_DECIMAL INTEGER);
\ No newline at end of file
diff --git a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryCustomConversionIntegrationTests-mariadb.sql b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryCustomConversionIntegrationTests-mariadb.sql
index 65cb053003..50fb20ce10 100644
--- a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryCustomConversionIntegrationTests-mariadb.sql
+++ b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryCustomConversionIntegrationTests-mariadb.sql
@@ -1,4 +1,2 @@
 CREATE TABLE ENTITY_WITH_STRINGY_BIG_DECIMAL ( id BIGINT AUTO_INCREMENT PRIMARY KEY, Stringy_number DECIMAL(20,10));
-CREATE TABLE OTHER_ENTITY ( ID BIGINT AUTO_INCREMENT PRIMARY KEY, CREATED DATE, ENTITY_WITH_STRINGY_BIG_DECIMAL INTEGER);
-CREATE TABLE ENTITY_WITH_ZONED_DATE_TIME (ID BIGINT AUTO_INCREMENT PRIMARY KEY, CREATED_AT TIMESTAMP(3));
-CREATE TABLE ENTITY_WITH_OFFSET_DATE_TIME (ID BIGINT AUTO_INCREMENT PRIMARY KEY, CREATED_AT TIMESTAMP(3));
\ No newline at end of file
+CREATE TABLE OTHER_ENTITY ( ID BIGINT AUTO_INCREMENT PRIMARY KEY, CREATED DATE, ENTITY_WITH_STRINGY_BIG_DECIMAL INTEGER);
\ No newline at end of file
diff --git a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryCustomConversionIntegrationTests-mssql.sql b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryCustomConversionIntegrationTests-mssql.sql
index 608e41495d..6eb1afcadc 100644
--- a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryCustomConversionIntegrationTests-mssql.sql
+++ b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryCustomConversionIntegrationTests-mssql.sql
@@ -1,9 +1,5 @@
 DROP TABLE OTHER_ENTITY;
 DROP TABLE ENTITY_WITH_STRINGY_BIG_DECIMAL;
-DROP TABLE ENTITY_WITH_ZONED_DATE_TIME;
-DROP TABLE ENTITY_WITH_OFFSET_DATE_TIME;
 
 CREATE TABLE ENTITY_WITH_STRINGY_BIG_DECIMAL ( id BIGINT IDENTITY PRIMARY KEY, Stringy_number DECIMAL(20,10));
-CREATE TABLE OTHER_ENTITY ( ID BIGINT IDENTITY PRIMARY KEY, CREATED DATE, ENTITY_WITH_STRINGY_BIG_DECIMAL INTEGER);
-CREATE TABLE ENTITY_WITH_ZONED_DATE_TIME (id BIGINT IDENTITY PRIMARY KEY, CREATED_AT DATETIMEOFFSET);
-CREATE TABLE ENTITY_WITH_OFFSET_DATE_TIME (id BIGINT IDENTITY PRIMARY KEY, CREATED_AT DATETIMEOFFSET);
\ No newline at end of file
+CREATE TABLE OTHER_ENTITY ( ID BIGINT IDENTITY PRIMARY KEY, CREATED DATE, ENTITY_WITH_STRINGY_BIG_DECIMAL INTEGER);
\ No newline at end of file
diff --git a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryCustomConversionIntegrationTests-mysql.sql b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryCustomConversionIntegrationTests-mysql.sql
index adc4d34488..ddd20eabba 100644
--- a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryCustomConversionIntegrationTests-mysql.sql
+++ b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryCustomConversionIntegrationTests-mysql.sql
@@ -1,4 +1,2 @@
 CREATE TABLE ENTITY_WITH_STRINGY_BIG_DECIMAL ( ID BIGINT AUTO_INCREMENT PRIMARY KEY, Stringy_number DECIMAL(20,10));
-CREATE TABLE OTHER_ENTITY ( ID BIGINT AUTO_INCREMENT PRIMARY KEY, CREATED DATE, ENTITY_WITH_STRINGY_BIG_DECIMAL INTEGER);
-CREATE TABLE ENTITY_WITH_ZONED_DATE_TIME (id BIGINT AUTO_INCREMENT PRIMARY KEY, CREATED_AT TIMESTAMP(3));
-CREATE TABLE ENTITY_WITH_OFFSET_DATE_TIME (id BIGINT AUTO_INCREMENT PRIMARY KEY, CREATED_AT TIMESTAMP(3));
\ No newline at end of file
+CREATE TABLE OTHER_ENTITY ( ID BIGINT AUTO_INCREMENT PRIMARY KEY, CREATED DATE, ENTITY_WITH_STRINGY_BIG_DECIMAL INTEGER);
\ No newline at end of file
diff --git a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryCustomConversionIntegrationTests-oracle.sql b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryCustomConversionIntegrationTests-oracle.sql
index 3c0f93e5a3..8ec5b011e7 100644
--- a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryCustomConversionIntegrationTests-oracle.sql
+++ b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryCustomConversionIntegrationTests-oracle.sql
@@ -1,7 +1,5 @@
 DROP TABLE ENTITY_WITH_STRINGY_BIG_DECIMAL CASCADE CONSTRAINTS PURGE;
 DROP TABLE OTHER_ENTITY CASCADE CONSTRAINTS PURGE;
-DROP TABLE ENTITY_WITH_ZONED_DATE_TIME CASCADE CONSTRAINTS PURGE;
-DROP TABLE ENTITY_WITH_OFFSET_DATE_TIME CASCADE CONSTRAINTS PURGE;
 
 CREATE TABLE ENTITY_WITH_STRINGY_BIG_DECIMAL (
     ID NUMBER GENERATED BY DEFAULT ON NULL AS IDENTITY PRIMARY KEY,
@@ -12,14 +10,4 @@ CREATE TABLE OTHER_ENTITY (
     ID  NUMBER GENERATED BY DEFAULT ON NULL AS IDENTITY PRIMARY KEY,
     CREATED DATE,
     ENTITY_WITH_STRINGY_BIG_DECIMAL INTEGER
-);
-
-CREATE TABLE ENTITY_WITH_ZONED_DATE_TIME (
-    id NUMBER GENERATED BY DEFAULT ON NULL AS IDENTITY PRIMARY KEY,
-    CREATED_AT TIMESTAMP WITH TIME ZONE
-);
-
-CREATE TABLE ENTITY_WITH_OFFSET_DATE_TIME (
-    id NUMBER GENERATED BY DEFAULT ON NULL AS IDENTITY PRIMARY KEY,
-    CREATED_AT TIMESTAMP WITH TIME ZONE
 );
\ No newline at end of file
diff --git a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryCustomConversionIntegrationTests-postgres.sql b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryCustomConversionIntegrationTests-postgres.sql
index b9f938d27e..36b46c27d9 100644
--- a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryCustomConversionIntegrationTests-postgres.sql
+++ b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryCustomConversionIntegrationTests-postgres.sql
@@ -1,4 +1,2 @@
 CREATE TABLE ENTITY_WITH_STRINGY_BIG_DECIMAL ( id SERIAL PRIMARY KEY, Stringy_number DECIMAL(20,10));
-CREATE TABLE OTHER_ENTITY ( ID SERIAL PRIMARY KEY, CREATED DATE, ENTITY_WITH_STRINGY_BIG_DECIMAL INTEGER);
-CREATE TABLE ENTITY_WITH_ZONED_DATE_TIME (id SERIAL PRIMARY KEY, CREATED_AT TIMESTAMPTZ);
-CREATE TABLE ENTITY_WITH_OFFSET_DATE_TIME (id SERIAL PRIMARY KEY, CREATED_AT TIMESTAMPTZ);
\ No newline at end of file
+CREATE TABLE OTHER_ENTITY ( ID SERIAL PRIMARY KEY, CREATED DATE, ENTITY_WITH_STRINGY_BIG_DECIMAL INTEGER);
\ No newline at end of file
diff --git a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryIntegrationTests-db2.sql b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryIntegrationTests-db2.sql
index 34be74ec51..d58d89f383 100644
--- a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryIntegrationTests-db2.sql
+++ b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryIntegrationTests-db2.sql
@@ -1,4 +1,6 @@
 DROP TABLE dummy_entity;
+DROP TABLE ENTITY_WITH_ZONED_DATE_TIME;
+DROP TABLE ENTITY_WITH_OFFSET_DATE_TIME;
 
 CREATE TABLE dummy_entity
 (
@@ -9,3 +11,6 @@ CREATE TABLE dummy_entity
     FLAG             BOOLEAN,
     REF              BIGINT
 );
+
+CREATE TABLE ENTITY_WITH_ZONED_DATE_TIME (ID BIGINT GENERATED BY DEFAULT AS IDENTITY (START WITH 1) PRIMARY KEY, CREATED_AT TIMESTAMP);
+CREATE TABLE ENTITY_WITH_OFFSET_DATE_TIME (ID BIGINT GENERATED BY DEFAULT AS IDENTITY (START WITH 1) PRIMARY KEY, CREATED_AT TIMESTAMP);
\ No newline at end of file
diff --git a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryIntegrationTests-h2.sql b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryIntegrationTests-h2.sql
index b3b93bc744..b5964a8179 100644
--- a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryIntegrationTests-h2.sql
+++ b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryIntegrationTests-h2.sql
@@ -7,3 +7,6 @@ CREATE TABLE dummy_entity
     FLAG             BOOLEAN,
     REF              BIGINT
 );
+
+CREATE TABLE ENTITY_WITH_ZONED_DATE_TIME (ID BIGINT AUTO_INCREMENT PRIMARY KEY, CREATED_AT TIMESTAMP WITH TIME ZONE);
+CREATE TABLE ENTITY_WITH_OFFSET_DATE_TIME (ID BIGINT AUTO_INCREMENT PRIMARY KEY, CREATED_AT TIMESTAMP WITH TIME ZONE);
\ No newline at end of file
diff --git a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryIntegrationTests-hsql.sql b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryIntegrationTests-hsql.sql
index b3b93bc744..1aabce50ab 100644
--- a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryIntegrationTests-hsql.sql
+++ b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryIntegrationTests-hsql.sql
@@ -7,3 +7,5 @@ CREATE TABLE dummy_entity
     FLAG             BOOLEAN,
     REF              BIGINT
 );
+CREATE TABLE ENTITY_WITH_ZONED_DATE_TIME (id IDENTITY PRIMARY KEY, CREATED_AT TIMESTAMP WITH TIME ZONE);
+CREATE TABLE ENTITY_WITH_OFFSET_DATE_TIME (id IDENTITY PRIMARY KEY, CREATED_AT TIMESTAMP WITH TIME ZONE);
\ No newline at end of file
diff --git a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryIntegrationTests-mariadb.sql b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryIntegrationTests-mariadb.sql
index 949e626399..0a48e46fb9 100644
--- a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryIntegrationTests-mariadb.sql
+++ b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryIntegrationTests-mariadb.sql
@@ -7,3 +7,6 @@ CREATE TABLE dummy_entity
     FLAG             BOOLEAN,
     REF              BIGINT
 );
+
+CREATE TABLE ENTITY_WITH_ZONED_DATE_TIME (ID BIGINT AUTO_INCREMENT PRIMARY KEY, CREATED_AT TIMESTAMP(3));
+CREATE TABLE ENTITY_WITH_OFFSET_DATE_TIME (ID BIGINT AUTO_INCREMENT PRIMARY KEY, CREATED_AT TIMESTAMP(3));
\ No newline at end of file
diff --git a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryIntegrationTests-mssql.sql b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryIntegrationTests-mssql.sql
index 15f8881327..ff5077ec36 100644
--- a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryIntegrationTests-mssql.sql
+++ b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryIntegrationTests-mssql.sql
@@ -1,4 +1,7 @@
 DROP TABLE IF EXISTS dummy_entity;
+DROP TABLE IF EXISTS ENTITY_WITH_ZONED_DATE_TIME;
+DROP TABLE IF EXISTS ENTITY_WITH_OFFSET_DATE_TIME;
+
 CREATE TABLE dummy_entity
 (
     id_Prop          BIGINT IDENTITY PRIMARY KEY,
@@ -8,3 +11,6 @@ CREATE TABLE dummy_entity
     FLAG             BIT,
     REF              BIGINT
 );
+
+CREATE TABLE ENTITY_WITH_ZONED_DATE_TIME (id BIGINT IDENTITY PRIMARY KEY, CREATED_AT DATETIMEOFFSET);
+CREATE TABLE ENTITY_WITH_OFFSET_DATE_TIME (id BIGINT IDENTITY PRIMARY KEY, CREATED_AT DATETIMEOFFSET);
\ No newline at end of file
diff --git a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryIntegrationTests-mysql.sql b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryIntegrationTests-mysql.sql
index e3baa94602..34d318a479 100644
--- a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryIntegrationTests-mysql.sql
+++ b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryIntegrationTests-mysql.sql
@@ -10,3 +10,6 @@ CREATE TABLE DUMMY_ENTITY
     FLAG             BIT(1),
     REF              BIGINT
 );
+
+CREATE TABLE ENTITY_WITH_ZONED_DATE_TIME (id BIGINT AUTO_INCREMENT PRIMARY KEY, CREATED_AT TIMESTAMP(3));
+CREATE TABLE ENTITY_WITH_OFFSET_DATE_TIME (id BIGINT AUTO_INCREMENT PRIMARY KEY, CREATED_AT TIMESTAMP(3));
\ No newline at end of file
diff --git a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryIntegrationTests-oracle.sql b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryIntegrationTests-oracle.sql
index e71eb63286..41621cfef9 100644
--- a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryIntegrationTests-oracle.sql
+++ b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryIntegrationTests-oracle.sql
@@ -1,4 +1,6 @@
 DROP TABLE DUMMY_ENTITY CASCADE CONSTRAINTS PURGE;
+DROP TABLE ENTITY_WITH_ZONED_DATE_TIME CASCADE CONSTRAINTS PURGE;
+DROP TABLE ENTITY_WITH_OFFSET_DATE_TIME CASCADE CONSTRAINTS PURGE;
 
 CREATE TABLE DUMMY_ENTITY
 (
@@ -9,3 +11,13 @@ CREATE TABLE DUMMY_ENTITY
     FLAG             NUMBER(1,0),
     REF              NUMBER
 );
+
+CREATE TABLE ENTITY_WITH_ZONED_DATE_TIME (
+  id NUMBER GENERATED BY DEFAULT ON NULL AS IDENTITY PRIMARY KEY,
+  CREATED_AT TIMESTAMP WITH TIME ZONE
+);
+
+CREATE TABLE ENTITY_WITH_OFFSET_DATE_TIME (
+  id NUMBER GENERATED BY DEFAULT ON NULL AS IDENTITY PRIMARY KEY,
+  CREATED_AT TIMESTAMP WITH TIME ZONE
+);
\ No newline at end of file
diff --git a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryIntegrationTests-postgres.sql b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryIntegrationTests-postgres.sql
index 97fc78c9da..37e485fa13 100644
--- a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryIntegrationTests-postgres.sql
+++ b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryIntegrationTests-postgres.sql
@@ -1,4 +1,7 @@
 DROP TABLE dummy_entity;
+DROP TABLE ENTITY_WITH_ZONED_DATE_TIME;
+DROP TABLE ENTITY_WITH_OFFSET_DATE_TIME;
+
 CREATE TABLE dummy_entity
 (
     id_Prop          SERIAL PRIMARY KEY,
@@ -8,3 +11,5 @@ CREATE TABLE dummy_entity
     FLAG             BOOLEAN,
     REF              BIGINT
 );
+CREATE TABLE ENTITY_WITH_ZONED_DATE_TIME (id SERIAL PRIMARY KEY, CREATED_AT TIMESTAMPTZ);
+CREATE TABLE ENTITY_WITH_OFFSET_DATE_TIME (id SERIAL PRIMARY KEY, CREATED_AT TIMESTAMPTZ);
\ No newline at end of file
diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/Dialect.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/Dialect.java
index 00a1b39d6c..d6ec8e1e68 100644
--- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/Dialect.java
+++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/Dialect.java
@@ -15,6 +15,7 @@
  */
 package org.springframework.data.relational.core.dialect;
 
+import java.sql.Timestamp;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
@@ -127,8 +128,20 @@ default InsertRenderContext getInsertRenderContext() {
 
 	/**
 	 * @return the map of custom mappings from java classes to sql codes (integer sql codes present in {@link java.sql.Types})
+	 *         For example, for most of the drivers {@link java.sql.Types#TIMESTAMP_WITH_TIMEZONE} is OK, but for the others,
+	 *         like MySQL, we have to use {@link java.sql.Types#TIMESTAMP} and pass OffsetDateTime or ZonedDateTime by the means
+	 *         of {@link java.sql.PreparedStatement#setTimestamp(int, Timestamp)}.
 	 * @since 3.0
 	 */
 	@NonNull
 	default Map, Integer> getCustomSqlCodesMappings() { return new HashMap<>(); }
-}
+
+	/**
+	 * @return the map of custom mappings from source java classes to appropriate jdbc columns java classes in the scope of current dialect.
+	 * 	          For example, most of the drivers can handle Jsr310 types directly, like {@link java.time.OffsetDateTime} or {@link java.time.ZonedDateTime},
+	 * 	          and we can just set them as is, but some others, like DB2 for example, require these types to be mapped/converted
+	 * 	          into {@link Timestamp} before passing to the underlying {@link java.sql.PreparedStatement}
+	 */
+	@NonNull
+	default Map, Class> getCustomJdbcColumnsMappings() { return new HashMap<>(); }
+}
\ No newline at end of file
diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/HsqlDbDialect.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/HsqlDbDialect.java
index cf534de202..b877c8e3ef 100644
--- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/HsqlDbDialect.java
+++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/HsqlDbDialect.java
@@ -15,11 +15,20 @@
  */
 package org.springframework.data.relational.core.dialect;
 
+import org.springframework.core.convert.converter.Converter;
+import org.springframework.data.convert.WritingConverter;
+
+import java.time.OffsetDateTime;
+import java.time.ZonedDateTime;
+import java.util.ArrayList;
+import java.util.Collection;
+
 /**
  * A {@link Dialect} for HsqlDb.
  *
  * @author Jens Schauder
  * @author Myeonghyeon Lee
+ * @author Mikhail Polivakha
  */
 public class HsqlDbDialect extends AbstractDialect {
 
@@ -59,4 +68,26 @@ public Position getClausePosition() {
 			return Position.AFTER_ORDER_BY;
 		}
 	};
+
+	@Override
+	public Collection getConverters() {
+		Collection converters = new ArrayList<>(super.getConverters());
+		converters.add(ZonedDateTimeOffsetDateTimeWritingConverter.INSTANCE);
+		return converters;
+	}
+
+	/**
+	 * Unfortunately, HSqlDb jdbc driver can accept {@link OffsetDateTime} as
+	 * {@link java.sql.Types#TIMESTAMP_WITH_TIMEZONE}, but not {@link ZonedDateTime}
+	 */
+	@WritingConverter
+	enum ZonedDateTimeOffsetDateTimeWritingConverter implements Converter {
+
+		INSTANCE;
+
+		@Override
+		public OffsetDateTime convert(ZonedDateTime source) {
+			return source.toOffsetDateTime();
+		}
+	}
 }