diff --git a/src/main/java/org/springframework/data/r2dbc/core/DefaultReactiveDataAccessStrategy.java b/src/main/java/org/springframework/data/r2dbc/core/DefaultReactiveDataAccessStrategy.java index 5d18989..f62d93b 100644 --- a/src/main/java/org/springframework/data/r2dbc/core/DefaultReactiveDataAccessStrategy.java +++ b/src/main/java/org/springframework/data/r2dbc/core/DefaultReactiveDataAccessStrategy.java @@ -101,7 +101,7 @@ public class DefaultReactiveDataAccessStrategy implements ReactiveDataAccessStra storeConverters.addAll(R2dbcCustomConversions.STORE_CONVERTERS); R2dbcCustomConversions customConversions = new R2dbcCustomConversions( - StoreConversions.of(dialect.getSimpleTypeHolder(), storeConverters), storeConverters); + StoreConversions.of(dialect.getSimpleTypeHolder(), storeConverters), converters); R2dbcMappingContext context = new R2dbcMappingContext(); context.setSimpleTypeHolder(customConversions.getSimpleTypeHolder()); @@ -215,7 +215,20 @@ public class DefaultReactiveDataAccessStrategy implements ReactiveDataAccessStra } private boolean shouldConvertArrayValue(RelationalPersistentProperty property, SettableValue value) { - return property.isCollectionLike(); + + if (!property.isCollectionLike()) { + return false; + } + + if (value.hasValue() && (value.getValue() instanceof Collection || value.getValue().getClass().isArray())) { + return true; + } + + if (Collection.class.isAssignableFrom(value.getType()) || value.getType().isArray()) { + return true; + } + + return false; } private SettableValue getArrayValue(SettableValue value, RelationalPersistentProperty property) { diff --git a/src/main/java/org/springframework/data/r2dbc/core/ReactiveDataAccessStrategy.java b/src/main/java/org/springframework/data/r2dbc/core/ReactiveDataAccessStrategy.java index 70deea3..33419b2 100644 --- a/src/main/java/org/springframework/data/r2dbc/core/ReactiveDataAccessStrategy.java +++ b/src/main/java/org/springframework/data/r2dbc/core/ReactiveDataAccessStrategy.java @@ -56,7 +56,13 @@ public interface ReactiveDataAccessStrategy { */ OutboundRow getOutboundRow(Object object); - // TODO: Broaden T to Mono/Flux for reactive relational data access? + /** + * Returns a {@link BiFunction row mapping function} to map {@link Row rows} to {@code T}. + * + * @param typeToRead + * @param + * @return + */ BiFunction getRowMapper(Class typeToRead); /** diff --git a/src/test/java/org/springframework/data/r2dbc/core/PostgresReactiveDataAccessStrategyTests.java b/src/test/java/org/springframework/data/r2dbc/core/PostgresReactiveDataAccessStrategyTests.java index 182f8b9..b0303fd 100644 --- a/src/test/java/org/springframework/data/r2dbc/core/PostgresReactiveDataAccessStrategyTests.java +++ b/src/test/java/org/springframework/data/r2dbc/core/PostgresReactiveDataAccessStrategyTests.java @@ -20,12 +20,16 @@ import static org.assertj.core.api.Assertions.*; import lombok.RequiredArgsConstructor; import java.util.Arrays; +import java.util.Collections; import java.util.List; import org.junit.Test; +import org.springframework.core.convert.converter.Converter; +import org.springframework.data.convert.WritingConverter; import org.springframework.data.r2dbc.dialect.PostgresDialect; import org.springframework.data.r2dbc.mapping.OutboundRow; +import org.springframework.data.r2dbc.mapping.SettableValue; /** * {@link PostgresDialect} specific tests for {@link ReactiveDataAccessStrategy}. @@ -69,6 +73,56 @@ public class PostgresReactiveDataAccessStrategyTests extends ReactiveDataAccessS assertThat((Integer[]) row.get("myarray").getValue()).contains(1, 2, 3); } + @Test // gh-139 + public void shouldConvertToArray() { + + DefaultReactiveDataAccessStrategy strategy = new DefaultReactiveDataAccessStrategy(PostgresDialect.INSTANCE); + + WithArray withArray = new WithArray(); + withArray.stringArray = new String[] { "hello", "world" }; + withArray.stringList = Arrays.asList("hello", "world"); + + OutboundRow outboundRow = strategy.getOutboundRow(withArray); + + assertThat(outboundRow) // + .containsEntry("string_array", SettableValue.from(new String[] { "hello", "world" })) + .containsEntry("string_list", SettableValue.from(new String[] { "hello", "world" })); + } + + @Test // gh-139 + public void shouldApplyCustomConversion() { + + DefaultReactiveDataAccessStrategy strategy = new DefaultReactiveDataAccessStrategy(PostgresDialect.INSTANCE, + Collections.singletonList(MyObjectsToStringConverter.INSTANCE)); + + WithConversion withConversion = new WithConversion(); + withConversion.myObjects = Arrays.asList(new MyObject("one"), new MyObject("two")); + + OutboundRow outboundRow = strategy.getOutboundRow(withConversion); + + assertThat(outboundRow) // + .containsEntry("my_objects", SettableValue.from("[one, two]")); + } + + @Test // gh-139 + public void shouldApplyCustomConversionForNull() { + + DefaultReactiveDataAccessStrategy strategy = new DefaultReactiveDataAccessStrategy(PostgresDialect.INSTANCE, + Collections.singletonList(MyObjectsToStringConverter.INSTANCE)); + + WithConversion withConversion = new WithConversion(); + withConversion.myObjects = null; + + OutboundRow outboundRow = strategy.getOutboundRow(withConversion); + + assertThat(outboundRow) // + .containsKey("my_objects"); + + SettableValue value = outboundRow.get("my_objects"); + assertThat(value.isEmpty()).isTrue(); + assertThat(value.getType()).isEqualTo(String.class); + } + @RequiredArgsConstructor static class WithMultidimensionalArray { @@ -80,4 +134,39 @@ public class PostgresReactiveDataAccessStrategyTests extends ReactiveDataAccessS final List myarray; } + + static class WithArray { + + String[] stringArray; + List stringList; + } + + static class WithConversion { + + List myObjects; + } + + static class MyObject { + String foo; + + public MyObject(String foo) { + this.foo = foo; + } + + @Override + public String toString() { + return foo; + } + } + + @WritingConverter + enum MyObjectsToStringConverter implements Converter, String> { + + INSTANCE; + + @Override + public String convert(List myObjects) { + return myObjects.toString(); + } + } }