Skip to content

Commit 5647835

Browse files
committed
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
1 parent 5eb15d0 commit 5647835

File tree

3 files changed

+87
-7
lines changed

3 files changed

+87
-7
lines changed

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

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757
import org.springframework.data.r2dbc.mapping.event.AfterSaveCallback;
5858
import org.springframework.data.r2dbc.mapping.event.BeforeConvertCallback;
5959
import org.springframework.data.r2dbc.mapping.event.BeforeSaveCallback;
60+
import org.springframework.data.relational.core.conversion.AbstractRelationalConverter;
6061
import org.springframework.data.relational.core.mapping.PersistentPropertyTranslator;
6162
import org.springframework.data.relational.core.mapping.RelationalPersistentEntity;
6263
import org.springframework.data.relational.core.mapping.RelationalPersistentProperty;
@@ -818,7 +819,13 @@ public <T> RowsFetchSpec<T> getRowsFetchSpec(DatabaseClient.GenericExecuteSpec e
818819

819820
BiFunction<Row, RowMetadata, T> rowMapper;
820821

821-
if (simpleType) {
822+
// Bridge-code: Consider Converter<Row, T> until we have fully migrated to RowDocument
823+
if (converter instanceof AbstractRelationalConverter relationalConverter
824+
&& relationalConverter.getConversions().hasCustomReadTarget(Row.class, entityType)) {
825+
826+
ConversionService conversionService = relationalConverter.getConversionService();
827+
rowMapper = (row, rowMetadata) -> (T) conversionService.convert(row, entityType);
828+
} else if (simpleType) {
822829
rowMapper = dataAccessStrategy.getRowMapper(resultType);
823830
} else {
824831

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

Lines changed: 66 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121

2222
import io.r2dbc.spi.Parameters;
2323
import io.r2dbc.spi.R2dbcType;
24+
import io.r2dbc.spi.Row;
2425
import io.r2dbc.spi.test.MockColumnMetadata;
2526
import io.r2dbc.spi.test.MockResult;
2627
import io.r2dbc.spi.test.MockRow;
@@ -62,6 +63,7 @@
6263
import org.springframework.data.relational.core.query.Query;
6364
import org.springframework.data.relational.core.query.Update;
6465
import org.springframework.data.relational.core.sql.SqlIdentifier;
66+
import org.springframework.data.relational.domain.RowDocument;
6567
import org.springframework.lang.Nullable;
6668
import org.springframework.r2dbc.core.DatabaseClient;
6769
import org.springframework.r2dbc.core.Parameter;
@@ -88,7 +90,8 @@ void before() {
8890
client = DatabaseClient.builder().connectionFactory(recorder)
8991
.bindMarkers(PostgresDialect.INSTANCE.getBindMarkersFactory()).build();
9092

91-
R2dbcCustomConversions conversions = R2dbcCustomConversions.of(PostgresDialect.INSTANCE, new MoneyConverter());
93+
R2dbcCustomConversions conversions = R2dbcCustomConversions.of(PostgresDialect.INSTANCE, new MoneyConverter(),
94+
new RowConverter(), new RowDocumentConverter());
9295

9396
entityTemplate = new R2dbcEntityTemplate(client, PostgresDialect.INSTANCE,
9497
new MappingR2dbcConverter(new R2dbcMappingContext(), conversions));
@@ -611,6 +614,42 @@ void shouldConsiderParameterConverter() {
611614
Parameter.from(Parameters.in(R2dbcType.VARCHAR, "$$$")));
612615
}
613616

617+
@Test // GH-1696
618+
void shouldConsiderRowConverter() {
619+
620+
MockRowMetadata metadata = MockRowMetadata.builder()
621+
.columnMetadata(MockColumnMetadata.builder().name("foo").type(R2dbcType.INTEGER).build())
622+
.columnMetadata(MockColumnMetadata.builder().name("bar").type(R2dbcType.VARCHAR).build()).build();
623+
MockResult result = MockResult.builder().row(MockRow.builder().identified("foo", Object.class, 42)
624+
.identified("bar", String.class, "the-bar").metadata(metadata).build()).build();
625+
626+
recorder.addStubbing(s -> s.startsWith("SELECT"), result);
627+
628+
entityTemplate.select(MyRowToEntityType.class).all().as(StepVerifier::create) //
629+
.assertNext(actual -> {
630+
assertThat(actual.foo).isEqualTo(1); // converter-fixed value
631+
assertThat(actual.bar).isEqualTo("the-bar"); // converted value
632+
}).verifyComplete();
633+
}
634+
635+
@Test // GH-1696
636+
void shouldConsiderRowDocumentConverter() {
637+
638+
MockRowMetadata metadata = MockRowMetadata.builder()
639+
.columnMetadata(MockColumnMetadata.builder().name("foo").type(R2dbcType.INTEGER).build())
640+
.columnMetadata(MockColumnMetadata.builder().name("bar").type(R2dbcType.VARCHAR).build()).build();
641+
MockResult result = MockResult.builder().row(MockRow.builder().identified("foo", Object.class, 42)
642+
.identified("bar", Object.class, "the-bar").metadata(metadata).build()).build();
643+
644+
recorder.addStubbing(s -> s.startsWith("SELECT"), result);
645+
646+
entityTemplate.select(MyRowDocumentToEntityType.class).all().as(StepVerifier::create) //
647+
.assertNext(actual -> {
648+
assertThat(actual.foo).isEqualTo(1); // converter-fixed value
649+
assertThat(actual.bar).isEqualTo("the-bar"); // converted value
650+
}).verifyComplete();
651+
}
652+
614653
record WithoutId(String name) {
615654
}
616655

@@ -826,4 +865,30 @@ public io.r2dbc.spi.Parameter convert(Money source) {
826865
}
827866

828867
}
868+
869+
record MyRowToEntityType(int foo, String bar) {
870+
871+
}
872+
873+
static class RowConverter implements Converter<Row, MyRowToEntityType> {
874+
875+
@Override
876+
public MyRowToEntityType convert(Row source) {
877+
return new MyRowToEntityType(1, source.get("bar", String.class));
878+
}
879+
880+
}
881+
882+
record MyRowDocumentToEntityType(int foo, String bar) {
883+
884+
}
885+
886+
static class RowDocumentConverter implements Converter<RowDocument, MyRowDocumentToEntityType> {
887+
888+
@Override
889+
public MyRowDocumentToEntityType convert(RowDocument source) {
890+
return new MyRowDocumentToEntityType(1, (String) source.get("bar"));
891+
}
892+
893+
}
829894
}

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

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,16 @@
4141
import org.springframework.data.mapping.PersistentPropertyAccessor;
4242
import org.springframework.data.mapping.PersistentPropertyPathAccessor;
4343
import org.springframework.data.mapping.context.MappingContext;
44-
import org.springframework.data.mapping.model.*;
44+
import org.springframework.data.mapping.model.ConvertingPropertyAccessor;
45+
import org.springframework.data.mapping.model.DefaultSpELExpressionEvaluator;
46+
import org.springframework.data.mapping.model.EntityInstantiator;
47+
import org.springframework.data.mapping.model.ParameterValueProvider;
48+
import org.springframework.data.mapping.model.PersistentEntityParameterValueProvider;
49+
import org.springframework.data.mapping.model.PropertyValueProvider;
50+
import org.springframework.data.mapping.model.SimpleTypeHolder;
51+
import org.springframework.data.mapping.model.SpELContext;
52+
import org.springframework.data.mapping.model.SpELExpressionEvaluator;
53+
import org.springframework.data.mapping.model.SpELExpressionParameterValueProvider;
4554
import org.springframework.data.projection.EntityProjection;
4655
import org.springframework.data.projection.EntityProjectionIntrospector;
4756
import org.springframework.data.projection.EntityProjectionIntrospector.ProjectionPredicate;
@@ -316,8 +325,8 @@ protected <S> S readAggregate(ConversionContext context, RowDocumentAccessor doc
316325

317326
Class<? extends S> rawType = typeHint.getType();
318327

319-
if (getConversions().hasCustomReadTarget(documentAccessor.getClass(), rawType)) {
320-
return doConvert(documentAccessor, rawType, typeHint.getType());
328+
if (getConversions().hasCustomReadTarget(RowDocument.class, rawType)) {
329+
return doConvert(documentAccessor.getDocument(), rawType, typeHint.getType());
321330
}
322331

323332
if (RowDocument.class.isAssignableFrom(rawType)) {
@@ -1199,8 +1208,7 @@ protected <T> T potentiallyConvertSpelValue(Object object, Parameter<T, Relation
11991208
}
12001209
}
12011210

1202-
private record PropertyTranslatingPropertyAccessor<T>(
1203-
PersistentPropertyAccessor<T> delegate,
1211+
private record PropertyTranslatingPropertyAccessor<T>(PersistentPropertyAccessor<T> delegate,
12041212
PersistentPropertyTranslator propertyTranslator) implements PersistentPropertyAccessor<T> {
12051213

12061214
static <T> PersistentPropertyAccessor<T> create(PersistentPropertyAccessor<T> delegate,

0 commit comments

Comments
 (0)