Polishing.

Reformat code. Tweak documentation wording.

See #3596
Original pull request: #3982.
This commit is contained in:
Mark Paluch
2022-03-21 09:19:59 +01:00
parent 29fb085d8b
commit 8672808222
6 changed files with 63 additions and 57 deletions

View File

@@ -176,8 +176,8 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
Assert.notNull(path, "ObjectPath must not be null"); Assert.notNull(path, "ObjectPath must not be null");
return new ConversionContext(this, conversions, path, this::readDocument, this::readCollectionOrArray, this::readMap, return new ConversionContext(this, conversions, path, this::readDocument, this::readCollectionOrArray,
this::readDBRef, this::getPotentiallyConvertedSimpleRead); this::readMap, this::readDBRef, this::getPotentiallyConvertedSimpleRead);
} }
/** /**
@@ -393,18 +393,18 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
EntityProjection<?, ?> property = returnedTypeDescriptor.findProperty(name); EntityProjection<?, ?> property = returnedTypeDescriptor.findProperty(name);
if (property == null) { if (property == null) {
return new ConversionContext(conversions, path, MappingMongoConverter.this::readDocument, collectionConverter, return new ConversionContext(sourceConverter, conversions, path, MappingMongoConverter.this::readDocument, collectionConverter,
mapConverter, dbRefConverter, elementConverter); mapConverter, dbRefConverter, elementConverter);
} }
return new ProjectingConversionContext(sourceConverter, conversions, path, collectionConverter, mapConverter, dbRefConverter, return new ProjectingConversionContext(sourceConverter, conversions, path, collectionConverter, mapConverter,
elementConverter, property); dbRefConverter, elementConverter, property);
} }
@Override @Override
public ConversionContext withPath(ObjectPath currentPath) { public ConversionContext withPath(ObjectPath currentPath) {
return new ProjectingConversionContext(sourceConverter, conversions, currentPath, collectionConverter, mapConverter, return new ProjectingConversionContext(sourceConverter, conversions, currentPath, collectionConverter,
dbRefConverter, elementConverter, returnedTypeDescriptor); mapConverter, dbRefConverter, elementConverter, returnedTypeDescriptor);
} }
} }
@@ -935,8 +935,9 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
TypeInformation<?> valueType = ClassTypeInformation.from(obj.getClass()); TypeInformation<?> valueType = ClassTypeInformation.from(obj.getClass());
TypeInformation<?> type = prop.getTypeInformation(); TypeInformation<?> type = prop.getTypeInformation();
if(conversions.hasPropertyValueConverter(prop)) { if (conversions.hasPropertyValueConverter(prop)) {
accessor.put(prop, conversions.getPropertyValueConverter(prop).write(obj, new MongoConversionContext(prop, this))); accessor.put(prop,
conversions.getPropertyValueConverter(prop).write(obj, new MongoConversionContext(prop, this)));
return; return;
} }
@@ -1270,8 +1271,9 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
private void writeSimpleInternal(@Nullable Object value, Bson bson, MongoPersistentProperty property) { private void writeSimpleInternal(@Nullable Object value, Bson bson, MongoPersistentProperty property) {
DocumentAccessor accessor = new DocumentAccessor(bson); DocumentAccessor accessor = new DocumentAccessor(bson);
if(conversions.hasPropertyValueConverter(property)) { if (conversions.hasPropertyValueConverter(property)) {
accessor.put(property, conversions.getPropertyValueConverter(property).write(value, new MongoConversionContext(property, this))); accessor.put(property,
conversions.getPropertyValueConverter(property).write(value, new MongoConversionContext(property, this)));
return; return;
} }
@@ -1916,9 +1918,9 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
return null; return null;
} }
if(context.conversions.hasPropertyValueConverter(property)) { if (context.conversions.hasPropertyValueConverter(property)) {
return (T) context.conversions.getPropertyValueConverter(property).read(value,
return (T) context.conversions.getPropertyValueConverter(property).read(value, new MongoConversionContext(property, context.sourceConverter)); new MongoConversionContext(property, context.sourceConverter));
} }
return (T) context.convert(value, property.getTypeInformation()); return (T) context.convert(value, property.getTypeInformation());
@@ -2148,7 +2150,8 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
final ContainerValueConverter<DBRef> dbRefConverter; final ContainerValueConverter<DBRef> dbRefConverter;
final ValueConverter<Object> elementConverter; final ValueConverter<Object> elementConverter;
ConversionContext(MongoConverter sourceConverter, org.springframework.data.convert.CustomConversions customConversions, ObjectPath path, ConversionContext(MongoConverter sourceConverter,
org.springframework.data.convert.CustomConversions customConversions, ObjectPath path,
ContainerValueConverter<Bson> documentConverter, ContainerValueConverter<Collection<?>> collectionConverter, ContainerValueConverter<Bson> documentConverter, ContainerValueConverter<Collection<?>> collectionConverter,
ContainerValueConverter<Bson> mapConverter, ContainerValueConverter<DBRef> dbRefConverter, ContainerValueConverter<Bson> mapConverter, ContainerValueConverter<DBRef> dbRefConverter,
ValueConverter<Object> elementConverter) { ValueConverter<Object> elementConverter) {
@@ -2235,8 +2238,8 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
Assert.notNull(currentPath, "ObjectPath must not be null"); Assert.notNull(currentPath, "ObjectPath must not be null");
return new ConversionContext(sourceConverter, conversions, currentPath, documentConverter, collectionConverter, mapConverter, return new ConversionContext(sourceConverter, conversions, currentPath, documentConverter, collectionConverter,
dbRefConverter, elementConverter); mapConverter, dbRefConverter, elementConverter);
} }
public ObjectPath getPath() { public ObjectPath getPath() {

View File

@@ -50,11 +50,7 @@ public class MongoConversionContext implements ValueConversionContext<MongoPersi
@Override @Override
public <T> T read(@Nullable Object value, TypeInformation<T> target) { public <T> T read(@Nullable Object value, TypeInformation<T> target) {
return value instanceof Bson ? mongoConverter.read(target.getType(), (Bson) value)
if (!(value instanceof Bson)) { : ValueConversionContext.super.read(value, target);
return ValueConversionContext.super.read(value, target);
}
return mongoConverter.read(target.getType(), (Bson) value);
} }
} }

View File

@@ -36,6 +36,7 @@ import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.convert.converter.Converter; import org.springframework.core.convert.converter.Converter;
import org.springframework.core.convert.converter.ConverterFactory; import org.springframework.core.convert.converter.ConverterFactory;
import org.springframework.core.convert.converter.GenericConverter; import org.springframework.core.convert.converter.GenericConverter;
import org.springframework.data.convert.ConverterBuilder;
import org.springframework.data.convert.PropertyValueConversions; import org.springframework.data.convert.PropertyValueConversions;
import org.springframework.data.convert.PropertyValueConverter; import org.springframework.data.convert.PropertyValueConverter;
import org.springframework.data.convert.PropertyValueConverterFactory; import org.springframework.data.convert.PropertyValueConverterFactory;
@@ -263,7 +264,8 @@ public class MongoCustomConversions extends org.springframework.data.convert.Cus
} }
/** /**
* Add {@link Converter converters}, {@link ConverterFactory factories}, ... * Add {@link Converter converters}, {@link ConverterFactory factories}, {@link ConverterBuilder.ConverterAware
* converter-aware objects}, and {@link GenericConverter generic converters}.
* *
* @param converters must not be {@literal null} nor contain {@literal null} values. * @param converters must not be {@literal null} nor contain {@literal null} values.
* @return this. * @return this.
@@ -307,6 +309,7 @@ public class MongoCustomConversions extends org.springframework.data.convert.Cus
*/ */
public MongoConverterConfigurationAdapter setPropertyValueConversions(PropertyValueConversions valueConversions) { public MongoConverterConfigurationAdapter setPropertyValueConversions(PropertyValueConversions valueConversions) {
Assert.notNull(valueConversions, "PropertyValueConversions must not be null");
this.propertyValueConversions = valueConversions; this.propertyValueConversions = valueConversions;
return this; return this;
} }

