Call an Oracle Function from Spring with CallableStatement
1 min read

Call an Oracle Function from Spring with CallableStatement

Call an Oracle Function from Spring with CallableStatement

Following the previous post, I wondered if there's a more elegant way to call a function. It turns out there is and it's based on CallableStatement:

int value = jdbcOperations.execute(
  new CallableStatementCreator() {
    // ...
  },
  new CallableStatementCallback<Integer>() {
    // ...
  }
);

The CallableStatementCreator sets up the statement and the CallableStatementCallback executes the statement and returns the value up to the execute() call.

Having a function:

CREATE OR REPLACE FUNCTION HAS_PANEL_FLAG_OK(
  P_USER_ID    in USERS.USER_ID%TYPE,
  P_PANEL_ID   in PANELS.PANEL_ID%TYPE,
)
RETURN integer
IS
  lv_count NUMBER;
BEGIN
  SELECT count(*) into lv_count
    FROM USERS
    WHERE USERS.PANEL_ID = P_PANEL_ID and USERS.USER_ID = P_USER_ID;
  return lv_count;
EXCEPTION
  when OTHERS then
    return 0;
END;

We can build a query string like:

static final String PANEL_OPERATIONS_OK =
  "{ ? = call HAS_PANEL_FLAG_OK(?, ?) }";

The CallableStatementCreator instance is then:

CallableStatementCreator statementCreator =
    new CallableStatementCreator() {
  @Override
  public CallableStatement createCallableStatement(Connection con) throws SQLException {
    CallableStatement cs = con.prepareCall(PANEL_OPERATIONS_OK);
    cs.registerOutParameter(1, Types.INTEGER); // The returned result
    cs.setString(2, userId);
    cs.setString(3, panelId);
    return cs;
  }
}

This states the first parameter is a result, the second and 3rd parameter are function parameters.

NOTE: Be careful because you can specify indices or keywords for parameter positioning, but you're not allowed to mix the two!

The CallableStatementCallback is:

CallableStatementCallback<Integer> statementCallback =
    new CallableStatementCallback<Integer>() {
  @Override
  public Integer doInCallableStatement(CallableStatement cs) throws SQLException, DataAccessException {
    cs.execute();
    int result = cs.getInt(1);
    return result; // Propagated to the jdbcTemplate.execute method
  }
}

This will execute the statement and will return the result (first parameter of the statement).

The jdbcOperations.execute() function will return the result from CallableStatementCallback's doInCallableStatement().

The full java code looks like this:

int value = jdbcOperations.execute(
  statementCreator,
  statementCallback
);

This is much more powerful. You can customise all function's parameters the same as with a classic SQL query.

HTH,