Skip to content

Commit 6685c78

Browse files
committed
Avoid NPE for anonymous SqlParameter in CallMetaDataContext
Issue: SPR-13628
1 parent ce20268 commit 6685c78

File tree

2 files changed

+78
-65
lines changed

2 files changed

+78
-65
lines changed

spring-jdbc/src/main/java/org/springframework/jdbc/core/metadata/CallMetaDataContext.java

Lines changed: 67 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -219,18 +219,21 @@ public boolean isAccessCallParameterMetaData() {
219219

220220
/**
221221
* Specify whether parameters should be bound by name.
222+
* @since 4.2
222223
*/
223224
public void setNamedBinding(boolean namedBinding) {
224225
this.namedBinding = namedBinding;
225226
}
226227

227228
/**
228229
* Check whether parameters should be bound by name.
230+
* @since 4.2
229231
*/
230232
public boolean isNamedBinding() {
231-
return namedBinding;
233+
return this.namedBinding;
232234
}
233235

236+
234237
/**
235238
* Create a ReturnResultSetParameter/SqlOutParameter depending on the support provided
236239
* by the JDBC driver used for the database in use.
@@ -297,88 +300,94 @@ public void processParameters(List<SqlParameter> parameters) {
297300
* Reconcile the provided parameters with available metadata and add new ones where appropriate.
298301
*/
299302
protected List<SqlParameter> reconcileParameters(List<SqlParameter> parameters) {
300-
final List<SqlParameter> declaredReturnParameters = new ArrayList<SqlParameter>();
301-
final Map<String, SqlParameter> declaredParameters = new LinkedHashMap<String, SqlParameter>();
303+
final List<SqlParameter> declaredReturnParams = new ArrayList<SqlParameter>();
304+
final Map<String, SqlParameter> declaredParams = new LinkedHashMap<String, SqlParameter>();
302305
boolean returnDeclared = false;
303-
List<String> outParameterNames = new ArrayList<String>();
304-
List<String> metaDataParameterNames = new ArrayList<String>();
306+
List<String> outParamNames = new ArrayList<String>();
307+
List<String> metaDataParamNames = new ArrayList<String>();
305308

306309
// Get the names of the meta data parameters
307310
for (CallParameterMetaData meta : this.metaDataProvider.getCallParameterMetaData()) {
308311
if (meta.getParameterType() != DatabaseMetaData.procedureColumnReturn) {
309-
metaDataParameterNames.add(meta.getParameterName().toLowerCase());
312+
metaDataParamNames.add(meta.getParameterName().toLowerCase());
310313
}
311314
}
312315

313316
// Separate implicit return parameters from explicit parameters...
314-
for (SqlParameter parameter : parameters) {
315-
if (parameter.isResultsParameter()) {
316-
declaredReturnParameters.add(parameter);
317+
for (SqlParameter param : parameters) {
318+
if (param.isResultsParameter()) {
319+
declaredReturnParams.add(param);
317320
}
318321
else {
319-
String parameterNameToMatch = this.metaDataProvider.parameterNameToUse(parameter.getName()).toLowerCase();
320-
declaredParameters.put(parameterNameToMatch, parameter);
321-
if (parameter instanceof SqlOutParameter) {
322-
outParameterNames.add(parameter.getName());
323-
if (isFunction() && !metaDataParameterNames.contains(parameterNameToMatch)) {
322+
String paramName = param.getName();
323+
if (paramName == null) {
324+
throw new IllegalArgumentException("Anonymous parameters not supported for calls - " +
325+
"please specify a name for the parameter of SQL type " + param.getSqlType());
326+
}
327+
String paramNameToMatch = this.metaDataProvider.parameterNameToUse(paramName).toLowerCase();
328+
declaredParams.put(paramNameToMatch, param);
329+
if (param instanceof SqlOutParameter) {
330+
outParamNames.add(paramName);
331+
if (isFunction() && !metaDataParamNames.contains(paramNameToMatch)) {
324332
if (!returnDeclared) {
325333
if (logger.isDebugEnabled()) {
326-
logger.debug("Using declared out parameter '" + parameter.getName() + "' for function return value");
334+
logger.debug("Using declared out parameter '" + paramName +
335+
"' for function return value");
327336
}
328-
setFunctionReturnName(parameter.getName());
337+
setFunctionReturnName(paramName);
329338
returnDeclared = true;
330339
}
331340
}
332341
}
333342
}
334343
}
335-
setOutParameterNames(outParameterNames);
344+
setOutParameterNames(outParamNames);
336345

337-
List<SqlParameter> workParameters = new ArrayList<SqlParameter>();
338-
workParameters.addAll(declaredReturnParameters);
346+
List<SqlParameter> workParams = new ArrayList<SqlParameter>();
347+
workParams.addAll(declaredReturnParams);
339348

340349
if (!this.metaDataProvider.isProcedureColumnMetaDataUsed()) {
341-
workParameters.addAll(declaredParameters.values());
342-
return workParameters;
350+
workParams.addAll(declaredParams.values());
351+
return workParams;
343352
}
344353

345354
Map<String, String> limitedInParamNamesMap = new HashMap<String, String>(this.limitedInParameterNames.size());
346-
for (String limitedParameterName : this.limitedInParameterNames) {
355+
for (String limitedParamName : this.limitedInParameterNames) {
347356
limitedInParamNamesMap.put(
348-
this.metaDataProvider.parameterNameToUse(limitedParameterName).toLowerCase(), limitedParameterName);
357+
this.metaDataProvider.parameterNameToUse(limitedParamName).toLowerCase(), limitedParamName);
349358
}
350359

351360
for (CallParameterMetaData meta : this.metaDataProvider.getCallParameterMetaData()) {
352-
String parNameToCheck = null;
361+
String paramNameToCheck = null;
353362
if (meta.getParameterName() != null) {
354-
parNameToCheck = this.metaDataProvider.parameterNameToUse(meta.getParameterName()).toLowerCase();
363+
paramNameToCheck = this.metaDataProvider.parameterNameToUse(meta.getParameterName()).toLowerCase();
355364
}
356-
String parNameToUse = this.metaDataProvider.parameterNameToUse(meta.getParameterName());
357-
if (declaredParameters.containsKey(parNameToCheck) ||
365+
String paramNameToUse = this.metaDataProvider.parameterNameToUse(meta.getParameterName());
366+
if (declaredParams.containsKey(paramNameToCheck) ||
358367
(meta.getParameterType() == DatabaseMetaData.procedureColumnReturn && returnDeclared)) {
359-
SqlParameter parameter;
368+
SqlParameter param;
360369
if (meta.getParameterType() == DatabaseMetaData.procedureColumnReturn) {
361-
parameter = declaredParameters.get(getFunctionReturnName());
362-
if (parameter == null && getOutParameterNames().size() > 0) {
363-
parameter = declaredParameters.get(getOutParameterNames().get(0).toLowerCase());
370+
param = declaredParams.get(getFunctionReturnName());
371+
if (param == null && getOutParameterNames().size() > 0) {
372+
param = declaredParams.get(getOutParameterNames().get(0).toLowerCase());
364373
}
365-
if (parameter == null) {
374+
if (param == null) {
366375
throw new InvalidDataAccessApiUsageException(
367376
"Unable to locate declared parameter for function return value - " +
368-
" add a SqlOutParameter with name \"" + getFunctionReturnName() +"\"");
377+
" add a SqlOutParameter with name '" + getFunctionReturnName() + "'");
369378
}
370379
else {
371-
setFunctionReturnName(parameter.getName());
380+
setFunctionReturnName(param.getName());
372381
}
373382
}
374383
else {
375-
parameter = declaredParameters.get(parNameToCheck);
384+
param = declaredParams.get(paramNameToCheck);
376385
}
377-
if (parameter != null) {
378-
workParameters.add(parameter);
386+
if (param != null) {
387+
workParams.add(param);
379388
if (logger.isDebugEnabled()) {
380-
logger.debug("Using declared parameter for: " +
381-
(parNameToUse == null ? getFunctionReturnName() : parNameToUse));
389+
logger.debug("Using declared parameter for '" +
390+
(paramNameToUse != null ? paramNameToUse : getFunctionReturnName()) + "'");
382391
}
383392
}
384393
}
@@ -387,57 +396,57 @@ protected List<SqlParameter> reconcileParameters(List<SqlParameter> parameters)
387396
if (!isFunction() && !isReturnValueRequired() &&
388397
this.metaDataProvider.byPassReturnParameter(meta.getParameterName())) {
389398
if (logger.isDebugEnabled()) {
390-
logger.debug("Bypassing metadata return parameter for: " + meta.getParameterName());
399+
logger.debug("Bypassing metadata return parameter for '" + meta.getParameterName() + "'");
391400
}
392401
}
393402
else {
394403
String returnNameToUse =(StringUtils.hasLength(meta.getParameterName()) ?
395-
parNameToUse : getFunctionReturnName());
396-
workParameters.add(this.metaDataProvider.createDefaultOutParameter(returnNameToUse, meta));
404+
paramNameToUse : getFunctionReturnName());
405+
workParams.add(this.metaDataProvider.createDefaultOutParameter(returnNameToUse, meta));
397406
if (isFunction()) {
398407
setFunctionReturnName(returnNameToUse);
399-
outParameterNames.add(returnNameToUse);
408+
outParamNames.add(returnNameToUse);
400409
}
401410
if (logger.isDebugEnabled()) {
402-
logger.debug("Added metadata return parameter for: " + returnNameToUse);
411+
logger.debug("Added metadata return parameter for '" + returnNameToUse + "'");
403412
}
404413
}
405414
}
406415
else {
407416
if (meta.getParameterType() == DatabaseMetaData.procedureColumnOut) {
408-
workParameters.add(this.metaDataProvider.createDefaultOutParameter(parNameToUse, meta));
409-
outParameterNames.add(parNameToUse);
417+
workParams.add(this.metaDataProvider.createDefaultOutParameter(paramNameToUse, meta));
418+
outParamNames.add(paramNameToUse);
410419
if (logger.isDebugEnabled()) {
411-
logger.debug("Added metadata out parameter for: " + parNameToUse);
420+
logger.debug("Added metadata out parameter for '" + paramNameToUse + "'");
412421
}
413422
}
414423
else if (meta.getParameterType() == DatabaseMetaData.procedureColumnInOut) {
415-
workParameters.add(this.metaDataProvider.createDefaultInOutParameter(parNameToUse, meta));
416-
outParameterNames.add(parNameToUse);
424+
workParams.add(this.metaDataProvider.createDefaultInOutParameter(paramNameToUse, meta));
425+
outParamNames.add(paramNameToUse);
417426
if (logger.isDebugEnabled()) {
418-
logger.debug("Added metadata in out parameter for: " + parNameToUse);
427+
logger.debug("Added metadata in out parameter for '" + paramNameToUse + "'");
419428
}
420429
}
421430
else {
422431
if (this.limitedInParameterNames.isEmpty() ||
423-
limitedInParamNamesMap.containsKey(parNameToUse.toLowerCase())) {
424-
workParameters.add(this.metaDataProvider.createDefaultInParameter(parNameToUse, meta));
432+
limitedInParamNamesMap.containsKey(paramNameToUse.toLowerCase())) {
433+
workParams.add(this.metaDataProvider.createDefaultInParameter(paramNameToUse, meta));
425434
if (logger.isDebugEnabled()) {
426-
logger.debug("Added metadata in parameter for: " + parNameToUse);
435+
logger.debug("Added metadata in parameter for '" + paramNameToUse + "'");
427436
}
428437
}
429438
else {
430439
if (logger.isDebugEnabled()) {
431440
logger.debug("Limited set of parameters " + limitedInParamNamesMap.keySet() +
432-
" skipped parameter for: " + parNameToUse);
441+
" skipped parameter for '" + paramNameToUse + "'");
433442
}
434443
}
435444
}
436445
}
437446
}
438447
}
439448

440-
return workParameters;
449+
return workParams;
441450
}
442451

443452
/**
@@ -626,11 +635,13 @@ public String createCallString() {
626635
* Build the parameter binding fragment.
627636
* @param parameter call parameter
628637
* @return parameter binding fragment
638+
* @since 4.2
629639
*/
630640
protected String createParameterBinding(SqlParameter parameter) {
631641
if (isNamedBinding()) {
632642
return parameter.getName() + " => ?";
633-
} else {
643+
}
644+
else {
634645
return "?";
635646
}
636647
}

spring-jdbc/src/main/java/org/springframework/jdbc/core/simple/AbstractJdbcCall.java

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -191,26 +191,28 @@ public boolean isReturnValueRequired() {
191191
}
192192

193193
/**
194-
* Specify whether the parameter metadata for the call should be used.
195-
* The default is {@code true}.
194+
* Specify whether parameters should be bound by name.
195+
* The default is {@code false}.
196+
* @since 4.2
196197
*/
197-
public void setAccessCallParameterMetaData(boolean accessCallParameterMetaData) {
198-
this.callMetaDataContext.setAccessCallParameterMetaData(accessCallParameterMetaData);
198+
public void setNamedBinding(boolean namedBinding) {
199+
this.callMetaDataContext.setNamedBinding(namedBinding);
199200
}
200201

201202
/**
202-
* Does parameters should be bound by name?
203+
* Should parameters be bound by name?
204+
* @since 4.2
203205
*/
204206
public boolean isNamedBinding() {
205207
return this.callMetaDataContext.isNamedBinding();
206208
}
207209

208210
/**
209-
* Specify whether parameters should be bound by name.
210-
* The default is {@code false}.
211+
* Specify whether the parameter metadata for the call should be used.
212+
* The default is {@code true}.
211213
*/
212-
public void setNamedBinding(boolean namedBinding) {
213-
this.callMetaDataContext.setNamedBinding(namedBinding);
214+
public void setAccessCallParameterMetaData(boolean accessCallParameterMetaData) {
215+
this.callMetaDataContext.setAccessCallParameterMetaData(accessCallParameterMetaData);
214216
}
215217

216218
/**

0 commit comments

Comments
 (0)