diff --git a/src/main/java/org/springframework/data/r2dbc/query/QueryMapper.java b/src/main/java/org/springframework/data/r2dbc/query/QueryMapper.java index fab7735..28172fb 100644 --- a/src/main/java/org/springframework/data/r2dbc/query/QueryMapper.java +++ b/src/main/java/org/springframework/data/r2dbc/query/QueryMapper.java @@ -348,24 +348,25 @@ public class QueryMapper { Object mappedValue; Class typeHint; + Comparator comparator = criteria.getComparator(); if (criteria.getValue() instanceof SettableValue) { SettableValue settableValue = (SettableValue) criteria.getValue(); - mappedValue = convertValue(settableValue.getValue(), propertyField.getTypeHint()); + mappedValue = convertValue(comparator, settableValue.getValue(), propertyField.getTypeHint()); typeHint = getTypeHint(mappedValue, actualType.getType(), settableValue); } else if (criteria.getValue() instanceof Parameter) { Parameter parameter = (Parameter) criteria.getValue(); - mappedValue = convertValue(parameter.getValue(), propertyField.getTypeHint()); + mappedValue = convertValue(comparator, parameter.getValue(), propertyField.getTypeHint()); typeHint = getTypeHint(mappedValue, actualType.getType(), parameter); } else if (criteria.getValue() instanceof ValueFunction) { ValueFunction valueFunction = (ValueFunction) criteria.getValue(); - Object value = valueFunction.apply(getEscaper(criteria.getComparator())); + Object value = valueFunction.apply(getEscaper(comparator)); - mappedValue = convertValue(value, propertyField.getTypeHint()); + mappedValue = convertValue(comparator, value, propertyField.getTypeHint()); typeHint = actualType.getType(); } else { @@ -373,18 +374,18 @@ public class QueryMapper { // Translate bind values for comparators that are bound as value but don't include a value. if (value == null) { - if (criteria.getComparator() == Comparator.IS_TRUE) { + if (comparator == Comparator.IS_TRUE) { value = true; - } else if (criteria.getComparator() == Comparator.IS_FALSE) { + } else if (comparator == Comparator.IS_FALSE) { value = false; } } - mappedValue = convertValue(value, propertyField.getTypeHint()); + mappedValue = convertValue(comparator, value, propertyField.getTypeHint()); typeHint = actualType.getType(); } - return createCondition(column, mappedValue, typeHint, bindings, criteria.getComparator(), criteria.isIgnoreCase()); + return createCondition(column, mappedValue, typeHint, bindings, comparator, criteria.isIgnoreCase()); } private Escaper getEscaper(Comparator comparator) { @@ -427,6 +428,24 @@ public class QueryMapper { return Parameter.from(convertValue(value.getValue(), ClassTypeInformation.OBJECT)); } + @Nullable + private Object convertValue(@Nullable Comparator comparator, @Nullable Object value, TypeInformation typeHint) { + + if (Comparator.IN.equals(comparator) && value instanceof Collection && !((Collection) value).isEmpty()) { + + Collection collection = (Collection) value; + Collection mapped = new ArrayList<>(collection.size()); + + for (Object o : collection) { + mapped.add(convertValue(o, typeHint)); + } + + return mapped; + } + + return convertValue(value, typeHint); + } + @Nullable protected Object convertValue(@Nullable Object value, TypeInformation typeInformation) { @@ -449,23 +468,6 @@ public class QueryMapper { return Pair.of(first, second); } - if (value instanceof Iterable) { - - List mapped = new ArrayList<>(); - - for (Object o : (Iterable) value) { - mapped.add(convertValue(o, typeInformation.getActualType() != null ? typeInformation.getRequiredActualType() - : ClassTypeInformation.OBJECT)); - } - - return mapped; - } - - if (value.getClass().isArray() - && (ClassTypeInformation.OBJECT.equals(typeInformation) || typeInformation.isCollectionLike())) { - return value; - } - return this.converter.writeValue(value, typeInformation); } diff --git a/src/test/java/org/springframework/data/r2dbc/query/QueryMapperUnitTests.java b/src/test/java/org/springframework/data/r2dbc/query/QueryMapperUnitTests.java index 15102ba..a9173f8 100644 --- a/src/test/java/org/springframework/data/r2dbc/query/QueryMapperUnitTests.java +++ b/src/test/java/org/springframework/data/r2dbc/query/QueryMapperUnitTests.java @@ -23,6 +23,7 @@ import java.util.Collections; import org.junit.jupiter.api.Test; +import org.springframework.core.convert.converter.Converter; import org.springframework.data.domain.Sort; import org.springframework.data.r2dbc.convert.MappingR2dbcConverter; import org.springframework.data.r2dbc.convert.R2dbcConverter; @@ -40,6 +41,9 @@ import org.springframework.data.relational.core.sql.Expression; import org.springframework.data.relational.core.sql.Functions; import org.springframework.data.relational.core.sql.Table; +import org.testcontainers.shaded.com.fasterxml.jackson.databind.JsonNode; +import org.testcontainers.shaded.com.fasterxml.jackson.databind.node.TextNode; + /** * Unit tests for {@link QueryMapper}. * @@ -53,7 +57,8 @@ class QueryMapperUnitTests { QueryMapper createMapper(R2dbcDialect dialect) { - R2dbcCustomConversions conversions = R2dbcCustomConversions.of(dialect); + R2dbcCustomConversions conversions = R2dbcCustomConversions.of(dialect, JsonNodeToStringConverter.INSTANCE, + StringToJsonNodeConverter.INSTANCE); R2dbcMappingContext context = new R2dbcMappingContext(); context.setSimpleTypeHolder(conversions.getSimpleTypeHolder()); @@ -463,6 +468,28 @@ class QueryMapperUnitTests { assertThat(bindings.getBindings().iterator().next().getValue()).isEqualTo((byte) 1); } + @Test // gh-1452 + void shouldMapJsonNodeToString() { + + Criteria criteria = Criteria.where("jsonNode").is(new TextNode("foo")); + + BoundCondition bindings = map(criteria); + + assertThat(bindings.getCondition()).hasToString("person.json_node = ?[$1]"); + assertThat(bindings.getBindings().iterator().next().getValue()).isEqualTo("foo"); + } + + @Test // gh-1452 + void shouldMapJsonNodeListToString() { + + Criteria criteria = Criteria.where("jsonNode").in(new TextNode("foo"), new TextNode("bar")); + + BoundCondition bindings = map(criteria); + + assertThat(bindings.getCondition()).hasToString("person.json_node IN (?[$1], ?[$2])"); + assertThat(bindings.getBindings().iterator().next().getValue()).isEqualTo("foo"); + } + private BoundCondition map(Criteria criteria) { BindMarkersFactory markers = BindMarkersFactory.indexed("$", 1); @@ -478,9 +505,29 @@ class QueryMapperUnitTests { MyEnum enumValue; boolean state; + + JsonNode jsonNode; } enum MyEnum { ONE, TWO, } + + enum JsonNodeToStringConverter implements Converter { + INSTANCE; + + @Override + public String convert(JsonNode source) { + return source.asText(); + } + } + + enum StringToJsonNodeConverter implements Converter { + INSTANCE; + + @Override + public JsonNode convert(String source) { + return new TextNode(source); + } + } }