Browse Source

Consider registered converters using Row and RowDocument as source.

We now consider converters for RowDocument. Additionally, we reinstated conversion from R2DBC's Row type into entities as that converter functionality got lost during the converter revision.

Closes #1710
3.2.x
Mark Paluch 2 years ago
parent
commit
c80873019c
No known key found for this signature in database
GPG Key ID: 55BC6374BAA9D973
  1. 9
      spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/core/R2dbcEntityTemplate.java
  2. 67
      spring-data-r2dbc/src/test/java/org/springframework/data/r2dbc/core/R2dbcEntityTemplateUnitTests.java
  3. 18
      spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/MappingRelationalConverter.java

9
spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/core/R2dbcEntityTemplate.java

@ -57,6 +57,7 @@ import org.springframework.data.r2dbc.mapping.event.AfterConvertCallback; @@ -57,6 +57,7 @@ import org.springframework.data.r2dbc.mapping.event.AfterConvertCallback;
import org.springframework.data.r2dbc.mapping.event.AfterSaveCallback;
import org.springframework.data.r2dbc.mapping.event.BeforeConvertCallback;
import org.springframework.data.r2dbc.mapping.event.BeforeSaveCallback;
import org.springframework.data.relational.core.conversion.AbstractRelationalConverter;
import org.springframework.data.relational.core.mapping.PersistentPropertyTranslator;
import org.springframework.data.relational.core.mapping.RelationalPersistentEntity;
import org.springframework.data.relational.core.mapping.RelationalPersistentProperty;
@ -818,7 +819,13 @@ public class R2dbcEntityTemplate implements R2dbcEntityOperations, BeanFactoryAw @@ -818,7 +819,13 @@ public class R2dbcEntityTemplate implements R2dbcEntityOperations, BeanFactoryAw
BiFunction<Row, RowMetadata, T> rowMapper;
if (simpleType) {
// Bridge-code: Consider Converter<Row, T> until we have fully migrated to RowDocument
if (converter instanceof AbstractRelationalConverter relationalConverter
&& relationalConverter.getConversions().hasCustomReadTarget(Row.class, entityType)) {
ConversionService conversionService = relationalConverter.getConversionService();
rowMapper = (row, rowMetadata) -> (T) conversionService.convert(row, entityType);
} else if (simpleType) {
rowMapper = dataAccessStrategy.getRowMapper(resultType);
} else {

67
spring-data-r2dbc/src/test/java/org/springframework/data/r2dbc/core/R2dbcEntityTemplateUnitTests.java

@ -21,6 +21,7 @@ import static org.springframework.data.relational.core.query.Criteria.*; @@ -21,6 +21,7 @@ import static org.springframework.data.relational.core.query.Criteria.*;
import io.r2dbc.spi.Parameters;
import io.r2dbc.spi.R2dbcType;
import io.r2dbc.spi.Row;
import io.r2dbc.spi.test.MockColumnMetadata;
import io.r2dbc.spi.test.MockResult;
import io.r2dbc.spi.test.MockRow;
@ -62,6 +63,7 @@ import org.springframework.data.relational.core.query.Criteria; @@ -62,6 +63,7 @@ import org.springframework.data.relational.core.query.Criteria;
import org.springframework.data.relational.core.query.Query;
import org.springframework.data.relational.core.query.Update;
import org.springframework.data.relational.core.sql.SqlIdentifier;
import org.springframework.data.relational.domain.RowDocument;
import org.springframework.lang.Nullable;
import org.springframework.r2dbc.core.DatabaseClient;
import org.springframework.r2dbc.core.Parameter;
@ -88,7 +90,8 @@ public class R2dbcEntityTemplateUnitTests { @@ -88,7 +90,8 @@ public class R2dbcEntityTemplateUnitTests {
client = DatabaseClient.builder().connectionFactory(recorder)
.bindMarkers(PostgresDialect.INSTANCE.getBindMarkersFactory()).build();
R2dbcCustomConversions conversions = R2dbcCustomConversions.of(PostgresDialect.INSTANCE, new MoneyConverter());
R2dbcCustomConversions conversions = R2dbcCustomConversions.of(PostgresDialect.INSTANCE, new MoneyConverter(),
new RowConverter(), new RowDocumentConverter());
entityTemplate = new R2dbcEntityTemplate(client, PostgresDialect.INSTANCE,
new MappingR2dbcConverter(new R2dbcMappingContext(), conversions));
@ -610,6 +613,42 @@ public class R2dbcEntityTemplateUnitTests { @@ -610,6 +613,42 @@ public class R2dbcEntityTemplateUnitTests {
Parameter.from(Parameters.in(R2dbcType.VARCHAR, "$$$")));
}
@Test // GH-1696
void shouldConsiderRowConverter() {
MockRowMetadata metadata = MockRowMetadata.builder()
.columnMetadata(MockColumnMetadata.builder().name("foo").type(R2dbcType.INTEGER).build())
.columnMetadata(MockColumnMetadata.builder().name("bar").type(R2dbcType.VARCHAR).build()).build();
MockResult result = MockResult.builder().row(MockRow.builder().identified("foo", Object.class, 42)
.identified("bar", String.class, "the-bar").metadata(metadata).build()).build();
recorder.addStubbing(s -> s.startsWith("SELECT"), result);
entityTemplate.select(MyRowToEntityType.class).all().as(StepVerifier::create) //
.assertNext(actual -> {
assertThat(actual.foo).isEqualTo(1); // converter-fixed value
assertThat(actual.bar).isEqualTo("the-bar"); // converted value
}).verifyComplete();
}
@Test // GH-1696
void shouldConsiderRowDocumentConverter() {
MockRowMetadata metadata = MockRowMetadata.builder()
.columnMetadata(MockColumnMetadata.builder().name("foo").type(R2dbcType.INTEGER).build())
.columnMetadata(MockColumnMetadata.builder().name("bar").type(R2dbcType.VARCHAR).build()).build();
MockResult result = MockResult.builder().row(MockRow.builder().identified("foo", Object.class, 42)
.identified("bar", Object.class, "the-bar").metadata(metadata).build()).build();
recorder.addStubbing(s -> s.startsWith("SELECT"), result);
entityTemplate.select(MyRowDocumentToEntityType.class).all().as(StepVerifier::create) //
.assertNext(actual -> {
assertThat(actual.foo).isEqualTo(1); // converter-fixed value
assertThat(actual.bar).isEqualTo("the-bar"); // converted value
}).verifyComplete();
}
record WithoutId(String name) {
}
@ -825,4 +864,30 @@ public class R2dbcEntityTemplateUnitTests { @@ -825,4 +864,30 @@ public class R2dbcEntityTemplateUnitTests {
}
}
record MyRowToEntityType(int foo, String bar) {
}
static class RowConverter implements Converter<Row, MyRowToEntityType> {
@Override
public MyRowToEntityType convert(Row source) {
return new MyRowToEntityType(1, source.get("bar", String.class));
}
}
record MyRowDocumentToEntityType(int foo, String bar) {
}
static class RowDocumentConverter implements Converter<RowDocument, MyRowDocumentToEntityType> {
@Override
public MyRowDocumentToEntityType convert(RowDocument source) {
return new MyRowDocumentToEntityType(1, (String) source.get("bar"));
}
}
}

18
spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/MappingRelationalConverter.java

@ -41,7 +41,16 @@ import org.springframework.data.mapping.PersistentProperty; @@ -41,7 +41,16 @@ import org.springframework.data.mapping.PersistentProperty;
import org.springframework.data.mapping.PersistentPropertyAccessor;
import org.springframework.data.mapping.PersistentPropertyPathAccessor;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.mapping.model.*;
import org.springframework.data.mapping.model.ConvertingPropertyAccessor;
import org.springframework.data.mapping.model.DefaultSpELExpressionEvaluator;
import org.springframework.data.mapping.model.EntityInstantiator;
import org.springframework.data.mapping.model.ParameterValueProvider;
import org.springframework.data.mapping.model.PersistentEntityParameterValueProvider;
import org.springframework.data.mapping.model.PropertyValueProvider;
import org.springframework.data.mapping.model.SimpleTypeHolder;
import org.springframework.data.mapping.model.SpELContext;
import org.springframework.data.mapping.model.SpELExpressionEvaluator;
import org.springframework.data.mapping.model.SpELExpressionParameterValueProvider;
import org.springframework.data.projection.EntityProjection;
import org.springframework.data.projection.EntityProjectionIntrospector;
import org.springframework.data.projection.EntityProjectionIntrospector.ProjectionPredicate;
@ -316,8 +325,8 @@ public class MappingRelationalConverter extends AbstractRelationalConverter impl @@ -316,8 +325,8 @@ public class MappingRelationalConverter extends AbstractRelationalConverter impl
Class<? extends S> rawType = typeHint.getType();
if (getConversions().hasCustomReadTarget(documentAccessor.getClass(), rawType)) {
return doConvert(documentAccessor, rawType, typeHint.getType());
if (getConversions().hasCustomReadTarget(RowDocument.class, rawType)) {
return doConvert(documentAccessor.getDocument(), rawType, typeHint.getType());
}
if (RowDocument.class.isAssignableFrom(rawType)) {
@ -1199,8 +1208,7 @@ public class MappingRelationalConverter extends AbstractRelationalConverter impl @@ -1199,8 +1208,7 @@ public class MappingRelationalConverter extends AbstractRelationalConverter impl
}
}
private record PropertyTranslatingPropertyAccessor<T>(
PersistentPropertyAccessor<T> delegate,
private record PropertyTranslatingPropertyAccessor<T>(PersistentPropertyAccessor<T> delegate,
PersistentPropertyTranslator propertyTranslator) implements PersistentPropertyAccessor<T> {
static <T> PersistentPropertyAccessor<T> create(PersistentPropertyAccessor<T> delegate,

Loading…
Cancel
Save