Skip to content

Commit f0d3b5e

Browse files
committed
Polishing.
Refactor convertAndAddParameter method to writeValue(…) decoupling responsibilities for a clearer value conversion code path. Also, refactor collection conversion to functional callback-style and extend test assertions. See #1323 Original pull request: #1838
1 parent 8241aa1 commit f0d3b5e

File tree

2 files changed

+76
-53
lines changed

2 files changed

+76
-53
lines changed

spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/StringBasedJdbcQuery.java

+73-52
Original file line numberDiff line numberDiff line change
@@ -23,21 +23,18 @@
2323
import java.sql.SQLType;
2424
import java.util.ArrayList;
2525
import java.util.Collection;
26+
import java.util.LinkedHashMap;
2627
import java.util.List;
27-
import java.util.Map;
2828
import java.util.function.Function;
2929
import java.util.function.Supplier;
3030

3131
import org.springframework.beans.BeanInstantiationException;
3232
import org.springframework.beans.BeanUtils;
3333
import org.springframework.beans.factory.BeanFactory;
34-
import org.springframework.data.jdbc.core.convert.JdbcColumnTypes;
3534
import org.springframework.data.jdbc.core.convert.JdbcConverter;
3635
import org.springframework.data.jdbc.core.mapping.JdbcValue;
37-
import org.springframework.data.jdbc.support.JdbcUtil;
3836
import org.springframework.data.relational.core.mapping.RelationalMappingContext;
3937
import org.springframework.data.relational.repository.query.RelationalParameterAccessor;
40-
import org.springframework.data.relational.repository.query.RelationalParameters;
4138
import org.springframework.data.relational.repository.query.RelationalParametersParameterAccessor;
4239
import org.springframework.data.repository.query.Parameter;
4340
import org.springframework.data.repository.query.Parameters;
@@ -47,15 +44,13 @@
4744
import org.springframework.data.repository.query.SpelQueryContext;
4845
import org.springframework.data.util.Lazy;
4946
import org.springframework.data.util.TypeInformation;
50-
import org.springframework.data.util.TypeUtils;
5147
import org.springframework.jdbc.core.ResultSetExtractor;
5248
import org.springframework.jdbc.core.RowMapper;
5349
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
5450
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations;
5551
import org.springframework.lang.Nullable;
5652
import org.springframework.util.Assert;
5753
import org.springframework.util.ClassUtils;
58-
import org.springframework.util.ConcurrentReferenceHashMap;
5954
import org.springframework.util.ObjectUtils;
6055

