Question

I have a problem with calling a stored procedure on Firebird. My procedure has one parameter (UID) and returns one value ID.

...
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
// sp declaration
SimpleJdbcCall jdbcCall = new SimpleJdbcCall(jdbcTemplate)
      .withProcedureName("SP_NEWRECORD").declareParameters(
       new SqlParameter("UID", Types.INTEGER),
       new SqlOutParameter("NEWID", Types.INTEGER));
Map<String, Object> in = new HashMap<String, Object>();
in.put("UID", uid);
in.put("NEWID", 0);

try {
Map<String, Object> out = jdbcCall.execute(in); //here throws Exception
    last_inserted = Integer.parseInt(String.valueOf(out.get("NEWID")));
} catch (Exception ex) {
    ex.printStackTrace();
} finally {
    System.out.println("createRecord result id=" + last_inserted);
}
// return id of inserted record
return last_inserted;

Catalina out

java.lang.NullPointerException
at org.firebirdsql.jdbc.AbstractResultSet.getRow(AbstractResultSet.java:1307)
at org.firebirdsql.jdbc.AbstractCallableStatement.assertHasData(AbstractCallableStatement.java:998)
at org.firebirdsql.jdbc.AbstractCallableStatement.getObject(AbstractCallableStatement.java:773)
at com.mchange.v2.c3p0.impl.NewProxyCallableStatement.getObject(NewProxyCallableStatement.java:675)
at org.springframework.jdbc.core.JdbcTemplate.extractOutputParameters(JdbcTemplate.java:1168)

code of SP is very simple, creates new record in table and returns ID of inserted record

ALTER PROCEDURE SP_NEWRECORD (UID integer)
RETURNS (NEWID integer)
AS
declare variable ID integer;
begin
  ID=GEN_ID(gen_myrecords_id,1);
  insert into myrecords (id,uid)
  values (:id,:uid);
  newid=ID;
end
Was it helpful?

Solution

The ultimate problem is that currently Jaybird makes no clear distinction between OUT parameters (which Firebird technically doesn't have), the (single row) return values of an executable procedure, and the (multiple row) result set of a selectable stored procedure. The problem you are experiencing is (at least partially) related to JDBC-229 (and a general review of the CallableStatement implementation that is planned for Jaybird 3.0).

This leads to a situation where the result set backing the single row is read twice, once as a ResultSet and once through the getters on CallableStatement, however as the backing result set has been closed, this causes a NullPointerException when the OUT parameter is retrieved.

There are two potential workarounds:

  1. Disable results processing on the JdbcTemplate:

    JdbcTemplate jdbcTemplate = new JdbcTemplate(ds);
    jdbcTemplate.setSkipResultsProcessing(true);
    
  2. Do not declare the OUT parameter, and process the result set that is returned in the out map (which contains a List with a Map for the result set):

    last_inserted = (int) ((List<Map>) out.get("#result-set-1")).get(0).get("NEWID");
    

Note that the cast from Object to int assumes Java 7 or higher.

I have reported this specific problem as JDBC-350 and I will try to fix this in the next Jaybird release (2.2.6).

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top