diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoConfigurationSupport.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoConfigurationSupport.java index b3c771bc4..445c4ef79 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoConfigurationSupport.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoConfigurationSupport.java @@ -95,15 +95,16 @@ public abstract class MongoConfigurationSupport { * Register custom {@link Converter}s in a {@link CustomConversions} object if required. These * {@link CustomConversions} will be registered with the * {@link org.springframework.data.mongodb.core.convert.MappingMongoConverter} and {@link #mongoMappingContext()}. - * Returns an empty {@link MongoCustomConversions} instance by default.
- * NOTE: Use {@link #customConversionsConfiguration(MongoConverterConfigurationAdapter)} to configure - * MongoDB native simple types and register custom {@link Converter converters}. + * Returns an empty {@link MongoCustomConversions} instance by default. + *

+ * NOTE: Use {@link #configureConverters(MongoConverterConfigurationAdapter)} to configure MongoDB + * native simple types and register custom {@link Converter converters}. * * @return must not be {@literal null}. */ @Bean public CustomConversions customConversions() { - return new MongoCustomConversions(this::customConversionsConfiguration); + return MongoCustomConversions.create(this::configureConverters); } /** @@ -111,17 +112,11 @@ public abstract class MongoConfigurationSupport { * * @param converterConfigurationAdapter never {@literal null}. * @since 2.3 + * @see MongoConverterConfigurationAdapter#useNativeDriverJavaTimeCodecs() + * @see MongoConverterConfigurationAdapter#useSpringDataJavaTimeCodecs() */ - protected void customConversionsConfiguration(MongoConverterConfigurationAdapter converterConfigurationAdapter) { + protected void configureConverters(MongoConverterConfigurationAdapter converterConfigurationAdapter) { - /* - * In case you want to use the MongoDB Java Driver native Codecs for java.time types instead of the converters SpringData - * ships with, then you may want to call the following here. - * - * converterConfigurationAdapter.useNativeDriverJavaTimeCodecs() - * - * But please, be careful! LocalDate, LocalTime and LocalDateTime will be stored with different values by doing so. - */ } /** diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoCustomConversions.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoCustomConversions.java index ff9023a4f..aa96f9cab 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoCustomConversions.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoCustomConversions.java @@ -31,13 +31,11 @@ import java.util.List; import java.util.Locale; import java.util.Set; import java.util.function.Consumer; -import java.util.stream.Collectors; import org.springframework.core.convert.TypeDescriptor; import org.springframework.core.convert.converter.Converter; import org.springframework.core.convert.converter.ConverterFactory; import org.springframework.core.convert.converter.GenericConverter; -import org.springframework.core.convert.converter.GenericConverter.ConvertiblePair; import org.springframework.data.convert.JodaTimeConverters; import org.springframework.data.convert.WritingConverter; import org.springframework.data.mapping.model.SimpleTypeHolder; @@ -87,12 +85,17 @@ public class MongoCustomConversions extends org.springframework.data.convert.Cus * @param converters must not be {@literal null}. */ public MongoCustomConversions(List converters) { + this(MongoConverterConfigurationAdapter.from(converters)); + } - this(converterConfigurationAdapter -> { - - converterConfigurationAdapter.useSpringDataJavaTimeCodecs(); - converterConfigurationAdapter.registerConverters(converters); - }); + /** + * Create a new {@link MongoCustomConversions} given {@link MongoConverterConfigurationAdapter}. + * + * @param conversionConfiguration must not be {@literal null}. + * @since 2.3 + */ + protected MongoCustomConversions(MongoConverterConfigurationAdapter conversionConfiguration) { + super(conversionConfiguration.createConverterConfiguration()); } /** @@ -100,17 +103,15 @@ public class MongoCustomConversions extends org.springframework.data.convert.Cus * of configuring store specific capabilities by providing deferred hooks to what will be configured when creating the * {@link org.springframework.data.convert.CustomConversions#CustomConversions(ConverterConfiguration) instance}. * - * @param conversionConfiguration must not be {@literal null}. + * @param configurer must not be {@literal null}. * @since 2.3 */ - public MongoCustomConversions(Consumer conversionConfiguration) { + public static MongoCustomConversions create(Consumer configurer) { - super(() -> { + MongoConverterConfigurationAdapter adapter = new MongoConverterConfigurationAdapter(); + configurer.accept(adapter); - MongoConverterConfigurationAdapter adapter = new MongoConverterConfigurationAdapter(); - conversionConfiguration.accept(adapter); - return adapter.createConverterConfiguration(); - }); + return new MongoCustomConversions(adapter); } @WritingConverter @@ -152,14 +153,32 @@ public class MongoCustomConversions extends org.springframework.data.convert.Cus * List of {@literal java.time} types having different representation when rendered via the native * {@link org.bson.codecs.Codec} than the Spring Data {@link Converter}. */ - private static final List> JAVA_DRIVER_TIME_SIMPLE_TYPES = Arrays.asList(LocalDate.class, LocalTime.class, - LocalDateTime.class); + private static final Set> JAVA_DRIVER_TIME_SIMPLE_TYPES = new HashSet<>( + Arrays.asList(LocalDate.class, LocalTime.class, LocalDateTime.class)); private boolean useNativeDriverJavaTimeCodecs = false; - private List customConverters = new ArrayList<>(); + private final List customConverters = new ArrayList<>(); /** - * Set wether or not to use the native MongoDB Java Driver {@link org.bson.codecs.Codec codes} for + * Create a {@link MongoConverterConfigurationAdapter} using the provided {@code converters} and our own codecs for + * JSR-310 types. + * + * @param converters must not be {@literal null}. + * @return + */ + public static MongoConverterConfigurationAdapter from(List converters) { + + Assert.notNull(converters, "Converters must not be null"); + + MongoConverterConfigurationAdapter converterConfigurationAdapter = new MongoConverterConfigurationAdapter(); + converterConfigurationAdapter.useSpringDataJavaTimeCodecs(); + converterConfigurationAdapter.registerConverters(converters); + + return converterConfigurationAdapter; + } + + /** + * Set whether or not to use the native MongoDB Java Driver {@link org.bson.codecs.Codec codes} for * {@link org.bson.codecs.jsr310.LocalDateCodec LocalDate}, {@link org.bson.codecs.jsr310.LocalTimeCodec LocalTime} * and {@link org.bson.codecs.jsr310.LocalDateTimeCodec LocalDateTime} using a {@link ZoneOffset#UTC}. * @@ -232,7 +251,9 @@ public class MongoCustomConversions extends org.springframework.data.convert.Cus */ public MongoConverterConfigurationAdapter registerConverters(Collection converters) { + Assert.notNull(converters, "Converters must not be null"); Assert.noNullElements(converters, "Converters must not be null nor contain null values!"); + customConverters.addAll(converters); return this; } @@ -246,23 +267,26 @@ public class MongoCustomConversions extends org.springframework.data.convert.Cus /* * We need to have those converters using UTC as the default ones would go on with the systemDefault. */ - List converters = new ArrayList<>(); + List converters = new ArrayList<>(STORE_CONVERTERS.size() + 3); converters.add(DateToUtcLocalDateConverter.INSTANCE); converters.add(DateToUtcLocalTimeConverter.INSTANCE); converters.add(DateToUtcLocalDateTimeConverter.INSTANCE); converters.addAll(STORE_CONVERTERS); - /* - * Right, good catch! We also need to make sure to remove default writing converters for java.time types. - */ - List skipConverterRegistrationFor = JAVA_DRIVER_TIME_SIMPLE_TYPES.stream() // - .map(it -> new ConvertiblePair(it, Date.class)) // - .collect(Collectors.toList()); // - StoreConversions storeConversions = StoreConversions - .of(new SimpleTypeHolder(new HashSet<>(JAVA_DRIVER_TIME_SIMPLE_TYPES), MongoSimpleTypes.HOLDER), converters); + .of(new SimpleTypeHolder(JAVA_DRIVER_TIME_SIMPLE_TYPES, MongoSimpleTypes.HOLDER), converters); - return new ConverterConfiguration(storeConversions, this.customConverters, skipConverterRegistrationFor); + return new ConverterConfiguration(storeConversions, this.customConverters, convertiblePair -> { + + // Avoid default registrations + + if (JAVA_DRIVER_TIME_SIMPLE_TYPES.contains(convertiblePair.getSourceType()) + && Date.class.isAssignableFrom(convertiblePair.getTargetType())) { + return false; + } + + return true; + }); } private enum DateToUtcLocalDateTimeConverter implements Converter { diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/MappingMongoConverterTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/MappingMongoConverterTests.java index 906bcf1a8..dcad85638 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/MappingMongoConverterTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/MappingMongoConverterTests.java @@ -29,6 +29,7 @@ import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; import java.time.ZoneId; +import java.time.temporal.ChronoUnit; import java.util.Arrays; import java.util.List; @@ -39,6 +40,7 @@ import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.data.annotation.Id; import org.springframework.data.mongodb.MongoDatabaseFactory; import org.springframework.data.mongodb.core.SimpleMongoClientDatabaseFactory; +import org.springframework.data.mongodb.core.convert.MongoCustomConversions.MongoConverterConfigurationAdapter; import org.springframework.data.mongodb.core.mapping.DBRef; import org.springframework.data.mongodb.core.mapping.MongoMappingContext; import org.springframework.data.mongodb.test.util.Client; @@ -125,7 +127,7 @@ public class MappingMongoConverterTests { configureConverterWithNativeJavaTimeCodec(); MongoCollection mongoCollection = client.getDatabase(DATABASE).getCollection("java-time-types"); - Instant now = Instant.now(); + Instant now = Instant.now().truncatedTo(ChronoUnit.MILLIS); WithJavaTimeTypes source = WithJavaTimeTypes.withJavaTimeTypes(now); source.id = "id-1"; @@ -138,9 +140,8 @@ public class MappingMongoConverterTests { void configureConverterWithNativeJavaTimeCodec() { converter = new MappingMongoConverter(dbRefResolver, mappingContext); - converter.setCustomConversions(new MongoCustomConversions(config -> { - config.useNativeDriverJavaTimeCodecs(); - })); + converter.setCustomConversions( + MongoCustomConversions.create(MongoConverterConfigurationAdapter::useNativeDriverJavaTimeCodecs)); converter.afterPropertiesSet(); } diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/MappingMongoConverterUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/MappingMongoConverterUnitTests.java index c4ad84f8c..c46f599f3 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/MappingMongoConverterUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/MappingMongoConverterUnitTests.java @@ -1087,9 +1087,9 @@ public class MappingMongoConverterUnitTests { void configureConverterWithNativeJavaTimeCodec() { converter = new MappingMongoConverter(resolver, mappingContext); - converter.setCustomConversions(new MongoCustomConversions(config -> { - config.useNativeDriverJavaTimeCodecs(); - })); + + converter.setCustomConversions(MongoCustomConversions + .create(MongoCustomConversions.MongoConverterConfigurationAdapter::useNativeDriverJavaTimeCodecs)); converter.afterPropertiesSet(); } diff --git a/src/main/asciidoc/reference/mapping.adoc b/src/main/asciidoc/reference/mapping.adoc index 968000e33..35d7b8387 100644 --- a/src/main/asciidoc/reference/mapping.adoc +++ b/src/main/asciidoc/reference/mapping.adoc @@ -1,3 +1,4 @@ + [[mapping-chapter]] = Mapping @@ -290,10 +291,8 @@ public class GeoSpatialAppConfig extends AbstractMongoClientConfiguration { return "com.bigbank.domain"; } - // the following are optional - @Override - void customConversionsConfiguration(MongoConverterConfigurationAdapter adapter) { + void configureConverters(MongoConverterConfigurationAdapter adapter) { adapter.registerConverter(new org.springframework.data.mongodb.test.PersonReadConverter()); adapter.registerConverter(new org.springframework.data.mongodb.test.PersonWriteConverter()); @@ -310,11 +309,12 @@ public class GeoSpatialAppConfig extends AbstractMongoClientConfiguration { `AbstractMongoClientConfiguration` requires you to implement methods that define a `com.mongodb.client.MongoClient` as well as provide a database name. `AbstractMongoClientConfiguration` also has a method named `getMappingBasePackage(…)` that you can override to tell the converter where to scan for classes annotated with the `@Document` annotation. You can add additional converters to the converter by overriding the `customConversionsConfiguration` method. +MongoDB's native JSR-310 support can be enabled through `MongoConverterConfigurationAdapter.useNativeDriverJavaTimeCodecs()`. Also shown in the preceding example is a `LoggingEventListener`, which logs `MongoMappingEvent` instances that are posted onto Spring's `ApplicationContextEvent` infrastructure. NOTE: `AbstractMongoClientConfiguration` creates a `MongoTemplate` instance and registers it with the container under the name `mongoTemplate`. -Spring's MongoDB namespace lets you enable mapping functionality in XML, as the following example shows: +Spring's MongoDB namespace lets you enable mapping functionality in XML, as the following example shows: .XML schema to configure MongoDB mapping support ====