#305 - Consistently apply registered converters.

We now apply registered write converters to bindable values that are bound via bind(…) or provided through the Criteria/Update API.
This commit is contained in:
Mark Paluch
2020-02-17 12:48:47 +01:00
parent 6051ab11ae
commit 8828ebb11d
8 changed files with 280 additions and 13 deletions

View File

@@ -522,6 +522,20 @@ public class MappingR2dbcConverter extends BasicRelationalConverter implements R
return value;
}
/*
* (non-Javadoc)
* @see org.springframework.data.r2dbc.convert.R2dbcConverter#getTargetType(Class)
*/
@Override
public Class<?> getTargetType(Class<?> valueType) {
Optional<Class<?>> writeTarget = getConversions().getCustomWriteTarget(valueType);
return writeTarget.orElseGet(() -> {
return Enum.class.isAssignableFrom(valueType) ? String.class : valueType;
});
}
// ----------------------------------
// Id handling
// ----------------------------------

View File

@@ -63,6 +63,15 @@ public interface R2dbcConverter
*/
Object getArrayValue(ArrayColumns arrayColumns, RelationalPersistentProperty property, Object value);
/**
* Return the target type for a value considering registered converters.
*
* @param valueType must not be {@literal null}.
* @return
* @since 1.1
*/
Class<?> getTargetType(Class<?> valueType);
/**
* Returns a {@link java.util.function.Function} that populates the id property of the {@code object} from a
* {@link Row}.
@@ -81,4 +90,5 @@ public interface R2dbcConverter
* @return
*/
<R> R read(Class<R> type, Row source, RowMetadata metadata);
}

View File

@@ -292,26 +292,29 @@ class DefaultDatabaseClient implements DatabaseClient, ConnectionAccessor {
return new DefaultGenericExecuteSpec(sqlSupplier);
}
private static void bindByName(Statement statement, Map<String, SettableValue> byName) {
private void bindByName(Statement statement, Map<String, SettableValue> byName) {
byName.forEach((name, o) -> {
if (o.getValue() != null) {
statement.bind(name, o.getValue());
SettableValue converted = dataAccessStrategy.getBindValue(o);
if (converted.getValue() != null) {
statement.bind(name, converted.getValue());
} else {
statement.bindNull(name, o.getType());
statement.bindNull(name, converted.getType());
}
});
}
private static void bindByIndex(Statement statement, Map<Integer, SettableValue> byIndex) {
private void bindByIndex(Statement statement, Map<Integer, SettableValue> byIndex) {
byIndex.forEach((i, o) -> {
if (o.getValue() != null) {
statement.bind(i, o.getValue());
SettableValue converted = dataAccessStrategy.getBindValue(o);
if (converted.getValue() != null) {
statement.bind(i, converted.getValue());
} else {
statement.bindNull(i, o.getType());
statement.bindNull(i, converted.getType());
}
});
}
@@ -366,12 +369,12 @@ class DefaultDatabaseClient implements DatabaseClient, ConnectionAccessor {
if (byName.containsKey(name)) {
remainderByName.remove(name);
return byName.get(name);
return dataAccessStrategy.getBindValue(byName.get(name));
}
if (byIndex.containsKey(index)) {
remainderByIndex.remove(index);
return byIndex.get(index);
return dataAccessStrategy.getBindValue(byIndex.get(index));
}
return null;

View File

@@ -270,6 +270,15 @@ public class DefaultReactiveDataAccessStrategy implements ReactiveDataAccessStra
actualType);
}
/*
* (non-Javadoc)
* @see org.springframework.data.r2dbc.function.ReactiveDataAccessStrategy#getBindValue(SettableValue)
*/
@Override
public SettableValue getBindValue(SettableValue value) {
return this.updateMapper.getBindValue(value);
}
/*
* (non-Javadoc)
* @see org.springframework.data.r2dbc.function.ReactiveDataAccessStrategy#getRowMapper(java.lang.Class)

View File

@@ -58,6 +58,15 @@ public interface ReactiveDataAccessStrategy {
*/
OutboundRow getOutboundRow(Object object);
/**
* Return a potentially converted {@link SettableValue} for strategies that support type conversion.
*
* @param value must not be {@literal null}.
* @return
* @since 1.1
*/
SettableValue getBindValue(SettableValue value);
/**
* Returns a {@link BiFunction row mapping function} to map {@link Row rows} to {@code T}.
*

View File

@@ -252,6 +252,21 @@ public class QueryMapper {
return createCondition(column, mappedValue, typeHint, bindings, criteria.getComparator());
}
/**
* Potentially convert the {@link SettableValue}.
*
* @param value
* @return
*/
public SettableValue getBindValue(SettableValue value) {
if (value.isEmpty()) {
return SettableValue.empty(converter.getTargetType(value.getType()));
}
return SettableValue.from(convertValue(value.getValue(), ClassTypeInformation.OBJECT));
}
@Nullable
protected Object convertValue(@Nullable Object value, TypeInformation<?> typeInformation) {
@@ -264,13 +279,15 @@ public class QueryMapper {
List<Object> mapped = new ArrayList<>();
for (Object o : (Iterable<?>) value) {
mapped.add(this.converter.writeValue(o, typeInformation.getActualType()));
mapped.add(convertValue(o, typeInformation.getActualType() != null ? typeInformation.getRequiredActualType()
: ClassTypeInformation.OBJECT));
}
return mapped;
}
if (typeInformation.getType().isAssignableFrom(value.getClass())
|| (typeInformation.getType().isArray() && value.getClass().isArray())) {
if (value.getClass().isArray()
&& (ClassTypeInformation.OBJECT.equals(typeInformation) || typeInformation.isCollectionLike())) {
return value;
}