Correctly translate absent convertible enum values to String[] using Postgres.
We now correctly obtain the target type for a persistent property that should be converted into an array value prior to constructing Parameter. Previously, we fell back to the actual type without considering potential converters which left collection-like enum properties without a value with the enum array type that was potentially not supported by the database driver. Closes #593
This commit is contained in:
@@ -247,14 +247,16 @@ public class DefaultReactiveDataAccessStrategy implements ReactiveDataAccessStra
|
|||||||
Class<?> actualType = null;
|
Class<?> actualType = null;
|
||||||
if (value.getValue() instanceof Collection) {
|
if (value.getValue() instanceof Collection) {
|
||||||
actualType = CollectionUtils.findCommonElementType((Collection<?>) value.getValue());
|
actualType = CollectionUtils.findCommonElementType((Collection<?>) value.getValue());
|
||||||
} else if (value.getClass().isArray()) {
|
} else if (!value.isEmpty() && value.getValue().getClass().isArray()) {
|
||||||
actualType = value.getClass().getComponentType();
|
actualType = value.getValue().getClass().getComponentType();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (actualType == null) {
|
if (actualType == null) {
|
||||||
actualType = property.getActualType();
|
actualType = property.getActualType();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
actualType = converter.getTargetType(actualType);
|
||||||
|
|
||||||
if (value.isEmpty()) {
|
if (value.isEmpty()) {
|
||||||
|
|
||||||
Class<?> targetType = arrayColumns.getArrayType(actualType);
|
Class<?> targetType = arrayColumns.getArrayType(actualType);
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ import org.junit.jupiter.api.Test;
|
|||||||
|
|
||||||
import org.springframework.core.convert.converter.Converter;
|
import org.springframework.core.convert.converter.Converter;
|
||||||
import org.springframework.data.convert.WritingConverter;
|
import org.springframework.data.convert.WritingConverter;
|
||||||
|
import org.springframework.data.r2dbc.convert.EnumWriteSupport;
|
||||||
import org.springframework.data.r2dbc.dialect.PostgresDialect;
|
import org.springframework.data.r2dbc.dialect.PostgresDialect;
|
||||||
import org.springframework.data.r2dbc.mapping.OutboundRow;
|
import org.springframework.data.r2dbc.mapping.OutboundRow;
|
||||||
import org.springframework.data.relational.core.sql.SqlIdentifier;
|
import org.springframework.data.relational.core.sql.SqlIdentifier;
|
||||||
@@ -126,38 +127,90 @@ public class PostgresReactiveDataAccessStrategyTests extends ReactiveDataAccessS
|
|||||||
assertThat(value.getType()).isEqualTo(String.class);
|
assertThat(value.getType()).isEqualTo(String.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test // gh-252
|
@Test // gh-252, gh-593
|
||||||
void shouldConvertSetOfEnumToString() {
|
void shouldConvertCollectionOfEnumToString() {
|
||||||
|
|
||||||
DefaultReactiveDataAccessStrategy strategy = new DefaultReactiveDataAccessStrategy(PostgresDialect.INSTANCE,
|
DefaultReactiveDataAccessStrategy strategy = new DefaultReactiveDataAccessStrategy(PostgresDialect.INSTANCE);
|
||||||
Collections.singletonList(MyObjectsToStringConverter.INSTANCE));
|
|
||||||
|
|
||||||
WithEnumCollections withEnums = new WithEnumCollections();
|
WithEnumCollections withEnums = new WithEnumCollections();
|
||||||
withEnums.enumSet = EnumSet.of(MyEnum.ONE, MyEnum.TWO);
|
withEnums.enumSet = EnumSet.of(MyEnum.ONE, MyEnum.TWO);
|
||||||
|
withEnums.enumList = Arrays.asList(MyEnum.ONE, MyEnum.TWO);
|
||||||
OutboundRow outboundRow = strategy.getOutboundRow(withEnums);
|
|
||||||
|
|
||||||
assertThat(outboundRow).containsKey(SqlIdentifier.unquoted("enum_set"));
|
|
||||||
|
|
||||||
Parameter value = outboundRow.get(SqlIdentifier.unquoted("enum_set"));
|
|
||||||
assertThat(value.getValue()).isEqualTo(new String[] { "ONE", "TWO" });
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test // gh-252
|
|
||||||
void shouldConvertArrayOfEnumToString() {
|
|
||||||
|
|
||||||
DefaultReactiveDataAccessStrategy strategy = new DefaultReactiveDataAccessStrategy(PostgresDialect.INSTANCE,
|
|
||||||
Collections.singletonList(MyObjectsToStringConverter.INSTANCE));
|
|
||||||
|
|
||||||
WithEnumCollections withEnums = new WithEnumCollections();
|
|
||||||
withEnums.enumArray = new MyEnum[] { MyEnum.ONE, MyEnum.TWO };
|
withEnums.enumArray = new MyEnum[] { MyEnum.ONE, MyEnum.TWO };
|
||||||
|
|
||||||
OutboundRow outboundRow = strategy.getOutboundRow(withEnums);
|
OutboundRow outboundRow = strategy.getOutboundRow(withEnums);
|
||||||
|
|
||||||
assertThat(outboundRow).containsKey(SqlIdentifier.unquoted("enum_array"));
|
assertThat(outboundRow).containsKey(SqlIdentifier.unquoted("enum_set"));
|
||||||
|
assertThat(outboundRow.get(SqlIdentifier.unquoted("enum_set")).getValue()).isEqualTo(new String[] { "ONE", "TWO" });
|
||||||
|
|
||||||
Parameter value = outboundRow.get(SqlIdentifier.unquoted("enum_array"));
|
assertThat(outboundRow).containsKey(SqlIdentifier.unquoted("enum_array"));
|
||||||
assertThat(value.getValue()).isEqualTo(new String[] { "ONE", "TWO" });
|
assertThat(outboundRow.get(SqlIdentifier.unquoted("enum_array")).getValue())
|
||||||
|
.isEqualTo(new String[] { "ONE", "TWO" });
|
||||||
|
|
||||||
|
assertThat(outboundRow).containsKey(SqlIdentifier.unquoted("enum_list"));
|
||||||
|
assertThat(outboundRow.get(SqlIdentifier.unquoted("enum_list")).getValue())
|
||||||
|
.isEqualTo(new String[] { "ONE", "TWO" });
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test // gh-593
|
||||||
|
void shouldCorrectlyWriteConvertedEnumNullValues() {
|
||||||
|
|
||||||
|
DefaultReactiveDataAccessStrategy strategy = new DefaultReactiveDataAccessStrategy(PostgresDialect.INSTANCE);
|
||||||
|
|
||||||
|
WithEnumCollections withEnums = new WithEnumCollections();
|
||||||
|
|
||||||
|
OutboundRow outboundRow = strategy.getOutboundRow(withEnums);
|
||||||
|
|
||||||
|
assertThat(outboundRow).containsKey(SqlIdentifier.unquoted("enum_set"));
|
||||||
|
assertThat(outboundRow.get(SqlIdentifier.unquoted("enum_set")).getType()).isEqualTo(String[].class);
|
||||||
|
|
||||||
|
assertThat(outboundRow).containsKey(SqlIdentifier.unquoted("enum_array"));
|
||||||
|
assertThat(outboundRow.get(SqlIdentifier.unquoted("enum_array")).getType()).isEqualTo(String[].class);
|
||||||
|
|
||||||
|
assertThat(outboundRow).containsKey(SqlIdentifier.unquoted("enum_list"));
|
||||||
|
assertThat(outboundRow.get(SqlIdentifier.unquoted("enum_list")).getType()).isEqualTo(String[].class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test // gh-593
|
||||||
|
void shouldConvertCollectionOfEnumNatively() {
|
||||||
|
|
||||||
|
DefaultReactiveDataAccessStrategy strategy = new DefaultReactiveDataAccessStrategy(PostgresDialect.INSTANCE,
|
||||||
|
Collections.singletonList(new MyEnumSupport()));
|
||||||
|
|
||||||
|
WithEnumCollections withEnums = new WithEnumCollections();
|
||||||
|
withEnums.enumSet = EnumSet.of(MyEnum.ONE, MyEnum.TWO);
|
||||||
|
withEnums.enumList = Arrays.asList(MyEnum.ONE, MyEnum.TWO);
|
||||||
|
withEnums.enumArray = new MyEnum[] { MyEnum.ONE, MyEnum.TWO };
|
||||||
|
|
||||||
|
OutboundRow outboundRow = strategy.getOutboundRow(withEnums);
|
||||||
|
|
||||||
|
assertThat(outboundRow).containsKey(SqlIdentifier.unquoted("enum_set"));
|
||||||
|
assertThat(outboundRow.get(SqlIdentifier.unquoted("enum_set")).getValue()).isInstanceOf(MyEnum[].class);
|
||||||
|
|
||||||
|
assertThat(outboundRow).containsKey(SqlIdentifier.unquoted("enum_array"));
|
||||||
|
assertThat(outboundRow.get(SqlIdentifier.unquoted("enum_array")).getValue()).isInstanceOf(MyEnum[].class);
|
||||||
|
|
||||||
|
assertThat(outboundRow).containsKey(SqlIdentifier.unquoted("enum_list"));
|
||||||
|
assertThat(outboundRow.get(SqlIdentifier.unquoted("enum_list")).getValue()).isInstanceOf(MyEnum[].class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test // gh-593
|
||||||
|
void shouldCorrectlyWriteNativeEnumNullValues() {
|
||||||
|
|
||||||
|
DefaultReactiveDataAccessStrategy strategy = new DefaultReactiveDataAccessStrategy(PostgresDialect.INSTANCE,
|
||||||
|
Collections.singletonList(new MyEnumSupport()));
|
||||||
|
|
||||||
|
WithEnumCollections withEnums = new WithEnumCollections();
|
||||||
|
|
||||||
|
OutboundRow outboundRow = strategy.getOutboundRow(withEnums);
|
||||||
|
|
||||||
|
assertThat(outboundRow).containsKey(SqlIdentifier.unquoted("enum_set"));
|
||||||
|
assertThat(outboundRow.get(SqlIdentifier.unquoted("enum_set")).getType()).isEqualTo(MyEnum[].class);
|
||||||
|
|
||||||
|
assertThat(outboundRow).containsKey(SqlIdentifier.unquoted("enum_array"));
|
||||||
|
assertThat(outboundRow.get(SqlIdentifier.unquoted("enum_array")).getType()).isEqualTo(MyEnum[].class);
|
||||||
|
|
||||||
|
assertThat(outboundRow).containsKey(SqlIdentifier.unquoted("enum_list"));
|
||||||
|
assertThat(outboundRow.get(SqlIdentifier.unquoted("enum_list")).getType()).isEqualTo(MyEnum[].class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
@@ -182,6 +235,7 @@ public class PostgresReactiveDataAccessStrategyTests extends ReactiveDataAccessS
|
|||||||
|
|
||||||
MyEnum[] enumArray;
|
MyEnum[] enumArray;
|
||||||
Set<MyEnum> enumSet;
|
Set<MyEnum> enumSet;
|
||||||
|
List<MyEnum> enumList;
|
||||||
}
|
}
|
||||||
|
|
||||||
static class WithConversion {
|
static class WithConversion {
|
||||||
@@ -216,4 +270,6 @@ public class PostgresReactiveDataAccessStrategyTests extends ReactiveDataAccessS
|
|||||||
return myObjects.toString();
|
return myObjects.toString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static class MyEnumSupport extends EnumWriteSupport<MyEnum> {}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -417,6 +417,16 @@ class QueryMapperUnitTests {
|
|||||||
assertThat(bindings.getCondition()).hasToString("person.alternative_name = ?[$1]");
|
assertThat(bindings.getCondition()).hasToString("person.alternative_name = ?[$1]");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test // gh-593
|
||||||
|
void mapQueryForEnumArrayShouldMapToStringList() {
|
||||||
|
|
||||||
|
Criteria criteria = Criteria.where("enumValue").in(MyEnum.ONE, MyEnum.TWO);
|
||||||
|
|
||||||
|
BoundCondition bindings = map(criteria);
|
||||||
|
|
||||||
|
assertThat(bindings.getCondition()).hasToString("person.enum_value IN (?[$1], ?[$2])");
|
||||||
|
}
|
||||||
|
|
||||||
private BoundCondition map(Criteria criteria) {
|
private BoundCondition map(Criteria criteria) {
|
||||||
|
|
||||||
BindMarkersFactory markers = BindMarkersFactory.indexed("$", 1);
|
BindMarkersFactory markers = BindMarkersFactory.indexed("$", 1);
|
||||||
@@ -429,5 +439,10 @@ class QueryMapperUnitTests {
|
|||||||
|
|
||||||
String name;
|
String name;
|
||||||
@Column("another_name") String alternative;
|
@Column("another_name") String alternative;
|
||||||
|
MyEnum enumValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum MyEnum {
|
||||||
|
ONE, TWO,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user