From 86524182450807283f0e2b37117620d7d381ae96 Mon Sep 17 00:00:00 2001 From: Jens Schauder Date: Mon, 7 Jun 2021 13:52:42 +0200 Subject: [PATCH] Do not convert from LocalDateTime to Timestamp by default. Most supported databases don't need that conversion. Db2 does need it and gets it through JdbcDb2Dialect. As part of the effort it became obvious that the filtering for conversions between Date and JSR310 data types was broken. It is fixed now, which required a dedicated reading conversion from LocalDateTime to Date for JdbcMySqlDialect. Closes #974 Original pull request: #985. --- .../jdbc/core/convert/JdbcColumnTypes.java | 2 + .../core/convert/JdbcCustomConversions.java | 31 ++++++++---- .../Jsr310TimestampBasedConverters.java | 13 +++-- .../jdbc/core/dialect/JdbcDb2Dialect.java | 2 + .../jdbc/core/dialect/JdbcMySqlDialect.java | 19 ++++++++ .../AggregateChangeIdGenerationUnitTests.java | 16 +++---- ...JdbcAggregateTemplateIntegrationTests.java | 5 +- .../convert/BasicJdbcConverterUnitTests.java | 14 ++++-- .../convert/EntityRowMapperUnitTests.java | 5 +- .../core/convert/IdentifierUnitTests.java | 4 +- .../SqlGeneratorEmbeddedUnitTests.java | 16 +++---- .../core/convert/SqlGeneratorUnitTests.java | 13 ++--- ...SqlIdentifierParameterSourceUnitTests.java | 11 +++-- .../core/dialect/JdbcDb2DialectUnitTests.java | 46 ++++++++++++++++++ .../dialect/JdbcMySqlDialectUnitTests.java | 47 +++++++++++++++++++ .../BasicJdbcPersistentPropertyUnitTests.java | 4 +- .../jdbc/mybatis/MyBatisContextUnitTests.java | 5 +- ...itoryCustomConversionIntegrationTests.java | 4 +- .../JdbcRepositoryIntegrationTests.java | 13 +++++ .../QueryAnnotationHsqlIntegrationTests.java | 4 +- .../DerivedSqlIdentifierUnitTests.java | 5 +- .../core/sql/SqlIdentifierUnitTests.java | 5 +- 22 files changed, 217 insertions(+), 67 deletions(-) create mode 100644 spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/dialect/JdbcDb2DialectUnitTests.java create mode 100644 spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/dialect/JdbcMySqlDialectUnitTests.java 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 bb7a0ef3..a5f2cc91 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 @@ -16,6 +16,7 @@ package org.springframework.data.jdbc.core.convert; import java.sql.Timestamp; +import java.time.LocalDateTime; import java.time.OffsetDateTime; import java.time.ZonedDateTime; import java.time.temporal.Temporal; @@ -54,6 +55,7 @@ public enum JdbcColumnTypes { javaToDbType.put(Enum.class, String.class); javaToDbType.put(ZonedDateTime.class, String.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/JdbcCustomConversions.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/JdbcCustomConversions.java index 97a5b3cb..e91329b8 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/JdbcCustomConversions.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/JdbcCustomConversions.java @@ -15,11 +15,9 @@ */ package org.springframework.data.jdbc.core.convert; -import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.List; -import java.util.function.Predicate; import org.springframework.core.convert.converter.GenericConverter.ConvertiblePair; import org.springframework.data.convert.CustomConversions; @@ -38,7 +36,8 @@ import org.springframework.data.jdbc.core.mapping.JdbcSimpleTypes; */ public class JdbcCustomConversions extends CustomConversions { - private static final Collection STORE_CONVERTERS = Collections.unmodifiableCollection(Jsr310TimestampBasedConverters.getConvertersToRegister()); + private static final Collection STORE_CONVERTERS = Collections + .unmodifiableCollection(Jsr310TimestampBasedConverters.getConvertersToRegister()); private static final StoreConversions STORE_CONVERSIONS = StoreConversions.of(JdbcSimpleTypes.HOLDER, STORE_CONVERTERS); @@ -50,21 +49,33 @@ public class JdbcCustomConversions extends CustomConversions { } /** - * Create a new {@link JdbcCustomConversions} instance registering the given converters and the default store converters. + * Create a new {@link JdbcCustomConversions} instance registering the given converters and the default store + * converters. * * @param converters must not be {@literal null}. */ public JdbcCustomConversions(List converters) { - super(new ConverterConfiguration(STORE_CONVERSIONS, converters, JdbcCustomConversions::isDateTimeApiConversion)); + + super(new ConverterConfiguration( // + STORE_CONVERSIONS, // + converters, // + JdbcCustomConversions::excludeConversionsBetweenDateAndJsr310Types // + )); } /** - * Create a new {@link JdbcCustomConversions} instance registering the given converters and the default store converters. + * Create a new {@link JdbcCustomConversions} instance registering the given converters and the default store + * converters. * * @since 2.3 */ public JdbcCustomConversions(StoreConversions storeConversions, List userConverters) { - super(new ConverterConfiguration(storeConversions, userConverters, JdbcCustomConversions::isDateTimeApiConversion)); + + super(new ConverterConfiguration( // + storeConversions, // + userConverters, // + JdbcCustomConversions::excludeConversionsBetweenDateAndJsr310Types // + )); } /** @@ -98,6 +109,10 @@ public class JdbcCustomConversions extends CustomConversions { return cp.getSourceType().getTypeName().startsWith("java.time."); } - return true; + return false; + } + + private static boolean excludeConversionsBetweenDateAndJsr310Types(ConvertiblePair cp) { + return !isDateTimeApiConversion(cp); } } 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 a509ad12..a331c818 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 @@ -45,22 +45,21 @@ import org.springframework.lang.NonNull; * @author Jens Schauder * @since 2.2 */ -abstract class Jsr310TimestampBasedConverters { - - private static final List> CLASSES = Arrays.asList(LocalDateTime.class, LocalDate.class, LocalTime.class, - Instant.class, ZoneId.class, Duration.class, Period.class); +public abstract class Jsr310TimestampBasedConverters { /** - * Returns the converters to be registered. Will only return converters in case we're running on Java 8. + * Returns the converters to be registered. * - * @return + * Note that the {@link LocalDateTimeToTimestampConverter} is not included, since many database don't need that conversion. + * Databases that do need it, should include it in the conversions offered by their respective dialect. + * + * @return a collection of converters. Guaranteed to be not {@literal null}. */ public static Collection> getConvertersToRegister() { List> converters = new ArrayList<>(8); converters.add(TimestampToLocalDateTimeConverter.INSTANCE); - converters.add(LocalDateTimeToTimestampConverter.INSTANCE); converters.add(TimestampToLocalDateConverter.INSTANCE); converters.add(LocalDateToTimestampConverter.INSTANCE); converters.add(TimestampToLocalTimeConverter.INSTANCE); 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 d99f9cc3..45a8c58e 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 @@ -23,6 +23,7 @@ import java.util.List; 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; /** @@ -43,6 +44,7 @@ public class JdbcDb2Dialect extends Db2Dialect { List converters = new ArrayList<>(super.getConverters()); converters.add(OffsetDateTimeToTimestampConverter.INSTANCE); + converters.add(Jsr310TimestampBasedConverters.LocalDateTimeToTimestampConverter.INSTANCE); return converters; } diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/JdbcMySqlDialect.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/JdbcMySqlDialect.java index 2dd8a869..07491ce6 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/JdbcMySqlDialect.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/JdbcMySqlDialect.java @@ -15,17 +15,23 @@ */ package org.springframework.data.jdbc.core.dialect; +import static java.time.ZoneId.*; + import java.sql.JDBCType; +import java.time.LocalDateTime; import java.time.OffsetDateTime; import java.util.ArrayList; import java.util.Collection; +import java.util.Date; import org.springframework.core.convert.converter.Converter; +import org.springframework.data.convert.ReadingConverter; import org.springframework.data.convert.WritingConverter; import org.springframework.data.jdbc.core.convert.JdbcValue; import org.springframework.data.relational.core.dialect.Db2Dialect; import org.springframework.data.relational.core.dialect.MySqlDialect; import org.springframework.data.relational.core.sql.IdentifierProcessing; +import org.springframework.lang.NonNull; /** * {@link Db2Dialect} that registers JDBC specific converters. @@ -47,6 +53,7 @@ public class JdbcMySqlDialect extends MySqlDialect { ArrayList converters = new ArrayList<>(super.getConverters()); converters.add(OffsetDateTimeToTimestampJdbcValueConverter.INSTANCE); + converters.add(LocalDateTimeToDateConverter.INSTANCE); return converters; } @@ -61,4 +68,16 @@ public class JdbcMySqlDialect extends MySqlDialect { return JdbcValue.of(source, JDBCType.TIMESTAMP); } } + + @ReadingConverter + enum LocalDateTimeToDateConverter implements Converter { + + INSTANCE; + + @NonNull + @Override + public Date convert(LocalDateTime source) { + return Date.from(source.atZone(systemDefault()).toInstant()); + } + } } diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/AggregateChangeIdGenerationUnitTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/AggregateChangeIdGenerationUnitTests.java index 76555053..1d7086e3 100644 --- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/AggregateChangeIdGenerationUnitTests.java +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/AggregateChangeIdGenerationUnitTests.java @@ -17,6 +17,7 @@ package org.springframework.data.jdbc.core; import static java.util.Collections.*; import static org.assertj.core.api.Assertions.*; +import static org.assertj.core.api.SoftAssertions.*; import static org.mockito.Mockito.*; import java.util.ArrayList; @@ -26,7 +27,6 @@ import java.util.List; import java.util.Map; import java.util.Set; -import org.assertj.core.api.SoftAssertions; import org.junit.jupiter.api.Test; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; @@ -87,7 +87,7 @@ public class AggregateChangeIdGenerationUnitTests { executor.execute(aggregateChange); - SoftAssertions.assertSoftly(softly -> { + assertSoftly(softly -> { softly.assertThat(entity.rootId).isEqualTo(1); softly.assertThat(entity.single.id).isEqualTo(2); @@ -107,7 +107,7 @@ public class AggregateChangeIdGenerationUnitTests { executor.execute(aggregateChange); - SoftAssertions.assertSoftly(softly -> { + assertSoftly(softly -> { softly.assertThat(entity.rootId).isEqualTo(1); softly.assertThat(entity.contentList).extracting(c -> c.id).containsExactly(2, 3); @@ -171,7 +171,7 @@ public class AggregateChangeIdGenerationUnitTests { executor.execute(aggregateChange); - SoftAssertions.assertSoftly(softly -> { + assertSoftly(softly -> { softly.assertThat(entity.rootId).isEqualTo(1); softly.assertThat(entity.single.id).isEqualTo(2); @@ -198,7 +198,7 @@ public class AggregateChangeIdGenerationUnitTests { executor.execute(aggregateChange); - SoftAssertions.assertSoftly(softly -> { + assertSoftly(softly -> { softly.assertThat(entity.rootId).isEqualTo(1); softly.assertThat(entity.contentSet) // @@ -233,7 +233,7 @@ public class AggregateChangeIdGenerationUnitTests { executor.execute(aggregateChange); - SoftAssertions.assertSoftly(softly -> { + assertSoftly(softly -> { softly.assertThat(entity.rootId).isEqualTo(1); softly.assertThat(entity.contentList) // @@ -267,7 +267,7 @@ public class AggregateChangeIdGenerationUnitTests { executor.execute(aggregateChange); - SoftAssertions.assertSoftly(softly -> { + assertSoftly(softly -> { softly.assertThat(entity.rootId).isEqualTo(1); softly.assertThat(entity.contentList) // @@ -305,7 +305,7 @@ public class AggregateChangeIdGenerationUnitTests { executor.execute(aggregateChange); - SoftAssertions.assertSoftly(softly -> { + assertSoftly(softly -> { softly.assertThat(entity.rootId).isEqualTo(1); softly.assertThat(entity.contentMap.entrySet()) // diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/JdbcAggregateTemplateIntegrationTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/JdbcAggregateTemplateIntegrationTests.java index 835ec9db..371c47b9 100644 --- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/JdbcAggregateTemplateIntegrationTests.java +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/JdbcAggregateTemplateIntegrationTests.java @@ -17,6 +17,7 @@ package org.springframework.data.jdbc.core; import static java.util.Collections.*; import static org.assertj.core.api.Assertions.*; +import static org.assertj.core.api.SoftAssertions.*; import static org.springframework.data.jdbc.testing.TestDatabaseFeatures.Feature.*; import static org.springframework.test.context.TestExecutionListeners.MergeMode.*; @@ -664,7 +665,7 @@ public class JdbcAggregateTemplateIntegrationTests { NoIdListChain4 saved = template.save(createNoIdTree()); template.deleteById(saved.four, NoIdListChain4.class); - SoftAssertions.assertSoftly(softly -> { + assertSoftly(softly -> { softly.assertThat(count("NO_ID_LIST_CHAIN4")).describedAs("Chain4 elements got deleted").isEqualTo(0); softly.assertThat(count("NO_ID_LIST_CHAIN3")).describedAs("Chain3 elements got deleted").isEqualTo(0); @@ -691,7 +692,7 @@ public class JdbcAggregateTemplateIntegrationTests { NoIdMapChain4 saved = template.save(createNoIdMapTree()); template.deleteById(saved.four, NoIdMapChain4.class); - SoftAssertions.assertSoftly(softly -> { + assertSoftly(softly -> { softly.assertThat(count("NO_ID_MAP_CHAIN4")).describedAs("Chain4 elements got deleted").isEqualTo(0); softly.assertThat(count("NO_ID_MAP_CHAIN3")).describedAs("Chain3 elements got deleted").isEqualTo(0); 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 1eed43cf..7f21e24b 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 @@ -16,6 +16,7 @@ package org.springframework.data.jdbc.core.convert; import static org.assertj.core.api.Assertions.*; +import static org.assertj.core.api.SoftAssertions.*; import static org.mockito.Mockito.*; import lombok.Data; @@ -26,6 +27,7 @@ import java.time.Instant; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; +import java.time.OffsetDateTime; import java.time.ZoneOffset; import java.time.ZonedDateTime; import java.util.Date; @@ -69,12 +71,14 @@ public class BasicJdbcConverterUnitTests { SoftAssertions softly = new SoftAssertions(); checkTargetType(softly, entity, "someEnum", String.class); - checkTargetType(softly, entity, "localDateTime", Timestamp.class); + 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, "offsetDateTime", OffsetDateTime.class); checkTargetType(softly, entity, "instant", Timestamp.class); checkTargetType(softly, entity, "date", Date.class); - checkTargetType(softly, entity, "zonedDateTime", String.class); + checkTargetType(softly, entity, "timestamp", Timestamp.class); checkTargetType(softly, entity, "uuid", UUID.class); softly.assertAll(); @@ -116,7 +120,7 @@ public class BasicJdbcConverterUnitTests { RelationalPersistentEntity persistentEntity = context.getRequiredPersistentEntity(DummyEntity.class); - SoftAssertions.assertSoftly(softly -> { + assertSoftly(softly -> { LocalDateTime testLocalDateTime = LocalDateTime.of(2001, 2, 3, 4, 5, 6, 123456789); checkConversionToTimestampAndBack(softly, persistentEntity, "localDateTime", testLocalDateTime); checkConversionToTimestampAndBack(softly, persistentEntity, "localDate", LocalDate.of(2001, 2, 3)); @@ -165,9 +169,11 @@ public class BasicJdbcConverterUnitTests { private final LocalDateTime localDateTime; private final LocalDate localDate; private final LocalTime localTime; + private final ZonedDateTime zonedDateTime; + private final OffsetDateTime offsetDateTime; private final Instant instant; private final Date date; - private final ZonedDateTime zonedDateTime; + private final Timestamp timestamp; private final AggregateReference reference; private final UUID uuid; diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/EntityRowMapperUnitTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/EntityRowMapperUnitTests.java index 94a01985..a4cd975c 100644 --- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/EntityRowMapperUnitTests.java +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/EntityRowMapperUnitTests.java @@ -18,6 +18,7 @@ package org.springframework.data.jdbc.core.convert; import static java.util.Arrays.*; import static java.util.Collections.*; import static org.assertj.core.api.Assertions.*; +import static org.assertj.core.api.SoftAssertions.*; import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*; @@ -43,12 +44,10 @@ import java.util.stream.Stream; import javax.naming.OperationNotSupportedException; -import org.assertj.core.api.SoftAssertions; import org.junit.jupiter.api.Test; import org.mockito.ArgumentMatchers; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; - import org.springframework.data.annotation.Id; import org.springframework.data.annotation.PersistenceConstructor; import org.springframework.data.annotation.Transient; @@ -1217,7 +1216,7 @@ public class EntityRowMapperUnitTests { public void assertOn(T result) { - SoftAssertions.assertSoftly(softly -> { + assertSoftly(softly -> { expectations.forEach(expectation -> { softly.assertThat(expectation.extractor.apply(result)).describedAs("From column: " + expectation.sourceColumn) diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/IdentifierUnitTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/IdentifierUnitTests.java index 0eb94fd0..e395b7b5 100644 --- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/IdentifierUnitTests.java +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/IdentifierUnitTests.java @@ -16,6 +16,7 @@ package org.springframework.data.jdbc.core.convert; import static org.assertj.core.api.Assertions.*; +import static org.assertj.core.api.SoftAssertions.*; import static org.springframework.data.relational.core.sql.SqlIdentifier.*; import java.util.ArrayList; @@ -25,7 +26,6 @@ import java.util.List; import java.util.Map; import org.assertj.core.api.Assertions; -import org.assertj.core.api.SoftAssertions; import org.junit.jupiter.api.Test; import org.springframework.data.relational.core.sql.IdentifierProcessing; import org.springframework.data.relational.core.sql.SqlIdentifier; @@ -125,7 +125,7 @@ public class IdentifierUnitTests { Map map = id.toMap(); - SoftAssertions.assertSoftly(softly -> { + assertSoftly(softly -> { softly.assertThat(map.get("aName")).describedAs("aName").isEqualTo("one"); softly.assertThat(map.get("Other")).describedAs("Other").isEqualTo("two"); softly.assertThat(map.get("other")).describedAs("other").isNull(); diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/SqlGeneratorEmbeddedUnitTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/SqlGeneratorEmbeddedUnitTests.java index 8f6ac02c..ec33659f 100644 --- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/SqlGeneratorEmbeddedUnitTests.java +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/SqlGeneratorEmbeddedUnitTests.java @@ -17,8 +17,8 @@ package org.springframework.data.jdbc.core.convert; import static java.util.Collections.*; import static org.assertj.core.api.Assertions.*; +import static org.assertj.core.api.SoftAssertions.*; -import org.assertj.core.api.SoftAssertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; @@ -42,7 +42,7 @@ import org.springframework.lang.Nullable; */ public class SqlGeneratorEmbeddedUnitTests { - private RelationalMappingContext context = new JdbcMappingContext(); + private final RelationalMappingContext context = new JdbcMappingContext(); JdbcConverter converter = new BasicJdbcConverter(context, (identifier, path) -> { throw new UnsupportedOperationException(); }); @@ -63,7 +63,7 @@ public class SqlGeneratorEmbeddedUnitTests { public void findOne() { final String sql = sqlGenerator.getFindOne(); - SoftAssertions.assertSoftly(softly -> { + assertSoftly(softly -> { softly.assertThat(sql).startsWith("SELECT") // .contains("dummy_entity.id1 AS id1") // @@ -86,7 +86,7 @@ public class SqlGeneratorEmbeddedUnitTests { public void findAll() { final String sql = sqlGenerator.getFindAll(); - SoftAssertions.assertSoftly(softly -> { + assertSoftly(softly -> { softly.assertThat(sql).startsWith("SELECT") // .contains("dummy_entity.id1 AS id1") // @@ -109,7 +109,7 @@ public class SqlGeneratorEmbeddedUnitTests { public void findAllInList() { final String sql = sqlGenerator.getFindAllInList(); - SoftAssertions.assertSoftly(softly -> { + assertSoftly(softly -> { softly.assertThat(sql).startsWith("SELECT") // .contains("dummy_entity.id1 AS id1") // @@ -132,7 +132,7 @@ public class SqlGeneratorEmbeddedUnitTests { public void insert() { final String sql = sqlGenerator.getInsert(emptySet()); - SoftAssertions.assertSoftly(softly -> { + assertSoftly(softly -> { softly.assertThat(sql) // .startsWith("INSERT INTO") // @@ -154,7 +154,7 @@ public class SqlGeneratorEmbeddedUnitTests { public void update() { final String sql = sqlGenerator.getUpdate(); - SoftAssertions.assertSoftly(softly -> { + assertSoftly(softly -> { softly.assertThat(sql) // .startsWith("UPDATE") // @@ -267,7 +267,7 @@ public class SqlGeneratorEmbeddedUnitTests { SqlGenerator.Join join = generateJoin("embedded.other", DummyEntity2.class); - SoftAssertions.assertSoftly(softly -> { + assertSoftly(softly -> { softly.assertThat(join.getJoinTable().getName()).isEqualTo(SqlIdentifier.unquoted("other_entity")); softly.assertThat(join.getJoinColumn().getTable()).isEqualTo(join.getJoinTable()); diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/SqlGeneratorUnitTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/SqlGeneratorUnitTests.java index 01c390a6..d6e89bc7 100644 --- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/SqlGeneratorUnitTests.java +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/SqlGeneratorUnitTests.java @@ -17,6 +17,7 @@ package org.springframework.data.jdbc.core.convert; import static java.util.Collections.*; import static org.assertj.core.api.Assertions.*; +import static org.assertj.core.api.SoftAssertions.*; import static org.springframework.data.relational.core.sql.SqlIdentifier.*; import java.util.Map; @@ -94,7 +95,7 @@ public class SqlGeneratorUnitTests { String sql = sqlGenerator.getFindOne(); - SoftAssertions.assertSoftly(softly -> softly // + assertSoftly(softly -> softly // .assertThat(sql) // .startsWith("SELECT") // .contains("dummy_entity.id1 AS id1,") // @@ -113,7 +114,7 @@ public class SqlGeneratorUnitTests { String sql = sqlGenerator.getAcquireLockById(LockMode.PESSIMISTIC_WRITE); - SoftAssertions.assertSoftly(softly -> softly // + assertSoftly(softly -> softly // .assertThat(sql) // .startsWith("SELECT") // .contains("dummy_entity.id1") // @@ -127,7 +128,7 @@ public class SqlGeneratorUnitTests { String sql = sqlGenerator.getAcquireLockAll(LockMode.PESSIMISTIC_WRITE); - SoftAssertions.assertSoftly(softly -> softly // + assertSoftly(softly -> softly // .assertThat(sql) // .startsWith("SELECT") // .contains("dummy_entity.id1") // @@ -580,7 +581,7 @@ public class SqlGeneratorUnitTests { SqlGenerator.Join join = generateJoin("ref", DummyEntity.class); - SoftAssertions.assertSoftly(softly -> { + assertSoftly(softly -> { softly.assertThat(join.getJoinTable().getName()).isEqualTo(SqlIdentifier.quoted("REFERENCED_ENTITY")); softly.assertThat(join.getJoinColumn().getTable()).isEqualTo(join.getJoinTable()); @@ -612,7 +613,7 @@ public class SqlGeneratorUnitTests { SqlGenerator.Join join = generateJoin("ref.further", DummyEntity.class); - SoftAssertions.assertSoftly(softly -> { + assertSoftly(softly -> { softly.assertThat(join.getJoinTable().getName()) .isEqualTo(SqlIdentifier.quoted("SECOND_LEVEL_REFERENCED_ENTITY")); @@ -629,7 +630,7 @@ public class SqlGeneratorUnitTests { SqlGenerator.Join join = generateJoin("child", ParentOfNoIdChild.class); Table joinTable = join.getJoinTable(); - SoftAssertions.assertSoftly(softly -> { + assertSoftly(softly -> { softly.assertThat(joinTable.getName()).isEqualTo(SqlIdentifier.quoted("NO_ID_CHILD")); softly.assertThat(joinTable).isInstanceOf(Aliased.class); diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/SqlIdentifierParameterSourceUnitTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/SqlIdentifierParameterSourceUnitTests.java index 88b58024..bf3ba897 100644 --- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/SqlIdentifierParameterSourceUnitTests.java +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/SqlIdentifierParameterSourceUnitTests.java @@ -15,7 +15,8 @@ */ package org.springframework.data.jdbc.core.convert; -import org.assertj.core.api.SoftAssertions; +import static org.assertj.core.api.SoftAssertions.*; + import org.junit.jupiter.api.Test; import org.springframework.data.relational.core.sql.IdentifierProcessing; import org.springframework.data.relational.core.sql.SqlIdentifier; @@ -34,7 +35,7 @@ public class SqlIdentifierParameterSourceUnitTests { SqlIdentifierParameterSource parameters = new SqlIdentifierParameterSource(identifierProcessing); - SoftAssertions.assertSoftly(softly -> { + assertSoftly(softly -> { softly.assertThat(parameters.getParameterNames()).isEmpty(); softly.assertThat(parameters.getValue("blah")).isNull(); @@ -50,7 +51,7 @@ public class SqlIdentifierParameterSourceUnitTests { parameters.addValue(SqlIdentifier.unquoted("key"), 23); - SoftAssertions.assertSoftly(softly -> { + assertSoftly(softly -> { softly.assertThat(parameters.getParameterNames()).isEqualTo(new String[] { "key" }); softly.assertThat(parameters.getValue("key")).isEqualTo(23); @@ -69,7 +70,7 @@ public class SqlIdentifierParameterSourceUnitTests { parameters.addValue(SqlIdentifier.unquoted("key"), 23, 42); - SoftAssertions.assertSoftly(softly -> { + assertSoftly(softly -> { softly.assertThat(parameters.getParameterNames()).isEqualTo(new String[] { "key" }); softly.assertThat(parameters.getValue("key")).isEqualTo(23); @@ -95,7 +96,7 @@ public class SqlIdentifierParameterSourceUnitTests { parameters.addAll(parameters2); - SoftAssertions.assertSoftly(softly -> { + assertSoftly(softly -> { softly.assertThat(parameters.getParameterNames()).isEqualTo(new String[] { "key1", "key2", "key3" }); softly.assertThat(parameters.getValue("key1")).isEqualTo(111); diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/dialect/JdbcDb2DialectUnitTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/dialect/JdbcDb2DialectUnitTests.java new file mode 100644 index 00000000..116d458d --- /dev/null +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/dialect/JdbcDb2DialectUnitTests.java @@ -0,0 +1,46 @@ +/* + * Copyright 2021 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.jdbc.core.dialect; + +import static org.assertj.core.api.SoftAssertions.*; + +import java.sql.Timestamp; +import java.time.LocalDateTime; +import java.time.OffsetDateTime; +import java.util.List; + +import org.junit.jupiter.api.Test; +import org.springframework.data.jdbc.core.convert.JdbcCustomConversions; + +/** + * Tests for {@link JdbcMySqlDialect}. + * + * @author Jens Schauder + */ +class JdbcDb2DialectUnitTests { + + @Test // #974 + void testCustomConversions() { + + JdbcCustomConversions customConversions = new JdbcCustomConversions( + (List) JdbcDb2Dialect.INSTANCE.getConverters()); + + assertSoftly(softly -> { + softly.assertThat(customConversions.getCustomWriteTarget(LocalDateTime.class)).contains(Timestamp.class); + softly.assertThat(customConversions.getCustomWriteTarget(OffsetDateTime.class)).contains(Timestamp.class); + }); + } +} diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/dialect/JdbcMySqlDialectUnitTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/dialect/JdbcMySqlDialectUnitTests.java new file mode 100644 index 00000000..26c0e687 --- /dev/null +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/dialect/JdbcMySqlDialectUnitTests.java @@ -0,0 +1,47 @@ +/* + * Copyright 2021 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.jdbc.core.dialect; + +import static org.assertj.core.api.SoftAssertions.*; + +import java.time.LocalDateTime; +import java.time.OffsetDateTime; +import java.util.List; + +import org.junit.jupiter.api.Test; +import org.springframework.data.jdbc.core.convert.JdbcCustomConversions; +import org.springframework.data.jdbc.core.convert.JdbcValue; + +/** + * Tests for {@link JdbcMySqlDialect}. + * + * @author Jens Schauder + */ +class JdbcMySqlDialectUnitTests { + + @Test // #974 + void testCustomConversions() { + + JdbcCustomConversions customConversions = new JdbcCustomConversions( + (List) new JdbcMySqlDialect().getConverters()); + + assertSoftly(softly -> { + + softly.assertThat(customConversions.getCustomWriteTarget(LocalDateTime.class)).isEmpty(); + softly.assertThat(customConversions.getCustomWriteTarget(OffsetDateTime.class)).contains(JdbcValue.class); + }); + } +} diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/mapping/BasicJdbcPersistentPropertyUnitTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/mapping/BasicJdbcPersistentPropertyUnitTests.java index 7a566a84..8d613ee5 100644 --- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/mapping/BasicJdbcPersistentPropertyUnitTests.java +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/mapping/BasicJdbcPersistentPropertyUnitTests.java @@ -16,6 +16,7 @@ package org.springframework.data.jdbc.core.mapping; import static org.assertj.core.api.Assertions.*; +import static org.assertj.core.api.SoftAssertions.*; import static org.springframework.data.relational.core.sql.SqlIdentifier.*; import junit.framework.AssertionFailedError; @@ -27,7 +28,6 @@ import java.util.Date; import java.util.List; import java.util.UUID; -import org.assertj.core.api.SoftAssertions; import org.junit.jupiter.api.Test; import org.springframework.data.annotation.Id; import org.springframework.data.mapping.PersistentPropertyPath; @@ -101,7 +101,7 @@ public class BasicJdbcPersistentPropertyUnitTests { RelationalPersistentEntity entity = context.getRequiredPersistentEntity(DummyEntity.class); - SoftAssertions.assertSoftly(softly -> { + assertSoftly(softly -> { softly.assertThat(entity.getRequiredPersistentProperty("reference").isAssociation()) // .as("reference") // diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/mybatis/MyBatisContextUnitTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/mybatis/MyBatisContextUnitTests.java index aa474739..322dfa7f 100644 --- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/mybatis/MyBatisContextUnitTests.java +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/mybatis/MyBatisContextUnitTests.java @@ -15,10 +15,11 @@ */ package org.springframework.data.jdbc.mybatis; +import static org.assertj.core.api.SoftAssertions.*; + import java.util.HashMap; import java.util.Map; -import org.assertj.core.api.SoftAssertions; import org.junit.jupiter.api.Test; import org.springframework.data.jdbc.core.convert.Identifier; import org.springframework.data.relational.core.sql.SqlIdentifier; @@ -36,7 +37,7 @@ public class MyBatisContextUnitTests { MyBatisContext context = new MyBatisContext(Identifier.from(map), null, null); - SoftAssertions.assertSoftly(softly -> { + assertSoftly(softly -> { softly.assertThat(context.get("one")).isEqualTo("oneValue"); softly.assertThat(context.get("two")).isEqualTo("twoValue"); 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 00e9fd80..790154e7 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 @@ -17,13 +17,13 @@ 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.assertj.core.api.SoftAssertions; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; @@ -130,7 +130,7 @@ public class JdbcRepositoryCustomConversionIntegrationTests { EntityWithStringyBigDecimal reloaded = repository.findById(entity.id).get(); // loading the number from the database might result in additional zeros at the end. - SoftAssertions.assertSoftly(softly -> { + assertSoftly(softly -> { String stringyNumber = reloaded.stringyNumber; softly.assertThat(stringyNumber).startsWith(entity.stringyNumber); softly.assertThat(stringyNumber.substring(entity.stringyNumber.length())).matches("0*"); 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 54497a5d..4b878d6c 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 @@ -27,6 +27,7 @@ import lombok.Value; import java.io.IOException; import java.sql.ResultSet; import java.time.Instant; +import java.time.LocalDateTime; import java.time.OffsetDateTime; import java.time.ZoneOffset; import java.util.ArrayList; @@ -49,6 +50,7 @@ import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Slice; +import org.springframework.data.jdbc.repository.query.Modifying; import org.springframework.data.jdbc.repository.query.Query; import org.springframework.data.jdbc.repository.support.JdbcRepositoryFactory; import org.springframework.data.jdbc.testing.AssumeFeatureTestExecutionListener; @@ -492,6 +494,13 @@ public class JdbcRepositoryIntegrationTests { assertThat(result.getContent().get(0).getName()).isEqualTo("Entity Name"); } + @Test // #974 + @EnabledOnFeature(TestDatabaseFeatures.Feature.IS_POSTGRES) + void intervalCalculation() { + + repository.updateWithIntervalCalculation(23L, LocalDateTime.now()); + } + private Instant createDummyBeforeAndAfterNow() { Instant now = Instant.now(); @@ -557,6 +566,10 @@ public class JdbcRepositoryIntegrationTests { @Query("SELECT * FROM DUMMY_ENTITY WHERE OFFSET_DATE_TIME > :threshhold") List findByOffsetDateTime(@Param("threshhold") OffsetDateTime threshhold); + + @Modifying + @Query("UPDATE dummy_entity SET point_in_time = :start - interval '30 minutes' WHERE id_prop = :id") + void updateWithIntervalCalculation(@Param("id") Long id, @Param("start") LocalDateTime start); } @Configuration diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/query/QueryAnnotationHsqlIntegrationTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/query/QueryAnnotationHsqlIntegrationTests.java index f6abbbe7..b4acc943 100644 --- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/query/QueryAnnotationHsqlIntegrationTests.java +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/query/QueryAnnotationHsqlIntegrationTests.java @@ -16,6 +16,7 @@ package org.springframework.data.jdbc.repository.query; import static org.assertj.core.api.Assertions.*; +import static org.assertj.core.api.SoftAssertions.*; import lombok.Value; @@ -25,7 +26,6 @@ import java.util.List; import java.util.Optional; import java.util.stream.Stream; -import org.assertj.core.api.SoftAssertions; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; @@ -192,7 +192,7 @@ public class QueryAnnotationHsqlIntegrationTests { repository.save(dummyEntity("bbb")); repository.save(dummyEntity("cac")); - SoftAssertions.assertSoftly(softly -> { + assertSoftly(softly -> { softly.assertThat(repository.existsByNameContaining("a")).describedAs("entities with A in the name").isTrue(); softly.assertThat(repository.existsByNameContaining("d")).describedAs("entities with D in the name").isFalse(); diff --git a/spring-data-relational/src/test/java/org/springframework/data/relational/core/mapping/DerivedSqlIdentifierUnitTests.java b/spring-data-relational/src/test/java/org/springframework/data/relational/core/mapping/DerivedSqlIdentifierUnitTests.java index e001f3bf..45788a96 100644 --- a/spring-data-relational/src/test/java/org/springframework/data/relational/core/mapping/DerivedSqlIdentifierUnitTests.java +++ b/spring-data-relational/src/test/java/org/springframework/data/relational/core/mapping/DerivedSqlIdentifierUnitTests.java @@ -16,10 +16,9 @@ package org.springframework.data.relational.core.mapping; import static org.assertj.core.api.Assertions.*; +import static org.assertj.core.api.SoftAssertions.*; -import org.assertj.core.api.SoftAssertions; import org.junit.jupiter.api.Test; - import org.springframework.data.relational.core.sql.IdentifierProcessing; import org.springframework.data.relational.core.sql.IdentifierProcessing.LetterCasing; import org.springframework.data.relational.core.sql.IdentifierProcessing.Quoting; @@ -75,7 +74,7 @@ public class DerivedSqlIdentifierUnitTests { SqlIdentifier notSimple = SqlIdentifier.from(new DerivedSqlIdentifier("simple", false), new DerivedSqlIdentifier("not", false)); - SoftAssertions.assertSoftly(softly -> { + assertSoftly(softly -> { softly.assertThat(basis).isEqualTo(equal); softly.assertThat(equal).isEqualTo(basis); diff --git a/spring-data-relational/src/test/java/org/springframework/data/relational/core/sql/SqlIdentifierUnitTests.java b/spring-data-relational/src/test/java/org/springframework/data/relational/core/sql/SqlIdentifierUnitTests.java index 8eadf7c0..a01bf854 100644 --- a/spring-data-relational/src/test/java/org/springframework/data/relational/core/sql/SqlIdentifierUnitTests.java +++ b/spring-data-relational/src/test/java/org/springframework/data/relational/core/sql/SqlIdentifierUnitTests.java @@ -16,11 +16,10 @@ package org.springframework.data.relational.core.sql; import static org.assertj.core.api.Assertions.*; +import static org.assertj.core.api.SoftAssertions.*; import static org.springframework.data.relational.core.sql.SqlIdentifier.*; -import org.assertj.core.api.SoftAssertions; import org.junit.jupiter.api.Test; - import org.springframework.data.relational.core.sql.IdentifierProcessing.LetterCasing; import org.springframework.data.relational.core.sql.IdentifierProcessing.Quoting; @@ -80,7 +79,7 @@ public class SqlIdentifierUnitTests { SqlIdentifier quoted = quoted("simple"); SqlIdentifier notSimple = SqlIdentifier.from(unquoted("simple"), unquoted("not")); - SoftAssertions.assertSoftly(softly -> { + assertSoftly(softly -> { softly.assertThat(basis).isEqualTo(equal); softly.assertThat(equal).isEqualTo(basis);