diff --git a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/core/R2dbcEntityTemplate.java b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/core/R2dbcEntityTemplate.java index b119e1cdd..febbfb2a6 100644 --- a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/core/R2dbcEntityTemplate.java +++ b/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; 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 BiFunction rowMapper; - if (simpleType) { + // Bridge-code: Consider Converter 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 { diff --git a/spring-data-r2dbc/src/test/java/org/springframework/data/r2dbc/core/R2dbcEntityTemplateUnitTests.java b/spring-data-r2dbc/src/test/java/org/springframework/data/r2dbc/core/R2dbcEntityTemplateUnitTests.java index 3529a10ff..fa2e4bc49 100644 --- a/spring-data-r2dbc/src/test/java/org/springframework/data/r2dbc/core/R2dbcEntityTemplateUnitTests.java +++ b/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.*; 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; 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 { 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 { 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 { } } + + record MyRowToEntityType(int foo, String bar) { + + } + + static class RowConverter implements Converter { + + @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 { + + @Override + public MyRowDocumentToEntityType convert(RowDocument source) { + return new MyRowDocumentToEntityType(1, (String) source.get("bar")); + } + + } } diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/MappingRelationalConverter.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/MappingRelationalConverter.java index 9503be81a..2c38f5bff 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/MappingRelationalConverter.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/MappingRelationalConverter.java @@ -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 Class 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 } } - private record PropertyTranslatingPropertyAccessor( - PersistentPropertyAccessor delegate, + private record PropertyTranslatingPropertyAccessor(PersistentPropertyAccessor delegate, PersistentPropertyTranslator propertyTranslator) implements PersistentPropertyAccessor { static PersistentPropertyAccessor create(PersistentPropertyAccessor delegate,