class StoredProcedureItemReader<T> extends AbstractCursorItemReader<T> {

	private CallableStatement callableStatement;

	private PreparedStatementSetter preparedStatementSetter;

	private String procedureName;

	private String callString;

	private RowMapper<T> rowMapper;

	private SqlParameter[] parameters = new SqlParameter[0];

	private boolean function = false;

	private int refCursorPosition = 0;

	public StoredProcedureItemReader() {
		super();
		setName(ClassUtils.getShortName(StoredProcedureItemReader.class));
	}

	/**
	 * Set the RowMapper to be used for all calls to read().
	 * @param rowMapper the RowMapper to use to map the results
	 */
	public void setRowMapper(RowMapper<T> rowMapper) {
		this.rowMapper = rowMapper;
	}

	/**
	 * Set the SQL statement to be used when creating the cursor. This statement should be
	 * a complete and valid SQL statement, as it will be run directly without any
	 * modification.
	 * @param sprocedureName the SQL used to call the statement
	 */
	public void setProcedureName(String sprocedureName) {
		this.procedureName = sprocedureName;
	}

	/**
	 * Set the PreparedStatementSetter to use if any parameter values that need to be set
	 * in the supplied query.
	 * @param preparedStatementSetter used to populate the SQL
	 */
	public void setPreparedStatementSetter(PreparedStatementSetter preparedStatementSetter) {
		this.preparedStatementSetter = preparedStatementSetter;
	}

	/**
	 * Add one or more declared parameters. Used for configuring this operation when used
	 * in a bean factory. Each parameter will specify SQL type and (optionally) the
	 * parameter's name.
	 * @param parameters Array containing the declared <code>SqlParameter</code> objects
	 */
	public void setParameters(SqlParameter[] parameters) {
		this.parameters = parameters;
	}

	/**
	 * Set whether this stored procedure is a function.
	 * @param function indicator
	 */
	public void setFunction(boolean function) {
		this.function = function;
	}

	/**
	 * Set the parameter position of the REF CURSOR. Only used for Oracle and PostgreSQL
	 * that use REF CURSORs. For any other database this should be kept as 0 which is the
	 * default.
	 * @param refCursorPosition The parameter position of the REF CURSOR
	 */
	public void setRefCursorPosition(int refCursorPosition) {
		this.refCursorPosition = refCursorPosition;
	}

	/**
	 * Assert that mandatory properties are set.
	 * @throws IllegalArgumentException if either data source or SQL properties not set.
	 */
	@Override
	public void afterPropertiesSet() throws Exception {
		super.afterPropertiesSet();
		Assert.state(procedureName != null, "The name of the stored procedure must be provided");
		Assert.state(rowMapper != null, "RowMapper must be provided");
	}

	@Override
	protected void openCursor(Connection con) {<FILL_FUNCTION_BODY>}

	@Nullable
	@Override
	protected T readCursor(ResultSet rs, int currentRow) throws SQLException {
		return rowMapper.mapRow(rs, currentRow);
	}

	/**
	 * Close the cursor and database connection.
	 * @param connection to the database
	 */
	@Override
	protected void cleanupOnClose(Connection connection) {
		JdbcUtils.closeStatement(this.callableStatement);
		JdbcUtils.closeConnection(connection);
	}

	@Override
	public String getSql() {
		if (callString != null) {
			return this.callString;
		}
		else {
			return "PROCEDURE NAME: " + procedureName;
		}
	}

}


		Assert.state(procedureName != null, "Procedure Name must not be null.");
		Assert.state(refCursorPosition >= 0, "invalid refCursorPosition specified as " + refCursorPosition
				+ "; it can't be " + "specified as a negative number.");
		Assert.state(refCursorPosition == 0 || refCursorPosition > 0, "invalid refCursorPosition specified as "
				+ refCursorPosition + "; there are " + parameters.length + " parameters defined.");

		CallMetaDataContext callContext = new CallMetaDataContext();
		callContext.setAccessCallParameterMetaData(false);
		callContext.setProcedureName(procedureName);
		callContext.setFunction(function);
		callContext.initializeMetaData(getDataSource());
		callContext.processParameters(Arrays.asList(parameters));
		SqlParameter cursorParameter = callContext.createReturnResultSetParameter("cursor", rowMapper);
		this.callString = callContext.createCallString();

		if (log.isDebugEnabled()) {
			log.debug("Call string is: " + callString);
		}

		int cursorSqlType = Types.OTHER;
		if (function) {
			if (cursorParameter instanceof SqlOutParameter) {
				cursorSqlType = cursorParameter.getSqlType();
			}
		}
		else {
			if (refCursorPosition > 0 && refCursorPosition <= parameters.length) {
				cursorSqlType = parameters[refCursorPosition - 1].getSqlType();
			}
		}

		try {
			if (isUseSharedExtendedConnection()) {
				callableStatement = con.prepareCall(callString, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY,
						ResultSet.HOLD_CURSORS_OVER_COMMIT);
			}
			else {
				callableStatement = con.prepareCall(callString, ResultSet.TYPE_FORWARD_ONLY,
						ResultSet.CONCUR_READ_ONLY);
			}
			applyStatementSettings(callableStatement);
			if (this.preparedStatementSetter != null) {
				preparedStatementSetter.setValues(callableStatement);
			}

			if (function) {
				callableStatement.registerOutParameter(1, cursorSqlType);
			}
			else {
				if (refCursorPosition > 0) {
					callableStatement.registerOutParameter(refCursorPosition, cursorSqlType);
				}
			}
			boolean results = callableStatement.execute();
			if (results) {
				rs = callableStatement.getResultSet();
			}
			else {
				if (function) {
					rs = (ResultSet) callableStatement.getObject(1);
				}
				else {
					rs = (ResultSet) callableStatement.getObject(refCursorPosition);
				}
			}
			handleWarnings(callableStatement);
		}
		catch (SQLException se) {
			close();
			throw translateSqlException("Executing stored procedure", getSql(), se);
		}

	
<methods>public void <init>() ,public void afterPropertiesSet() throws java.lang.Exception,public javax.sql.DataSource getDataSource() ,public abstract java.lang.String getSql() ,public boolean isUseSharedExtendedConnection() ,public void setConnectionAutoCommit(boolean) ,public void setDataSource(javax.sql.DataSource) ,public void setDriverSupportsAbsolute(boolean) ,public void setFetchSize(int) ,public void setIgnoreWarnings(boolean) ,public void setMaxRows(int) ,public void setQueryTimeout(int) ,public void setUseSharedExtendedConnection(boolean) ,public void setVerifyCursorPosition(boolean) <variables>public static final int VALUE_NOT_SET,private java.sql.Connection con,private java.lang.Boolean connectionAutoCommit,private javax.sql.DataSource dataSource,private boolean driverSupportsAbsolute,private org.springframework.jdbc.support.SQLExceptionTranslator exceptionTranslator,private int fetchSize,private boolean ignoreWarnings,private boolean initialConnectionAutoCommit,private boolean initialized,protected final org.apache.commons.logging.Log log,private int maxRows,private int queryTimeout,protected java.sql.ResultSet rs,private boolean useSharedExtendedConnection,private boolean verifyCursorPosition