6156
/**
@@ -75,7 +70,7 @@
7570
*/
7671
public class StringBasedJdbcQuery extends AbstractJdbcQuery {
7772

78-
private static final String PARAMETER_NEEDS_TO_BE_NAMED = "For queries with named parameters you need to provide names for method parameters; Use @Param for query method parameters, or when on Java 8+ use the javac flag -parameters";
73+
private static final String PARAMETER_NEEDS_TO_BE_NAMED = "For queries with named parameters you need to provide names for method parameters; Use @Param for query method parameters, or use the javac flag -parameters";
7974
private final JdbcConverter converter;
8075
private final RowMapperFactory rowMapperFactory;
8176
private final SpelEvaluator spelEvaluator;
@@ -188,77 +183,103 @@ private JdbcQueryExecution<?> createJdbcQueryExecution(RelationalParameterAccess
188183

189184
private MapSqlParameterSource bindParameters(RelationalParameterAccessor accessor) {
190185

191-
MapSqlParameterSource parameters = new MapSqlParameterSource();
192-
193186
Parameters<?, ?> bindableParameters = accessor.getBindableParameters();
187+
MapSqlParameterSource parameters = new MapSqlParameterSource(
188+
new LinkedHashMap<>(bindableParameters.getNumberOfParameters(), 1.0f));
194189

195190
for (Parameter bindableParameter : bindableParameters) {
196-
convertAndAddParameter(parameters, bindableParameter, accessor.getBindableValue(bindableParameter.getIndex()));
191+
192+
Object value = accessor.getBindableValue(bindableParameter.getIndex());
193+
String parameterName = bindableParameter.getName()
194+
.orElseThrow(() -> new IllegalStateException(PARAMETER_NEEDS_TO_BE_NAMED));
195+
JdbcParameters.JdbcParameter parameter = getQueryMethod().getParameters()
196+
.getParameter(bindableParameter.getIndex());
197+
198+
JdbcValue jdbcValue = writeValue(value, parameter.getTypeInformation(), parameter);
199+
SQLType jdbcType = jdbcValue.getJdbcType();
200+
201+
if (jdbcType == null) {
202+
parameters.addValue(parameterName, jdbcValue.getValue());
203+
} else {
204+
parameters.addValue(parameterName, jdbcValue.getValue(), jdbcType.getVendorTypeNumber());
205+
}
197206
}
198207

199208
return parameters;
200209
}
201210

202-
private void convertAndAddParameter(MapSqlParameterSource parameters, Parameter p, Object value) {
211+
private JdbcValue writeValue(@Nullable Object value, TypeInformation<?> typeInformation,
212+
JdbcParameters.JdbcParameter parameter) {
203213

204-
String parameterName = p.getName().orElseThrow(() -> new IllegalStateException(PARAMETER_NEEDS_TO_BE_NAMED));
214+
if (value == null) {
215+
return JdbcValue.of(value, parameter.getSqlType());
216+
}
205217

206-
JdbcParameters.JdbcParameter parameter = getQueryMethod().getParameters().getParameter(p.getIndex());
207-
TypeInformation<?> typeInformation = parameter.getTypeInformation();
218+
if (typeInformation.isCollectionLike() && value instanceof Collection<?> collection) {
208219

209-
JdbcValue jdbcValue;
210-
if (typeInformation.isCollectionLike() //
211-
&& value instanceof Collection<?> collectionValue//
212-
) {
213-
if ( typeInformation.getActualType().getType().isArray() ){
220+
TypeInformation<?> actualType = typeInformation.getActualType();
214221

215-
TypeInformation<?> arrayElementType = typeInformation.getActualType().getActualType();
222+
// tuple-binding
223+
if (actualType != null && actualType.getType().isArray()) {
216224

217-
List<Object[]> mapped = new ArrayList<>();
225+
TypeInformation<?> nestedElementType = actualType.getRequiredActualType();
226+
return writeCollection(collection, JDBCType.OTHER,
227+
array -> writeArrayValue(parameter, array, nestedElementType));
228+
}
218229

219-
for (Object array : collectionValue) {
220-
int length = Array.getLength(array);
221-
Object[] mappedArray = new Object[length];
230+
// parameter expansion
231+
return writeCollection(collection, parameter.getActualSqlType(),
232+
it -> converter.writeJdbcValue(it, typeInformation.getRequiredActualType(), parameter.getActualSqlType()));
233+
}
222234

223-
for (int i = 0; i < length; i++) {
224-
Object element = Array.get(array, i);
225-
JdbcValue elementJdbcValue = converter.writeJdbcValue(element, arrayElementType, parameter.getActualSqlType());
235+
SQLType sqlType = parameter.getSqlType();
236+
return converter.writeJdbcValue(value, typeInformation, sqlType);
237+
}
226238

227-
mappedArray[i] = elementJdbcValue.getValue();
228-
}
229-
mapped.add(mappedArray);
230-
}
231-
jdbcValue = JdbcValue.of(mapped, JDBCType.OTHER);
239+
private JdbcValue writeCollection(Collection<?> value, SQLType defaultType, Function<Object, Object> mapper) {
232240

233-
} else {
234-
List<Object> mapped = new ArrayList<>();
235-
SQLType jdbcType = null;
241+
if (value.isEmpty()) {
242+
return JdbcValue.of(value, defaultType);
243+
}
236244

237-
TypeInformation<?> actualType = typeInformation.getRequiredActualType();
238-
for (Object o : collectionValue) {
239-
JdbcValue elementJdbcValue = converter.writeJdbcValue(o, actualType, parameter.getActualSqlType());
240-
if (jdbcType == null) {
241-
jdbcType = elementJdbcValue.getJdbcType();
242-
}
245+
JdbcValue jdbcValue;
246+
List<Object> mapped = new ArrayList<>(value.size());
247+
SQLType jdbcType = null;
243248

244-
mapped.add(elementJdbcValue.getValue());
245-
}
249+
for (Object o : value) {
246250

247-
jdbcValue = JdbcValue.of(mapped, jdbcType);
251+
Object mappedValue = mapper.apply(o);
252+
253+
if (mappedValue instanceof JdbcValue jv) {
254+
if (jdbcType == null) {
255+
jdbcType = jv.getJdbcType();
256+
}
257+
mappedValue = jv.getValue();
248258
}
249-
} else {
250259

251-
SQLType sqlType = parameter.getSqlType();
252-
jdbcValue = converter.writeJdbcValue(value, typeInformation, sqlType);
260+
mapped.add(mappedValue);
253261
}
254262

255-
SQLType jdbcType = jdbcValue.getJdbcType();
256-
if (jdbcType == null) {
263+
jdbcValue = JdbcValue.of(mapped, jdbcType == null ? defaultType : jdbcType);
257264

258-
parameters.addValue(parameterName, jdbcValue.getValue());
259-
} else {
260-
parameters.addValue(parameterName, jdbcValue.getValue(), jdbcType.getVendorTypeNumber());
265+
return jdbcValue;
266+
}
267+
268+
private Object[] writeArrayValue(JdbcParameters.JdbcParameter parameter, Object array,
269+
TypeInformation<?> nestedElementType) {
270+
271+
int length = Array.getLength(array);
272+
Object[] mappedArray = new Object[length];
273+
274+
for (int i = 0; i < length; i++) {
275+
276+
Object element = Array.get(array, i);
277+
JdbcValue elementJdbcValue = converter.writeJdbcValue(element, nestedElementType, parameter.getActualSqlType());
278+
279+
mappedArray[i] = elementJdbcValue.getValue();
261280
}
281+
282+
return mappedArray;
262283
}
263284

264285
RowMapper<Object> determineRowMapper(ResultProcessor resultProcessor, boolean hasDynamicProjection) {

spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryIntegrationTests.java

+3-1
Original file line numberDiff line numberDiff line change
@@ -1350,7 +1350,9 @@ void queryWithTupleIn() {
13501350
new Object[]{three.idProp, "two"} // matches nothing
13511351
);
13521352

1353-
repository.findByListInTuple(tuples);
1353+
List<DummyEntity> result = repository.findByListInTuple(tuples);
1354+
1355+
assertThat(result).containsOnly(two);
13541356
}
13551357

13561358
private Root createRoot(String namePrefix) {

0 commit comments

Comments
 (0)