Spring SimpleJdbcCall default (optional) arguments

Ater giving up on this question and just passing all the parameters, including optional ones I ran into its inability to pass boolean arguments, because boolean is not an SQL data type, only PL/SQL.

So my current solution is that JDBC is not suited for running stored procedures and this is how I'm working around it:

  jdbcTemplate.execute(
        new CallableStatementCreator() {
           public CallableStatement createCallableStatement(Connection con) throws SQLException{
              CallableStatement cs = con.prepareCall("{call sys.dbms_stats.gather_table_stats(ownname=>user, tabname=>'" + cachedMetadataTableName + "', estimate_percent=>20, method_opt=>'FOR ALL COLUMNS SIZE 1', degree=>0, granularity=>'AUTO', cascade=>TRUE, no_invalidate=>FALSE, force=>FALSE) }");
              return cs;
           }
        },
        new CallableStatementCallback() {
           public Object doInCallableStatement(CallableStatement cs) throws SQLException{
              cs.execute();
              return null; // Whatever is returned here is returned from the jdbcTemplate.execute method
           }
        }
  );

Came up with a decent solution to this today, that copes with non-null defaults, and does not use fruity reflection techniques. It works by creating the metadata context for the function externally to retrieve all the parameter types and so forth, then constructing the SimpleJdbcCall manually from that.

First, create a CallMetaDataContext for the function:

    CallMetaDataContext context = new CallMetaDataContext();
    context.setFunction(true);
    context.setSchemaName(schemaName);
    context.setProcedureName(functionName);
    context.initializeMetaData(jdbcTemplate.getDataSource());
    context.processParameters(Collections.emptyList());

Next, create the SimpleJdbcCall, but force it to not do its own metadata lookup:

SimpleJdbcCall simpleJdbcCall = new SimpleJdbcCall(jdbcTemplate);
// This forces the call object to skip metadata lookup, which is the part that forces all parameters
simpleJdbcCall.setAccessCallParameterMetaData(false);

// Now go back to our previously created context and pull the parameters we need from it
simpleJdbcCall.addDeclaredParameter(context.getCallParameters().get(0));
for (int i = 0; i < params.length; ++i) {
    simpleJdbcCall.addDeclaredParameter(context.getCallParameters().get(i));
}
// Call the function and retrieve the result
Map<String, Object> resultsMap = simpleJdbcCall
                        .withSchemaName(schemaName)
                        .withFunctionName(functionName)
                        .execute(params);
Object returnValue = resultsMap.get(context.getScalarOutParameterName());

I found solution for my case with SimpleJdbcCall and Spring 5.2.1, Java 8, Oracle 12.

You need to:

  1. Use .withoutProcedureColumnMetaDataAccess()
  2. Use .withNamedBinding()
  3. Declare parameters, you know about in .declareParameters() call. Procedure will be called only with parameters, declared in this method. Default parameters, you dont want to set, arent writing here.

Example call is below

final String dataParamName = "P_DATA";
final String ageParamName = "P_AGE";
final String genderParamName = "P_GENDER";

final String acceptedParamName = "P_ACCEPTED";


SimpleJdbcCall simpleJdbcCall = new SimpleJdbcCall(getJdbcTemplate())
        .withCatalogName("PKG_USER")
        .withProcedureName("USER_CHECK")
        .withoutProcedureColumnMetaDataAccess()
        .withNamedBinding()
        .declareParameters(
                new SqlParameter(dataParamName, OracleTypes.VARCHAR),
                new SqlParameter(ageParamName, OracleTypes.NUMBER),
                new SqlParameter(genderParamName, OracleTypes.VARCHAR),
                new SqlOutParameter(acceptedParamName, OracleTypes.NUMBER)
        );

SqlParameterSource parameterSource = new MapSqlParameterSource()
        .addValue(dataParamName, data)
        .addValue(ageParamName, age)
        .addValue(genderParamName, gender);

Map<String, Object> out = simpleJdbcCall.execute(parameterSource);