View File

@@ -18,11 +18,9 @@ package org.springframework.data.mongodb.core.convert;
import org.springframework.data.convert.PropertyValueConverter; import org.springframework.data.convert.PropertyValueConverter;
/** /**
* Pre typed {@link PropertyValueConverter} specific for the Data MongoDB module. * MongoDB-specific {@link PropertyValueConverter} extension.
* *
* @author Christoph Strobl * @author Christoph Strobl
* @since 3.4 * @since 3.4
*/ */
public interface MongoValueConverter<S, T> extends PropertyValueConverter<S, T, MongoConversionContext> { public interface MongoValueConverter<S, T> extends PropertyValueConverter<S, T, MongoConversionContext> {}
}

View File

@@ -5,7 +5,7 @@
== What's New in Spring Data MongoDB 3.4 == What's New in Spring Data MongoDB 3.4
* Find and update ``Document``s via <<mongodb.repositories.queries.update,Repository method>>. * Find and update ``Document``s via <<mongodb.repositories.queries.update,Repository method>>.
* Property specific <<mongo.property-converters, value converters>>. * Property-specific <<mongo.property-converters, value converters>>.
[[new-features.3.3]] [[new-features.3.3]]
== What's New in Spring Data MongoDB 3.3 == What's New in Spring Data MongoDB 3.3

