Skip to content

Commit 2e116df

Browse files
committed
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
1 parent 3513c45 commit 2e116df

File tree

3 files changed

+86
-13
lines changed

3 files changed

+86
-13
lines changed

src/main/java/org/springframework/data/r2dbc/core/DefaultReactiveDataAccessStrategy.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -247,14 +247,16 @@ private Parameter getArrayValue(Parameter value, RelationalPersistentProperty pr
247247
Class<?> actualType = null;
248248
if (value.getValue() instanceof Collection) {
249249
actualType = CollectionUtils.findCommonElementType((Collection<?>) value.getValue());
250-
} else if (value.getClass().isArray()) {
251-
actualType = value.getClass().getComponentType();
250+
} else if (!value.isEmpty() && value.getValue().getClass().isArray()) {
251+
actualType = value.getValue().getClass().getComponentType();
252252
}
253253

254254
if (actualType == null) {
255255
actualType = property.getActualType();
256256
}
257257

258+
actualType = converter.getTargetType(actualType);
259+
258260
if (value.isEmpty()) {
259261

260262
Class<?> targetType = arrayColumns.getArrayType(actualType);

src/test/java/org/springframework/data/r2dbc/core/PostgresReactiveDataAccessStrategyTests.java

Lines changed: 67 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929

3030
import org.springframework.core.convert.converter.Converter;
3131
import org.springframework.data.convert.WritingConverter;
32+
import org.springframework.data.r2dbc.convert.EnumWriteSupport;
3233
import org.springframework.data.r2dbc.dialect.PostgresDialect;
3334
import org.springframework.data.r2dbc.mapping.OutboundRow;
3435
import org.springframework.data.relational.core.sql.SqlIdentifier;
@@ -126,38 +127,90 @@ void shouldApplyCustomConversionForNull() {
126127
assertThat(value.getType()).isEqualTo(String.class);
127128
}
128129

129-
@Test // gh-252
130-
void shouldConvertSetOfEnumToString() {
130+
@Test // gh-252, gh-593
131+
void shouldConvertCollectionOfEnumToString() {
131132

132-
DefaultReactiveDataAccessStrategy strategy = new DefaultReactiveDataAccessStrategy(PostgresDialect.INSTANCE,
133-
Collections.singletonList(MyObjectsToStringConverter.INSTANCE));
133+
DefaultReactiveDataAccessStrategy strategy = new DefaultReactiveDataAccessStrategy(PostgresDialect.INSTANCE);
134134

135135
WithEnumCollections withEnums = new WithEnumCollections();
136136
withEnums.enumSet = EnumSet.of(MyEnum.ONE, MyEnum.TWO);
137+
withEnums.enumList = Arrays.asList(MyEnum.ONE, MyEnum.TWO);
138+
withEnums.enumArray = new MyEnum[] { MyEnum.ONE, MyEnum.TWO };
137139

138140
OutboundRow outboundRow = strategy.getOutboundRow(withEnums);
139141

140142
assertThat(outboundRow).containsKey(SqlIdentifier.unquoted("enum_set"));
143+
assertThat(outboundRow.get(SqlIdentifier.unquoted("enum_set")).getValue()).isEqualTo(new String[] { "ONE", "TWO" });
144+
145+
assertThat(outboundRow).containsKey(SqlIdentifier.unquoted("enum_array"));
146+
assertThat(outboundRow.get(SqlIdentifier.unquoted("enum_array")).getValue())
147+
.isEqualTo(new String[] { "ONE", "TWO" });
148+
149+
assertThat(outboundRow).containsKey(SqlIdentifier.unquoted("enum_list"));
150+
assertThat(outboundRow.get(SqlIdentifier.unquoted("enum_list")).getValue())
151+
.isEqualTo(new String[] { "ONE", "TWO" });
152+
}
153+
154+
@Test // gh-593
155+
void shouldCorrectlyWriteConvertedEnumNullValues() {
156+
157+
DefaultReactiveDataAccessStrategy strategy = new DefaultReactiveDataAccessStrategy(PostgresDialect.INSTANCE);
158+
159+
WithEnumCollections withEnums = new WithEnumCollections();
160+
161+
OutboundRow outboundRow = strategy.getOutboundRow(withEnums);
162+
163+
assertThat(outboundRow).containsKey(SqlIdentifier.unquoted("enum_set"));
164+
assertThat(outboundRow.get(SqlIdentifier.unquoted("enum_set")).getType()).isEqualTo(String[].class);
165+
166+
assertThat(outboundRow).containsKey(SqlIdentifier.unquoted("enum_array"));
167+
assertThat(outboundRow.get(SqlIdentifier.unquoted("enum_array")).getType()).isEqualTo(String[].class);
141168

142-
Parameter value = outboundRow.get(SqlIdentifier.unquoted("enum_set"));
143-
assertThat(value.getValue()).isEqualTo(new String[] { "ONE", "TWO" });
169+
assertThat(outboundRow).containsKey(SqlIdentifier.unquoted("enum_list"));
170+
assertThat(outboundRow.get(SqlIdentifier.unquoted("enum_list")).getType()).isEqualTo(String[].class);
144171
}
145172

146-
@Test // gh-252
147-
void shouldConvertArrayOfEnumToString() {
173+
@Test // gh-593
174+
void shouldConvertCollectionOfEnumNatively() {
148175

149176
DefaultReactiveDataAccessStrategy strategy = new DefaultReactiveDataAccessStrategy(PostgresDialect.INSTANCE,
150-
Collections.singletonList(MyObjectsToStringConverter.INSTANCE));
177+
Collections.singletonList(new MyEnumSupport()));
151178

152179
WithEnumCollections withEnums = new WithEnumCollections();
180+
withEnums.enumSet = EnumSet.of(MyEnum.ONE, MyEnum.TWO);
181+
withEnums.enumList = Arrays.asList(MyEnum.ONE, MyEnum.TWO);
153182
withEnums.enumArray = new MyEnum[] { MyEnum.ONE, MyEnum.TWO };
154183

155184
OutboundRow outboundRow = strategy.getOutboundRow(withEnums);
156185

186+
assertThat(outboundRow).containsKey(SqlIdentifier.unquoted("enum_set"));
187+
assertThat(outboundRow.get(SqlIdentifier.unquoted("enum_set")).getValue()).isInstanceOf(MyEnum[].class);
188+
189+
assertThat(outboundRow).containsKey(SqlIdentifier.unquoted("enum_array"));
190+
assertThat(outboundRow.get(SqlIdentifier.unquoted("enum_array")).getValue()).isInstanceOf(MyEnum[].class);
191+
192+
assertThat(outboundRow).containsKey(SqlIdentifier.unquoted("enum_list"));
193+
assertThat(outboundRow.get(SqlIdentifier.unquoted("enum_list")).getValue()).isInstanceOf(MyEnum[].class);
194+
}
195+
196+
@Test // gh-593
197+
void shouldCorrectlyWriteNativeEnumNullValues() {
198+
199+
DefaultReactiveDataAccessStrategy strategy = new DefaultReactiveDataAccessStrategy(PostgresDialect.INSTANCE,
200+
Collections.singletonList(new MyEnumSupport()));
201+
202+
WithEnumCollections withEnums = new WithEnumCollections();
203+
204+
OutboundRow outboundRow = strategy.getOutboundRow(withEnums);
205+
206+
assertThat(outboundRow).containsKey(SqlIdentifier.unquoted("enum_set"));
207+
assertThat(outboundRow.get(SqlIdentifier.unquoted("enum_set")).getType()).isEqualTo(MyEnum[].class);
208+
157209
assertThat(outboundRow).containsKey(SqlIdentifier.unquoted("enum_array"));
210+
assertThat(outboundRow.get(SqlIdentifier.unquoted("enum_array")).getType()).isEqualTo(MyEnum[].class);
158211

159-
Parameter value = outboundRow.get(SqlIdentifier.unquoted("enum_array"));
160-
assertThat(value.getValue()).isEqualTo(new String[] { "ONE", "TWO" });
212+
assertThat(outboundRow).containsKey(SqlIdentifier.unquoted("enum_list"));
213+
assertThat(outboundRow.get(SqlIdentifier.unquoted("enum_list")).getType()).isEqualTo(MyEnum[].class);
161214
}
162215

163216
@RequiredArgsConstructor
@@ -182,6 +235,7 @@ static class WithEnumCollections {
182235

183236
MyEnum[] enumArray;
184237
Set<MyEnum> enumSet;
238+
List<MyEnum> enumList;
185239
}
186240

187241
static class WithConversion {
@@ -216,4 +270,6 @@ public String convert(List<MyObject> myObjects) {
216270
return myObjects.toString();
217271
}
218272
}
273+
274+
private static class MyEnumSupport extends EnumWriteSupport<MyEnum> {}
219275
}

src/test/java/org/springframework/data/r2dbc/query/QueryMapperUnitTests.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -417,6 +417,16 @@ void mapQueryForPropertyPathInPrimitiveShouldFallBackToColumnName() {
417417
assertThat(bindings.getCondition()).hasToString("person.alternative_name = ?[$1]");
418418
}
419419

420+
@Test // gh-593
421+
void mapQueryForEnumArrayShouldMapToStringList() {
422+
423+
Criteria criteria = Criteria.where("enumValue").in(MyEnum.ONE, MyEnum.TWO);
424+
425+
BoundCondition bindings = map(criteria);
426+
427+
assertThat(bindings.getCondition()).hasToString("person.enum_value IN (?[$1], ?[$2])");
428+
}
429+
420430
private BoundCondition map(Criteria criteria) {
421431

422432
BindMarkersFactory markers = BindMarkersFactory.indexed("$", 1);
@@ -429,5 +439,10 @@ static class Person {
429439

430440
String name;
431441
@Column("another_name") String alternative;
442+
MyEnum enumValue;
443+
}
444+
445+
enum MyEnum {
446+
ONE, TWO,
432447
}
433448
}

0 commit comments

Comments
 (0)