From 7cfac09aa04c62694a02369657eeb5488567df8d Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Fri, 15 May 2020 11:50:24 +0200 Subject: [PATCH] #207 - Skip converter registrations for JSR-310 to java.util.Date. We now prevent converter registrations that enforce a conversion from JSR-310 types to java.util.Date. R2DBC drivers use natively JSR-310 types and some of them don't implement java.util.Date at all. --- .../r2dbc/convert/R2dbcCustomConversions.java | 22 ++++++++-- .../MappingR2dbcConverterUnitTests.java | 12 +++++- .../MySqlR2dbcRepositoryIntegrationTests.java | 43 ++++++++++++++++++- 3 files changed, 71 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/springframework/data/r2dbc/convert/R2dbcCustomConversions.java b/src/main/java/org/springframework/data/r2dbc/convert/R2dbcCustomConversions.java index 5831b25..6d8cb90 100644 --- a/src/main/java/org/springframework/data/r2dbc/convert/R2dbcCustomConversions.java +++ b/src/main/java/org/springframework/data/r2dbc/convert/R2dbcCustomConversions.java @@ -3,6 +3,7 @@ package org.springframework.data.r2dbc.convert; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; +import java.util.Date; import java.util.List; import org.springframework.data.convert.CustomConversions; @@ -40,7 +41,7 @@ public class R2dbcCustomConversions extends CustomConversions { * @param converters must not be {@literal null}. */ public R2dbcCustomConversions(Collection converters) { - super(STORE_CONVERSIONS, appendOverrides(converters)); + super(new R2dbcCustomConversionsConfiguration(STORE_CONVERSIONS, appendOverrides(converters))); } /** @@ -50,14 +51,29 @@ public class R2dbcCustomConversions extends CustomConversions { * @param converters must not be {@literal null}. */ public R2dbcCustomConversions(StoreConversions storeConversions, Collection converters) { - super(storeConversions, appendOverrides(converters)); + super(new R2dbcCustomConversionsConfiguration(storeConversions, appendOverrides(converters))); } - private static Collection appendOverrides(Collection converters) { + private static List appendOverrides(Collection converters) { List objects = new ArrayList<>(converters); objects.addAll(R2dbcConverters.getOverrideConvertersToRegister()); return objects; } + + static class R2dbcCustomConversionsConfiguration extends ConverterConfiguration { + + public R2dbcCustomConversionsConfiguration(StoreConversions storeConversions, List userConverters) { + super(storeConversions, userConverters, convertiblePair -> { + + if (convertiblePair.getSourceType().getName().startsWith("java.time.") + && convertiblePair.getTargetType().equals(Date.class)) { + return false; + } + + return true; + }); + } + } } diff --git a/src/test/java/org/springframework/data/r2dbc/convert/MappingR2dbcConverterUnitTests.java b/src/test/java/org/springframework/data/r2dbc/convert/MappingR2dbcConverterUnitTests.java index e2bfcce..63496b5 100644 --- a/src/test/java/org/springframework/data/r2dbc/convert/MappingR2dbcConverterUnitTests.java +++ b/src/test/java/org/springframework/data/r2dbc/convert/MappingR2dbcConverterUnitTests.java @@ -21,6 +21,8 @@ import static org.mockito.Mockito.*; import io.r2dbc.spi.Row; import lombok.AllArgsConstructor; +import java.time.Instant; +import java.time.LocalDateTime; import java.util.Arrays; import java.util.Collections; import java.util.Map; @@ -61,17 +63,21 @@ public class MappingR2dbcConverterUnitTests { converter = new MappingR2dbcConverter(mappingContext, conversions); } - @Test // gh-61 + @Test // gh-61, gh-207 public void shouldIncludeAllPropertiesInOutboundRow() { OutboundRow row = new OutboundRow(); - converter.write(new Person("id", "Walter", "White"), row); + Instant instant = Instant.now(); + LocalDateTime localDateTime = LocalDateTime.now(); + converter.write(new Person("id", "Walter", "White", instant, localDateTime), row); assertThat(row).containsEntry(SqlIdentifier.unquoted("id"), SettableValue.fromOrEmpty("id", String.class)); assertThat(row).containsEntry(SqlIdentifier.unquoted("firstname"), SettableValue.fromOrEmpty("Walter", String.class)); assertThat(row).containsEntry(SqlIdentifier.unquoted("lastname"), SettableValue.fromOrEmpty("White", String.class)); + assertThat(row).containsEntry(SqlIdentifier.unquoted("instant"), SettableValue.from(instant)); + assertThat(row).containsEntry(SqlIdentifier.unquoted("local_date_time"), SettableValue.from(localDateTime)); } @Test // gh-41 @@ -187,6 +193,8 @@ public class MappingR2dbcConverterUnitTests { static class Person { @Id String id; String firstname, lastname; + Instant instant; + LocalDateTime localDateTime; } @AllArgsConstructor diff --git a/src/test/java/org/springframework/data/r2dbc/repository/MySqlR2dbcRepositoryIntegrationTests.java b/src/test/java/org/springframework/data/r2dbc/repository/MySqlR2dbcRepositoryIntegrationTests.java index 0a03c4c..24e513a 100644 --- a/src/test/java/org/springframework/data/r2dbc/repository/MySqlR2dbcRepositoryIntegrationTests.java +++ b/src/test/java/org/springframework/data/r2dbc/repository/MySqlR2dbcRepositoryIntegrationTests.java @@ -16,23 +16,34 @@ package org.springframework.data.r2dbc.repository; import io.r2dbc.spi.ConnectionFactory; +import lombok.AllArgsConstructor; +import lombok.Data; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; +import reactor.test.StepVerifier; + +import java.time.LocalDateTime; import javax.sql.DataSource; import org.junit.ClassRule; +import org.junit.Test; import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan.Filter; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.FilterType; +import org.springframework.dao.DataAccessException; +import org.springframework.data.annotation.Id; import org.springframework.data.r2dbc.config.AbstractR2dbcConfiguration; import org.springframework.data.r2dbc.repository.config.EnableR2dbcRepositories; import org.springframework.data.r2dbc.repository.support.R2dbcRepositoryFactory; import org.springframework.data.r2dbc.testing.ExternalDatabase; import org.springframework.data.r2dbc.testing.MySqlTestSupport; +import org.springframework.data.repository.reactive.ReactiveCrudRepository; +import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringRunner; @@ -47,9 +58,12 @@ public class MySqlR2dbcRepositoryIntegrationTests extends AbstractR2dbcRepositor @ClassRule public static final ExternalDatabase database = MySqlTestSupport.database(); + @Autowired DateTestsRepository dateTestsRepository; + @Configuration @EnableR2dbcRepositories(considerNestedRepositories = true, - includeFilters = @Filter(classes = MySqlLegoSetRepository.class, type = FilterType.ASSIGNABLE_TYPE)) + includeFilters = { @Filter(classes = MySqlLegoSetRepository.class, type = FilterType.ASSIGNABLE_TYPE), + @Filter(classes = DateTestsRepository.class, type = FilterType.ASSIGNABLE_TYPE) }) static class IntegrationTestConfiguration extends AbstractR2dbcConfiguration { @Bean @@ -79,6 +93,29 @@ public class MySqlR2dbcRepositoryIntegrationTests extends AbstractR2dbcRepositor return MySqlLegoSetRepository.class; } + @Test + public void shouldUserJsr310Types() { + + JdbcTemplate jdbcTemplate = createJdbcTemplate(createDataSource()); + + try { + jdbcTemplate.execute("DROP TABLE date_tests"); + } catch (DataAccessException e) {} + + jdbcTemplate.execute("CREATE TABLE date_tests (id int, created_timestamp TIMESTAMP, created_date datetime);"); + + dateTestsRepository.save(new DateTests(null, LocalDateTime.now(), LocalDateTime.now())).as(StepVerifier::create) + .expectNextCount(1).verifyComplete(); + } + + @Data + @AllArgsConstructor + static class DateTests { + @Id Integer id; + LocalDateTime createdTimestamp; + LocalDateTime createdDate; + } + interface MySqlLegoSetRepository extends LegoSetRepository { @Override @@ -93,4 +130,8 @@ public class MySqlR2dbcRepositoryIntegrationTests extends AbstractR2dbcRepositor @Query("SELECT id FROM legoset") Flux findAllIds(); } + + interface DateTestsRepository extends ReactiveCrudRepository { + + } }