View File

@@ -1,13 +1,13 @@
[[mongo.property-converters]] [[mongo.property-converters]]
== Property Converters - Mapping specific fields == Property Converters - Mapping specific fields
Although to the <<mongo.custom-converters, type based conversion>> already offers means to influence the representation of certain types within the target store it has its limitations when not all potential values of that type should be considered as a conversion targets. While <<mongo.custom-converters, type-based conversion>> already offers ways to influence the conversion and representation of certain types within the target store it has its limitations when only certain values or properties of a particular type should be considered for conversion.
Property based converters allow to specify conversion instructions on a per property basis either declarative, via `@ValueConverter`, or programmatic by registering a `PropertyValueConverter` for a specific field. Property-based converters allow configuring conversion rules on a per-property basis, either declarative, via `@ValueConverter`, or programmatic by registering a `PropertyValueConverter` for a specific property.
A `PropertyValueConverter` is responsible of transforming a given value into its store representation (write) and back (read) as shown in the snippet below. A `PropertyValueConverter` can transform a given value into its store representation (**write**) and back (**read**) as shown in the snippet below.
Please mind the presence of the `ValueConversionContext` providing additional information, such as mapping metadata. The additional `ValueConversionContext` provides additional information, such as mapping metadata and direct `read`/`write` methods.
.PropertyValueConverter .A simple PropertyValueConverter
==== ====
[source,java] [source,java]
---- ----
@@ -26,69 +26,75 @@ class ReversingValueConverter implements PropertyValueConverter<String, String,
---- ----
==== ====
`PropertyValueConverter` instances can be obtained via `CustomConversions#getPropertyValueConverter(...)` delegating to `PropertyValueConversions` typically using a `PropertyValueConverterFactory` to provide the actual converter. `PropertyValueConverter` instances can be obtained via `CustomConversions#getPropertyValueConverter()` delegating to `PropertyValueConversions`, typically using a `PropertyValueConverterFactory` providing the actual converter.
Depending on the applications needs multiple instances of `PropertyValueConverterFactory` can be chained or decorated (eg. for caching). Depending on the applications needs, multiple instances of `PropertyValueConverterFactory` can be chained or decorated, for example to apply caching.
By default a caching implementation is used that is capable of serving types with a default constructor or enum values. By default, a caching implementation is used that is capable of serving types with a default constructor or enum values.
A set of predefined factories is available via `PropertyValueConverterFactory`. A set of predefined factories is available through `PropertyValueConverterFactory` factory methods.
To obtain a `PropertyValueConverter` from an `ApplicationContext` make sure to use the `PropertyValueConverterFactory.beanFactoryAware(...)` factory. Use `PropertyValueConverterFactory.beanFactoryAware(…)` to obtain a `PropertyValueConverter` instances from an `ApplicationContext`.
Changing the default behavior can be done via the `ConverterConfiguration`. You can change the default behavior through `ConverterConfiguration`.
[[mongo.property-converters.declarative]]
=== Declarative Value Converter === Declarative Value Converter
The most straight forward usage of a `PropertyValueConverter` is via the `@ValueConverter` annotation referring to the target converter type. The most straight forward usage of a `PropertyValueConverter` is by annotating properties with the `@ValueConverter` annotation that defines the converter type.
.Declarative PropertyValueConverter .Declarative PropertyValueConverter
==== ====
[source,java] [source,java]
---- ----
public class Person { class Person {
// ...
@ValueConverter(ReversingValueConverter.class) @ValueConverter(ReversingValueConverter.class)
String ssn; String ssn;
} }
---- ----
==== ====
=== Programmatic Value Converter [[mongo.property-converters.programmatic]]
=== Programmatic Value Converter Registration
Following the programmatic approach does not require to put additional annotations on the domain model but registers `PropertyValueConverter` instances for certain paths in a `PropertyValueConverterRegistrar` as shown below. Programmatic registration registers `PropertyValueConverter` instances for properties within an entity model using a `PropertyValueConverterRegistrar` as shown below.
The difference to declarative registration is that programmatic registration happens entirely outside of the entity model.
Such an approach is useful if you cannot or do not want to annotate the entity model.
.Programmatic PropertyValueConverter .Programmatic PropertyValueConverter registration
==== ====
[source,java] [source,java]
---- ----
PropertyValueConverterRegistrar registrar = new PropertyValueConverterRegistrar(); PropertyValueConverterRegistrar registrar = new PropertyValueConverterRegistrar();
registrar.registerConverter(Address.class, "street", new PropertyValueConverter() { ... }); <1> registrar.registerConverter(Address.class, "street", new PropertyValueConverter() { }); <1>
// type safe registration // type safe registration
registrar.registerConverter(Person.class, Person::getSsn()) <2> registrar.registerConverter(Person.class, Person::getSsn()) <2>
.writing(value -> encrypt(value)) .writing(value -> encrypt(value))
.reading(value -> decrypt(value)); .reading(value -> decrypt(value));
---- ----
<1> Register a converter for the field identified by its name. <1> Register a converter for the field identified by its name.
<2> Type safe variant that allows to register a converter and its conversion functions. <2> Type safe variant that allows to register a converter and its conversion functions.
==== ====
[WARNING] [WARNING]
==== ====
Dot notation (eg. `registerConverter(Person.class, "address.street", ...)`) is *not* supported when registering converters. Dot-notation (such as `registerConverter(Person.class, "address.street", …)`) nagivating across properties into subdocuments is *not* supported when registering converters.
==== ====
[[mongo.property-converters.value-conversions]]
=== MongoDB property value conversions === MongoDB property value conversions
The above sections outlined the purpose an overall structure of `PropertyValueConverters`. The above sections outlined the purpose an overall structure of `PropertyValueConverters`.
This section will focus on MongoDB specific aspects. This section will focus on MongoDB specific aspects.
==== MongoValueConverter & MongoConversionContext ==== MongoValueConverter and MongoConversionContext
The `MongoValueConverter` offers a pre typed `PropertyValueConverter` interface leveraging the `MongoConversionContext`. `MongoValueConverter` offers a pre typed `PropertyValueConverter` interface leveraging the `MongoConversionContext`.
==== MongoCustomConversions configuration ==== MongoCustomConversions configuration
`MongoCustomConversions` are by default capable of dealing with declarative value converters depending on the configured `PropertyValueConverterFactory`. `MongoCustomConversions` are by default capable of handling declarative value converters depending on the configured `PropertyValueConverterFactory`.
The `MongoConverterConfigurationAdapter` is there to help set up programmatic value conversions or define the `PropertyValueConverterFactory` to be used. `MongoConverterConfigurationAdapter` is there to help set up programmatic value conversions or define the `PropertyValueConverterFactory` to be used.
.Configuration Sample .Configuration Sample
==== ====
@@ -97,10 +103,10 @@ The `MongoConverterConfigurationAdapter` is there to help set up programmatic va
MongoCustomConversions.create(configurationAdapter -> { MongoCustomConversions.create(configurationAdapter -> {
SimplePropertyValueConversions valueConversions = new SimplePropertyValueConversions(); SimplePropertyValueConversions valueConversions = new SimplePropertyValueConversions();
valueConversions.setConverterFactory(...); valueConversions.setConverterFactory();
valueConversions.setValueConverterRegistry(new PropertyValueConverterRegistrar() valueConversions.setValueConverterRegistry(new PropertyValueConverterRegistrar()
.registerConverter(...) .registerConverter()
.buildRegistry()); .buildRegistry());
configurationAdapter.setPropertyValueConversions(valueConversions); configurationAdapter.setPropertyValueConversions(valueConversions);
}); });