Reputation: 1
I'm attempting to read a BLOB out of a stored procedure's output parameter in Oracle11gR2 using Spring's SimpleJdbcCall. However, when I call to get the length of the BLOB in order to get the byte array, I get the following error:
java.sql.SQLRecoverableException: Closed Connection
at oracle.sql.BLOB.getDBAccess(BLOB.java:1122) ~[ojdbc6-11.2.0.4.0.jar:11.2.0.4.0]
at oracle.sql.BLOB.length(BLOB.java:156) ~[ojdbc6-11.2.0.4.0.jar:11.2.0.4.0]
This is my stored procedure (names changed to protect the innocent):
FUNCTION my_stored_proc(in_param_1 IN VARCHAR2, in_param_2 IN VARCHAR2, out_param_1 OUT VARCHAR2, out_param_2 OUT VARCHAR2, out_param_3 OUT BLOB)
RETURN REF CURSOR
IS
...
And this is my Java code (or at least a reasonable facsimile):
SqlParameter[] parameters = {
getResultParameter(getRowMapper()),
new SqlParameter("in_param_1", Types.VARCHAR),
new SqlParameter("in_param_2", Types.VARCHAR),
new SqlOutParameter("out_param_1", Types.VARCHAR),
new SqlOutParameter("out_param_2", Types.VARCHAR),
new SqlOutParameter("out_param_3", Types.BLOB)
};
simpleJdbcCall = new SimpleJdbcCall(getDataSource());
simpleJdbcCall.setFunction(getResultIsCursor());
simpleJdbcCall.setProcedureName("my_stored_proc");
simpleJdbcCall.withoutProcedureColumnMetaDataAccess().declareParameters(parameters);
MapSqlParameterSource params = new MapSqlParameterSource();
params.addValue("in_param_1", requestId);
params.addValue("in_param_2", compositionId);
Map<String, Object> result = simpleJdbcCall.execute(params);
_errorMessage = (String)result.get("out_param_1");
_account = (String)result.get("out_param_2");
Blob blob = (Blob)result.get("out_param_3");
if (null != blob) {
final byte[] data = blob.getBytes(1, (int) blob.length());
...
}
(I understand that having a stored proc that has output paremeters and that returns a ref cursor may not be the best designed stored proc, but refactoring that is the beginning of unraveling a very large sweater which is well beyond my scope right now. Also, getting the blob data as a byte array in memory is okay here because the file being stored has a pretty small upper bound on its size.)
If possible, I'd like to stick to using the SimpleJdbcCall as it is the cornerstone of a framework I'm using. It seems like a LobHandler would solve this problem, but I haven't been able to find any reference on how to use that with SimpleJdbcCall and an out parameter (just ResultSets).
Upvotes: 0
Views: 3126
Reputation: 11
I solved this situation with:
SimpleJdbcCall fObtenerDocumentoBinario = new SimpleJdbcCall(jdbcTemplate).withSchemaName(Constantes.SCHEMA) .withCatalogName(Constantes.CATALOG) .withFunctionName(Constantes.F_RECUPERA_BLOB_DOCUMENTO);
fObtenerDocumentoBinario.addDeclaredParameter(new SqlOutParameter("P_DOCUMENTO", OracleTypes.BLOB, "BLOB", new DocumentoBinarioSqlReturnType()));
Where DocumentoBinarioSqlReturnType is defined as:
private class DocumentoBinarioSqlReturnType implements SqlReturnType {
/**
* Método sobrescrito getTypeValue.<br>
* @param cs
* @param paramIndex
* @param sqlType
* @param typeName
* @return
* @throws SQLException
* @see org.springframework.jdbc.core.SqlReturnType#getTypeValue(java.sql.CallableStatement, int, int,
* java.lang.String)
*/
public Object getTypeValue(CallableStatement cs, int paramIndex, int sqlType, String typeName)
throws SQLException {
String pathname = Constantes.getTempDir() + System.currentTimeMillis() + Constantes.EXTENSION_PDF;
InputStream inStream = null;
try {
inStream = cs.getBlob(3).getBinaryStream();
UtilidadesIO.inputStreamToFile(inStream, pathname);
} catch (SQLException e) {
LOGGER.error("Error obteniendo el documento, comprobar en BBDD", e);
throw e;
} finally {
if (null != inStream) {
try {
inStream.close();
} catch (IOException e) {
LOGGER.error(e.getMessage(), e);
inStream = null;
}
}
}
return pathname;
}
Upvotes: 1