@Override
 public SqlParameter createDefaultOutParameter(String parameterName, CallParameterMetaData meta) {
   if (meta.getSqlType() == Types.OTHER && REF_CURSOR_NAME.equals(meta.getTypeName())) {
     return new SqlOutParameter(parameterName, getRefCursorSqlType(), new ColumnMapRowMapper());
   } else {
     return super.createDefaultOutParameter(parameterName, meta);
   }
 }
  /**
   * Reconcile the provided parameters with available metadata and add new ones where appropriate.
   */
  protected List<SqlParameter> reconcileParameters(List<SqlParameter> parameters) {
    final List<SqlParameter> declaredReturnParams = new ArrayList<SqlParameter>();
    final Map<String, SqlParameter> declaredParams = new LinkedHashMap<String, SqlParameter>();
    boolean returnDeclared = false;
    List<String> outParamNames = new ArrayList<String>();
    List<String> metaDataParamNames = new ArrayList<String>();

    // Get the names of the meta data parameters
    for (CallParameterMetaData meta : this.metaDataProvider.getCallParameterMetaData()) {
      if (meta.getParameterType() != DatabaseMetaData.procedureColumnReturn) {
        metaDataParamNames.add(meta.getParameterName().toLowerCase());
      }
    }

    // Separate implicit return parameters from explicit parameters...
    for (SqlParameter param : parameters) {
      if (param.isResultsParameter()) {
        declaredReturnParams.add(param);
      } else {
        String paramName = param.getName();
        if (paramName == null) {
          throw new IllegalArgumentException(
              "Anonymous parameters not supported for calls - "
                  + "please specify a name for the parameter of SQL type "
                  + param.getSqlType());
        }
        String paramNameToMatch = this.metaDataProvider.parameterNameToUse(paramName).toLowerCase();
        declaredParams.put(paramNameToMatch, param);
        if (param instanceof SqlOutParameter) {
          outParamNames.add(paramName);
          if (isFunction() && !metaDataParamNames.contains(paramNameToMatch)) {
            if (!returnDeclared) {
              if (logger.isDebugEnabled()) {
                logger.debug(
                    "Using declared out parameter '" + paramName + "' for function return value");
              }
              setFunctionReturnName(paramName);
              returnDeclared = true;
            }
          }
        }
      }
    }
    setOutParameterNames(outParamNames);

    List<SqlParameter> workParams = new ArrayList<SqlParameter>();
    workParams.addAll(declaredReturnParams);

    if (!this.metaDataProvider.isProcedureColumnMetaDataUsed()) {
      workParams.addAll(declaredParams.values());
      return workParams;
    }

    Map<String, String> limitedInParamNamesMap =
        new HashMap<String, String>(this.limitedInParameterNames.size());
    for (String limitedParamName : this.limitedInParameterNames) {
      limitedInParamNamesMap.put(
          this.metaDataProvider.parameterNameToUse(limitedParamName).toLowerCase(),
          limitedParamName);
    }

    for (CallParameterMetaData meta : this.metaDataProvider.getCallParameterMetaData()) {
      String paramNameToCheck = null;
      if (meta.getParameterName() != null) {
        paramNameToCheck =
            this.metaDataProvider.parameterNameToUse(meta.getParameterName()).toLowerCase();
      }
      String paramNameToUse = this.metaDataProvider.parameterNameToUse(meta.getParameterName());
      if (declaredParams.containsKey(paramNameToCheck)
          || (meta.getParameterType() == DatabaseMetaData.procedureColumnReturn
              && returnDeclared)) {
        SqlParameter param;
        if (meta.getParameterType() == DatabaseMetaData.procedureColumnReturn) {
          param = declaredParams.get(getFunctionReturnName());
          if (param == null && getOutParameterNames().size() > 0) {
            param = declaredParams.get(getOutParameterNames().get(0).toLowerCase());
          }
          if (param == null) {
            throw new InvalidDataAccessApiUsageException(
                "Unable to locate declared parameter for function return value - "
                    + " add a SqlOutParameter with name '"
                    + getFunctionReturnName()
                    + "'");
          } else {
            setFunctionReturnName(param.getName());
          }
        } else {
          param = declaredParams.get(paramNameToCheck);
        }
        if (param != null) {
          workParams.add(param);
          if (logger.isDebugEnabled()) {
            logger.debug(
                "Using declared parameter for '"
                    + (paramNameToUse != null ? paramNameToUse : getFunctionReturnName())
                    + "'");
          }
        }
      } else {
        if (meta.getParameterType() == DatabaseMetaData.procedureColumnReturn) {
          if (!isFunction()
              && !isReturnValueRequired()
              && this.metaDataProvider.byPassReturnParameter(meta.getParameterName())) {
            if (logger.isDebugEnabled()) {
              logger.debug(
                  "Bypassing metadata return parameter for '" + meta.getParameterName() + "'");
            }
          } else {
            String returnNameToUse =
                (StringUtils.hasLength(meta.getParameterName())
                    ? paramNameToUse
                    : getFunctionReturnName());
            workParams.add(this.metaDataProvider.createDefaultOutParameter(returnNameToUse, meta));
            if (isFunction()) {
              setFunctionReturnName(returnNameToUse);
              outParamNames.add(returnNameToUse);
            }
            if (logger.isDebugEnabled()) {
              logger.debug("Added metadata return parameter for '" + returnNameToUse + "'");
            }
          }
        } else {
          if (meta.getParameterType() == DatabaseMetaData.procedureColumnOut) {
            workParams.add(this.metaDataProvider.createDefaultOutParameter(paramNameToUse, meta));
            outParamNames.add(paramNameToUse);
            if (logger.isDebugEnabled()) {
              logger.debug("Added metadata out parameter for '" + paramNameToUse + "'");
            }
          } else if (meta.getParameterType() == DatabaseMetaData.procedureColumnInOut) {
            workParams.add(this.metaDataProvider.createDefaultInOutParameter(paramNameToUse, meta));
            outParamNames.add(paramNameToUse);
            if (logger.isDebugEnabled()) {
              logger.debug("Added metadata in out parameter for '" + paramNameToUse + "'");
            }
          } else {
            if (this.limitedInParameterNames.isEmpty()
                || limitedInParamNamesMap.containsKey(paramNameToUse.toLowerCase())) {
              workParams.add(this.metaDataProvider.createDefaultInParameter(paramNameToUse, meta));
              if (logger.isDebugEnabled()) {
                logger.debug("Added metadata in parameter for '" + paramNameToUse + "'");
              }
            } else {
              if (logger.isDebugEnabled()) {
                logger.debug(
                    "Limited set of parameters "
                        + limitedInParamNamesMap.keySet()
                        + " skipped parameter for '"
                        + paramNameToUse
                        + "'");
              }
            }
          }
        }
      }
    }

    return workParams;
  }