Polishing.

Increase visibility of converter builders.

Refine generics naming towards DV/SV instead of A/B to easier identify store-native values and domains-specific values in the API. Refactor PropertyValueConversions.hasValueConverter into a non-default method.

Tweak documentation.

See #1484
Original pull request: #2566.
This commit is contained in:
Mark Paluch
2022-03-04 09:13:37 +01:00
parent 228431534f
commit dda102ad7f
17 changed files with 156 additions and 381 deletions

View File

@@ -55,7 +55,7 @@ import org.springframework.util.ObjectUtils;
* Value object to capture custom conversion. That is essentially a {@link List} of converters and some additional logic
* around them. The converters build up two sets of types which store-specific basic types can be converted into and
* from. These types will be considered simple ones (which means they neither need deeper inspection nor nested
* conversion. Thus the {@link CustomConversions} also act as factory for {@link SimpleTypeHolder} .
* conversion. Thus, the {@link CustomConversions} also act as factory for {@link SimpleTypeHolder} .
*
* @author Oliver Gierke
* @author Thomas Darimont
@@ -193,22 +193,23 @@ public class CustomConversions {
* @since 2.7
*/
public boolean hasPropertyValueConverter(PersistentProperty<?> property) {
return propertyValueConversions != null ? propertyValueConversions.hasValueConverter(property) : false;
return propertyValueConversions != null && propertyValueConversions.hasValueConverter(property);
}
/**
* Delegate to obtain the {@link PropertyValueConverter} for the given {@literal property} from
* {@link PropertyValueConversions}.
*
* @param property must not be {@literal null}. param <A> domain specific type
* @param <B> store native type
* @param property must not be {@literal null}.
* @param <DV> domain-specific type
* @param <SV> store-native type
* @param <C> conversion context type
* @return the suitable {@link PropertyValueConverter} or {@literal null} if none available.
* @see PropertyValueConversions#getValueConverter(PersistentProperty)
* @since 2.7
*/
@Nullable
public <A, B, C extends PersistentProperty<C>, D extends ValueConversionContext<C>> PropertyValueConverter<A, B, D> getPropertyValueConverter(
public <DV, SV, C extends PersistentProperty<C>, VCC extends ValueConversionContext<C>> PropertyValueConverter<DV, SV, VCC> getPropertyValueConverter(
C property) {
return propertyValueConversions != null ? propertyValueConversions.getValueConverter(property) : null;
}

View File

@@ -18,7 +18,6 @@ package org.springframework.data.convert;
import java.util.function.Consumer;
import org.springframework.data.mapping.PersistentProperty;
import org.springframework.lang.Nullable;
/**
* {@link PropertyValueConversions} provides access to {@link PropertyValueConverter converters} that may only be
@@ -38,26 +37,25 @@ public interface PropertyValueConversions {
* @param property must not be {@literal null}.
* @return {@literal true} if a specific {@link PropertyValueConverter} is available.
*/
default boolean hasValueConverter(PersistentProperty<?> property) {
return getValueConverter((PersistentProperty) property) != null;
}
boolean hasValueConverter(PersistentProperty<?> property);
/**
* Get the {@link PropertyValueConverter} for the given {@literal property} if present.
* Get the {@link PropertyValueConverter} for the given {@literal property}.
*
* @param property must not be {@literal null}. param <A> domain specific type
* @param <B> store native type
* @param property must not be {@literal null}.
* @param <DV> domain-specific type
* @param <SV> store-native type
* @param <C> conversion context type
* @return the suitable {@link PropertyValueConverter} or {@literal null} if none available.
* @return the suitable {@link PropertyValueConverter}.
*/
@Nullable
<A, B, C extends PersistentProperty<C>, D extends ValueConversionContext<C>> PropertyValueConverter<A, B, D> getValueConverter(
<DV, SV, C extends PersistentProperty<C>, VCC extends ValueConversionContext<C>> PropertyValueConverter<DV, SV, VCC> getValueConverter(
C property);
/**
* Helper that allows to create {@link PropertyValueConversions} instance with the configured
* {@link PropertyValueConverter converters} provided via the given callback.
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
static <P extends PersistentProperty<P>> PropertyValueConversions simple(
Consumer<PropertyValueConverterRegistrar<P>> config) {

View File

@@ -21,22 +21,22 @@ import org.springframework.data.mapping.PersistentProperty;
import org.springframework.lang.Nullable;
/**
* {@link PropertyValueConverter} provides a symmetric way of converting certain properties from domain to store
* specific values.
* {@link PropertyValueConverter} provides a symmetric way of converting certain properties from domain to
* store-specific values.
* <p>
* A {@link PropertyValueConverter} is, other than a {@link ReadingConverter} or {@link WritingConverter}, only applied
* to special annotated fields which allows a fine grained conversion of certain values within a specific context.
* to special annotated fields which allows a fine-grained conversion of certain values within a specific context.
*
* @author Christoph Strobl
* @param <A> domain specific type.
* @param <B> store native type.
* @param <DV> domain-specific type.
* @param <SV> store-native type.
* @param <C> the store specific {@link ValueConversionContext conversion context}.
* @since 2.7
*/
public interface PropertyValueConverter<A, B, C extends ValueConversionContext<? extends PersistentProperty<?>>> {
public interface PropertyValueConverter<DV, SV, C extends ValueConversionContext<? extends PersistentProperty<?>>> {
/**
* Convert the given store specific value into it's domain value representation. Typically a {@literal read}
* Convert the given store specific value into it's domain value representation. Typically, a {@literal read}
* operation.
*
* @param value can be {@literal null}.
@@ -44,10 +44,10 @@ public interface PropertyValueConverter<A, B, C extends ValueConversionContext<?
* @return the converted value. Can be {@literal null}.
*/
@Nullable
A read(@Nullable B value, C context);
DV read(@Nullable SV value, C context);
/**
* Convert the given domain specific value into it's native store representation. Typically a {@literal write}
* Convert the given domain-specific value into it's native store representation. Typically, a {@literal write}
* operation.
*
* @param value can be {@literal null}.
@@ -55,10 +55,10 @@ public interface PropertyValueConverter<A, B, C extends ValueConversionContext<?
* @return the converted value. Can be {@literal null}.
*/
@Nullable
B write(@Nullable A value, C context);
SV write(@Nullable DV value, C context);
/**
* NoOp {@link PropertyValueConverter} implementation.
* No-op {@link PropertyValueConverter} implementation.
*
* @author Christoph Strobl
*/
@@ -67,13 +67,15 @@ public interface PropertyValueConverter<A, B, C extends ValueConversionContext<?
INSTANCE;
@Nullable
@Override
public Object read(Object value, ValueConversionContext context) {
public Object read(@Nullable Object value, ValueConversionContext context) {
return value;
}
@Nullable
@Override
public Object write(Object value, ValueConversionContext context) {
public Object write(@Nullable Object value, ValueConversionContext context) {
return value;
}
}
@@ -83,26 +85,28 @@ public interface PropertyValueConverter<A, B, C extends ValueConversionContext<?
*
* @author Oliver Drotbohm
*/
class FunctionPropertyValueConverter<A, B, P extends PersistentProperty<P>>
implements PropertyValueConverter<A, B, ValueConversionContext<P>> {
class FunctionPropertyValueConverter<DV, SV, P extends PersistentProperty<P>>
implements PropertyValueConverter<DV, SV, ValueConversionContext<P>> {
private final BiFunction<A, ValueConversionContext<P>, B> writer;
private final BiFunction<B, ValueConversionContext<P>, A> reader;
private final BiFunction<DV, ValueConversionContext<P>, SV> writer;
private final BiFunction<SV, ValueConversionContext<P>, DV> reader;
public FunctionPropertyValueConverter(BiFunction<A, ValueConversionContext<P>, B> writer,
BiFunction<B, ValueConversionContext<P>, A> reader) {
public FunctionPropertyValueConverter(BiFunction<DV, ValueConversionContext<P>, SV> writer,
BiFunction<SV, ValueConversionContext<P>, DV> reader) {
this.writer = writer;
this.reader = reader;
}
@Nullable
@Override
public B write(A value, ValueConversionContext<P> context) {
public SV write(@Nullable DV value, ValueConversionContext<P> context) {
return writer.apply(value, context);
}
@Nullable
@Override
public A read(B value, ValueConversionContext<P> context) {
public DV read(@Nullable SV value, ValueConversionContext<P> context) {
return reader.apply(value, context);
}
}

View File

@@ -18,15 +18,14 @@ package org.springframework.data.convert;
import java.util.Arrays;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.data.mapping.PersistentProperty;
import org.springframework.lang.Nullable;
@@ -120,29 +119,26 @@ final class PropertyValueConverterFactories {
}
@Override
public <S, T, C extends ValueConversionContext<?>> PropertyValueConverter<S, T, C> getConverter(
Class<? extends PropertyValueConverter<S, T, C>> converterType) {
public <DV, SV, C extends ValueConversionContext<?>> PropertyValueConverter<DV, SV, C> getConverter(
Class<? extends PropertyValueConverter<DV, SV, C>> converterType) {
Assert.state(beanFactory != null, "BeanFactory must not be null. Did you forget to set it!");
Assert.notNull(converterType, "ConverterType must not be null!");
try {
return beanFactory.getBean(converterType);
} catch (NoSuchBeanDefinitionException exception) {
PropertyValueConverter<DV, SV, C> converter = beanFactory.getBeanProvider(converterType).getIfAvailable();
if (beanFactory instanceof AutowireCapableBeanFactory) {
return (PropertyValueConverter<S, T, C>) ((AutowireCapableBeanFactory) beanFactory).createBean(converterType,
AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR, false);
}
if (converter == null && beanFactory instanceof AutowireCapableBeanFactory) {
return (PropertyValueConverter<DV, SV, C>) ((AutowireCapableBeanFactory) beanFactory).createBean(converterType,
AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR, false);
}
return null;
return converter;
}
}
/**
* {@link PropertyValueConverterFactory} implementation that serves {@link PropertyValueConverter} from a given
* {@link ValueConverterRegistry registry}.
*
*
* @author Christoph Strobl
* @since 2.7
*/
@@ -158,9 +154,9 @@ final class PropertyValueConverterFactories {
@Nullable
@Override
public <A, B, C extends ValueConversionContext<?>> PropertyValueConverter<A, B, C> getConverter(
public <DV, SV, C extends ValueConversionContext<?>> PropertyValueConverter<DV, SV, C> getConverter(
PersistentProperty<?> property) {
return (PropertyValueConverter<A, B, C>) converterRegistry.getConverter(property.getOwner().getType(),
return (PropertyValueConverter<DV, SV, C>) converterRegistry.getConverter(property.getOwner().getType(),
property.getName());
}
@@ -174,7 +170,7 @@ final class PropertyValueConverterFactories {
/**
* {@link PropertyValueConverterFactory} implementation that caches converters provided by an underlying
* {@link PropertyValueConverterFactory factory}.
*
*
* @author Christoph Strobl
* @since 2.7
*/
@@ -191,7 +187,7 @@ final class PropertyValueConverterFactories {
@Nullable
@Override
public <S, T, C extends ValueConversionContext<?>> PropertyValueConverter<S, T, C> getConverter(
public <DV, SV, C extends ValueConversionContext<?>> PropertyValueConverter<DV, SV, C> getConverter(
PersistentProperty<?> property) {
Optional<PropertyValueConverter<?, ?, ? extends ValueConversionContext<?>>> converter = cache.get(property);
@@ -201,8 +197,8 @@ final class PropertyValueConverterFactories {
}
@Override
public <S, T, C extends ValueConversionContext<?>> PropertyValueConverter<S, T, C> getConverter(
Class<? extends PropertyValueConverter<S, T, C>> converterType) {
public <DV, SV, C extends ValueConversionContext<?>> PropertyValueConverter<DV, SV, C> getConverter(
Class<? extends PropertyValueConverter<DV, SV, C>> converterType) {
Optional<PropertyValueConverter<?, ?, ? extends ValueConversionContext<?>>> converter = cache.get(converterType);
@@ -212,8 +208,8 @@ final class PropertyValueConverterFactories {
static class Cache {
Map<PersistentProperty<?>, Optional<PropertyValueConverter<?, ?, ? extends ValueConversionContext<?>>>> perPropertyCache = new HashMap<>();
Map<Class<?>, Optional<PropertyValueConverter<?, ?, ? extends ValueConversionContext<?>>>> typeCache = new HashMap<>();
Map<PersistentProperty<?>, Optional<PropertyValueConverter<?, ?, ? extends ValueConversionContext<?>>>> perPropertyCache = new ConcurrentHashMap<>();
Map<Class<?>, Optional<PropertyValueConverter<?, ?, ? extends ValueConversionContext<?>>>> typeCache = new ConcurrentHashMap<>();
Optional<PropertyValueConverter<?, ?, ? extends ValueConversionContext<?>>> get(PersistentProperty<?> property) {
return perPropertyCache.get(property);
@@ -226,7 +222,12 @@ final class PropertyValueConverterFactories {
<S, T, C extends ValueConversionContext<?>> PropertyValueConverter<S, T, C> cache(PersistentProperty<?> property,
@Nullable PropertyValueConverter<S, T, C> converter) {
perPropertyCache.putIfAbsent(property, Optional.ofNullable(converter));
cache(property.getValueConverterType(), converter);
Class<? extends PropertyValueConverter<?, ?, ? extends ValueConversionContext<? extends PersistentProperty<?>>>> valueConverterType = property
.getValueConverterType();
if (valueConverterType != null) {
cache(valueConverterType, converter);
}
return converter;
}

View File

@@ -44,35 +44,35 @@ public interface PropertyValueConverterFactory {
* Get the {@link PropertyValueConverter} applicable for the given {@link PersistentProperty}.
*
* @param property must not be {@literal null}.
* @param <A> domain specific type.
* @param <B> store native type.
* @param <DV> domain-specific type.
* @param <SV> store-native type.
* @param <C> value conversion context to use.
* @return can be {@literal null}.
*/
@Nullable
@SuppressWarnings("unchecked")
default <A, B, C extends ValueConversionContext<?>> PropertyValueConverter<A, B, C> getConverter(
@Nullable
default <DV, SV, C extends ValueConversionContext<?>> PropertyValueConverter<DV, SV, C> getConverter(
PersistentProperty<?> property) {
if (!property.hasValueConverter()) {
return null;
}
return getConverter((Class<PropertyValueConverter<A, B, C>>) property.getValueConverterType());
return getConverter((Class<PropertyValueConverter<DV, SV, C>>) property.getValueConverterType());
}
/**
* Get the converter by its type.
*
* @param converterType must not be {@literal null}.
* @param <A> domain specific type.
* @param <B> store native type.
* @param <DV> domain-specific type.
* @param <SV> store-native type.
* @param <C> value conversion context to use.
* @return
* @return can be {@literal null}.
*/
@Nullable
<A, B, C extends ValueConversionContext<?>> PropertyValueConverter<A, B, C> getConverter(
Class<? extends PropertyValueConverter<A, B, C>> converterType);
<DV, SV, C extends ValueConversionContext<?>> PropertyValueConverter<DV, SV, C> getConverter(
Class<? extends PropertyValueConverter<DV, SV, C>> converterType);
/**
* Obtain a simple {@link PropertyValueConverterFactory} capable of instantiating {@link PropertyValueConverter}

View File

@@ -29,7 +29,7 @@ import org.springframework.util.Assert;
* that can be used with {@link PropertyValueConversions}.
* <p>
* It is possible to register type safe converters via {@link #registerConverter(Class, Function)}
*
*
* <pre class="code">
* registrar.registerConverter(Person.class, Person::getName) //
* .writing(StringConverter::encrypt) //
@@ -58,7 +58,7 @@ public class PropertyValueConverterRegistrar<P extends PersistentProperty<P>> {
String propertyName = MethodInvocationRecorder.forProxyOf(type).record(property).getPropertyPath()
.orElseThrow(() -> new IllegalArgumentException("Cannot obtain property name!"));
return new WritingConverterRegistrationBuilder<T, S, P>(type, propertyName, this);
return new WritingConverterRegistrationBuilder<>(type, propertyName, this);
}
/**
@@ -71,8 +71,8 @@ public class PropertyValueConverterRegistrar<P extends PersistentProperty<P>> {
* @return will never be {@literal null}.
*/
public <T, S> WritingConverterRegistrationBuilder<T, S, P> registerConverter(Class<T> type, String propertyName,
Class<S> propertyType) {
return new WritingConverterRegistrationBuilder<T, S, P>(type, propertyName, this);
@SuppressWarnings("unused") Class<S> propertyType) {
return new WritingConverterRegistrationBuilder<>(type, propertyName, this);
}
/**
@@ -83,7 +83,7 @@ public class PropertyValueConverterRegistrar<P extends PersistentProperty<P>> {
* @param converter the converter to apply.
* @return this.
*/
public PropertyValueConverterRegistrar registerConverter(Class<?> type, String path,
public PropertyValueConverterRegistrar<P> registerConverter(Class<?> type, String path,
PropertyValueConverter<?, ?, ? extends ValueConversionContext<?>> converter) {
registry.registerConverter(type, path,
@@ -120,22 +120,22 @@ public class PropertyValueConverterRegistrar<P extends PersistentProperty<P>> {
*
* @author Oliver Drotbohm
*/
static class WritingConverterRegistrationBuilder<T, S, P extends PersistentProperty<P>> {
public static class WritingConverterRegistrationBuilder<T, S, P extends PersistentProperty<P>> {
private final Consumer<PropertyValueConverter> registration;
private final PropertyValueConverterRegistrar config;
private final Consumer<PropertyValueConverter<T, S, ValueConversionContext<P>>> registration;
private final PropertyValueConverterRegistrar<P> config;
public WritingConverterRegistrationBuilder(Class<T> type, String property, PropertyValueConverterRegistrar config) {
WritingConverterRegistrationBuilder(Class<T> type, String property, PropertyValueConverterRegistrar<P> config) {
this.config = config;
this.registration = (converter) -> config.registerConverter(type, property, converter);
}
<R> ReadingConverterRegistrationBuilder<T, S, S, P> writingAsIs() {
public ReadingConverterRegistrationBuilder<T, S, S, P> writingAsIs() {
return writing((source, context) -> source);
}
<R> ReadingConverterRegistrationBuilder<T, S, R, P> writing(Function<S, R> writer) {
public <R> ReadingConverterRegistrationBuilder<T, S, R, P> writing(Function<S, R> writer) {
return writing((source, context) -> writer.apply(source));
}
@@ -146,7 +146,8 @@ public class PropertyValueConverterRegistrar<P extends PersistentProperty<P>> {
* @param writer the property conversion to extract a value written to the database
* @return will never be {@literal null}.
*/
<R> ReadingConverterRegistrationBuilder<T, S, R, P> writing(BiFunction<S, ValueConversionContext<P>, R> writer) {
public <R> ReadingConverterRegistrationBuilder<T, S, R, P> writing(
BiFunction<S, ValueConversionContext<P>, R> writer) {
return new ReadingConverterRegistrationBuilder<>(this, writer);
}
}
@@ -156,22 +157,22 @@ public class PropertyValueConverterRegistrar<P extends PersistentProperty<P>> {
*
* @author Oliver Drotbohm
*/
static class ReadingConverterRegistrationBuilder<T, S, R, P extends PersistentProperty<P>> {
public static class ReadingConverterRegistrationBuilder<T, S, R, P extends PersistentProperty<P>> {
private WritingConverterRegistrationBuilder<T, S, P> origin;
private BiFunction<S, ValueConversionContext<P>, R> writer;
private final WritingConverterRegistrationBuilder<T, S, P> origin;
private final BiFunction<S, ValueConversionContext<P>, R> writer;
public ReadingConverterRegistrationBuilder(WritingConverterRegistrationBuilder<T, S, P> origin,
ReadingConverterRegistrationBuilder(WritingConverterRegistrationBuilder<T, S, P> origin,
BiFunction<S, ValueConversionContext<P>, R> writer) {
this.origin = origin;
this.writer = writer;
}
PropertyValueConverterRegistrar<P> readingAsIs() {
public PropertyValueConverterRegistrar<P> readingAsIs() {
return reading((source, context) -> (S) source);
}
PropertyValueConverterRegistrar<P> reading(Function<R, S> reader) {
public PropertyValueConverterRegistrar<P> reading(Function<R, S> reader) {
return reading((source, context) -> reader.apply(source));
}
@@ -181,7 +182,7 @@ public class PropertyValueConverterRegistrar<P extends PersistentProperty<P>> {
* @param reader must not be {@literal null}.
* @return
*/
PropertyValueConverterRegistrar<P> reading(BiFunction<R, ValueConversionContext<P>, S> reader) {
public PropertyValueConverterRegistrar<P> reading(BiFunction<R, ValueConversionContext<P>, S> reader) {
origin.registration.accept(new FunctionPropertyValueConverter(writer, reader));

View File

@@ -40,12 +40,12 @@ public class SimplePropertyValueConversions implements PropertyValueConversions,
private @Nullable PropertyValueConverterFactory converterFactory;
private @Nullable ValueConverterRegistry<?> valueConverterRegistry;
private boolean converterCacheEnabled = true;
private AtomicBoolean initialized = new AtomicBoolean(false);
private final AtomicBoolean initialized = new AtomicBoolean(false);
/**
* Set the {@link PropertyValueConverterFactory factory} responsible for creating the actual
* {@link PropertyValueConverter converter}.
*
*
* @param converterFactory must not be {@literal null}.
*/
public void setConverterFactory(PropertyValueConverterFactory converterFactory) {
@@ -62,7 +62,7 @@ public class SimplePropertyValueConversions implements PropertyValueConversions,
* a
* {@link org.springframework.data.convert.PropertyValueConverterFactories.ConfiguredInstanceServingValueConverterFactory}
* at the end of a {@link ChainedPropertyValueConverterFactory}.
*
*
* @param valueConverterRegistry must not be {@literal null}.
*/
public void setValueConverterRegistry(ValueConverterRegistry<?> valueConverterRegistry) {
@@ -89,9 +89,19 @@ public class SimplePropertyValueConversions implements PropertyValueConversions,
this.converterCacheEnabled = converterCacheEnabled;
}
@Override
public boolean hasValueConverter(PersistentProperty<?> property) {
if (!initialized.get()) {
init();
}
return this.converterFactory.getConverter(property) != null;
}
@Nullable
@Override
public <A, B, C extends PersistentProperty<C>, D extends ValueConversionContext<C>> PropertyValueConverter<A, B, D> getValueConverter(
public <DV, SV, C extends PersistentProperty<C>, D extends ValueConversionContext<C>> PropertyValueConverter<DV, SV, D> getValueConverter(
C property) {
if (!initialized.get()) {

View File

@@ -24,11 +24,13 @@ import org.springframework.util.ObjectUtils;
/**
* A registry of property specific {@link PropertyValueConverter value convertes} that may be used to convert only
* specific properties/values of an object.
*
*
* @param <P> persistent property type.
* @author Christoph Strobl
* @since 2.7
*/
public class SimplePropertyValueConverterRegistry<P extends PersistentProperty<P>> implements ValueConverterRegistry<P> {
public class SimplePropertyValueConverterRegistry<P extends PersistentProperty<P>>
implements ValueConverterRegistry<P> {
private final Map<Key, PropertyValueConverter<?, ?, ? extends ValueConversionContext<P>>> converterRegistrationMap = new LinkedHashMap<>();
@@ -83,7 +85,7 @@ public class SimplePropertyValueConverterRegistry<P extends PersistentProperty<P
/**
* Obtain the underlying (mutable) map of converters.
*
*
* @return never {@literal null}.
*/
Map<Key, PropertyValueConverter<?, ?, ? extends ValueConversionContext<P>>> getConverterRegistrationMap() {
@@ -92,8 +94,8 @@ public class SimplePropertyValueConverterRegistry<P extends PersistentProperty<P
static class Key {
Class<?> type;
String path;
final Class<?> type;
final String path;
public Key(Class<?> type, String path) {
this.type = type;

View File

@@ -21,8 +21,8 @@ import org.springframework.data.util.TypeInformation;
import org.springframework.lang.Nullable;
/**
* The {@link ValueConversionContext} provides access to the store specific {@link PersistentProperty} and allows to
* call the store default conversion via the {@literal read}/{@literal write} methods.
* The {@link ValueConversionContext} provides access to the store-specific {@link PersistentProperty} and allows to
* call the store-default conversion through the {@literal read}/{@literal write} methods.
* <p>
* Store implementations should provide their own flavor of {@link ValueConversionContext} enhancing the existing API,
* implementing delegates for {@link #read(Object, TypeInformation)}, {@link #write(Object, TypeInformation)}.
@@ -71,6 +71,11 @@ public interface ValueConversionContext<P extends PersistentProperty<P>> {
*/
@Nullable
default <T> T write(@Nullable Object value, TypeInformation<T> target) {
if (value == null || target.getType().isInstance(value)) {
return target.getType().cast(value);
}
throw new IllegalStateException(String.format(
"%s does not provide write function that allows value conversion to target type (%s).", getClass(), target));
}
@@ -107,6 +112,11 @@ public interface ValueConversionContext<P extends PersistentProperty<P>> {
*/
@Nullable
default <T> T read(@Nullable Object value, TypeInformation<T> target) {
if (value == null || target.getType().isInstance(value)) {
return target.getType().cast(value);
}
throw new IllegalStateException(String.format(
"%s does not provide write function that allows value conversion to target type (%s).", getClass(), target));
}

View File

@@ -26,13 +26,13 @@ import org.springframework.data.convert.PropertyValueConverter.ObjectToObjectPro
/**
* Annotation to define usage of a {@link PropertyValueConverter} to read/write the property. <br />
* May be used as meta annotation utilizing {@link org.springframework.core.annotation.AliasFor}.
* Can be used as meta annotation utilizing {@link org.springframework.core.annotation.AliasFor}.
* <p>
* The target {@link PropertyValueConverter} is typically provided via a {@link PropertyValueConverterFactory converter
* factory}.
* <p>
* Consult the store specific documentation for details and support notes.
*
* Consult the store-specific documentation for details and support notes.
*
* @author Christoph Strobl
* @since 2.7
* @see PropertyValueConverter

View File

@@ -19,8 +19,8 @@ import org.springframework.data.mapping.PersistentProperty;
import org.springframework.lang.Nullable;
/**
* A registry of property specific {@link PropertyValueConverter value convertes} that may be used to convert only
* specific properties/values of an object.
* A registry of property-specific {@link PropertyValueConverter value converters} to convert only specific
* properties/values of an object.
*
* @author Christoph Strobl
* @since 2.7
@@ -42,12 +42,12 @@ public interface ValueConverterRegistry<P extends PersistentProperty<P>> {
*
* @param type the target type. Must not be {@literal null}.
* @param path the property name. Must not be {@literal null}.
* @param <S>
* @param <T>
* @param <DV>
* @param <SV>
* @return {@literal null} if no converter present for the given type/path combination.
*/
@Nullable
<S, T> PropertyValueConverter<S, T, ? extends ValueConversionContext<P>> getConverter(Class<?> type, String path);
<DV, SV> PropertyValueConverter<DV, SV, ? extends ValueConversionContext<P>> getConverter(Class<?> type, String path);
/**
* Check if a converter is registered for the given type, path combination.
@@ -61,7 +61,7 @@ public interface ValueConverterRegistry<P extends PersistentProperty<P>> {
}
/**
* Check if there a converters registered.
* Check if converters are registered.
*/
boolean isEmpty();

View File

@@ -428,28 +428,30 @@ public interface PersistentProperty<P extends PersistentProperty<P>> {
}
/**
* Obtain the the {@link PropertyValueConverter converter type} to be used for read-/writing the properties value. By
* default looks for the {@link ValueConverter} annotation and extracts its {@link ValueConverter#value() value}
* attribute.
* Obtain the {@link PropertyValueConverter converter type} to be used for reading and writing property values. Uses
* the {@link ValueConverter} annotation and extracts its {@link ValueConverter#value() value} attribute.
* <p>
* Store implementations may override the default and provide a more specific type.
* Store implementations may override the default and resort to a more specific annotation type.
*
* @return {@literal null} if none defined. Check {@link #hasValueConverter()} to check if the annotation is present
* at all.
* @since 2.7
*/
@Nullable
@SuppressWarnings({ "unchecked", "rawtypes" })
default Class<? extends PropertyValueConverter<?, ?, ? extends ValueConversionContext<? extends PersistentProperty<?>>>> getValueConverterType() {
ValueConverter annotation = findAnnotation(ValueConverter.class);
return annotation == null ? null
: (Class<? extends PropertyValueConverter<?, ?, ? extends ValueConversionContext<? extends PersistentProperty<?>>>>) annotation
.value();
return annotation == null ? null : (Class) annotation.value();
}
/**
* @return by default return {@literal true} if {@link ValueConverter} annotation is present.
* Return whether a value converter is configured. Uses {@link ValueConverter} as annotation type.
* <p>
* Store implementations may override the default and resort to a more specific annotation type.
*
* @return {@literal true} if a value converter is configured.
* @since 2.7
*/
default boolean hasValueConverter() {