Save This Page
Home » spring-framework-2.5.6-with-dependencies » org.springframework » jdbc » core » [javadoc | source]
    1   /*
    2    * Copyright 2002-2008 the original author or authors.
    3    *
    4    * Licensed under the Apache License, Version 2.0 (the "License");
    5    * you may not use this file except in compliance with the License.
    6    * You may obtain a copy of the License at
    7    *
    8    *      http://www.apache.org/licenses/LICENSE-2.0
    9    *
   10    * Unless required by applicable law or agreed to in writing, software
   11    * distributed under the License is distributed on an "AS IS" BASIS,
   12    * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   13    * See the License for the specific language governing permissions and
   14    * limitations under the License.
   15    */
   16   
   17   package org.springframework.jdbc.core;
   18   
   19   import java.lang.reflect.InvocationHandler;
   20   import java.lang.reflect.InvocationTargetException;
   21   import java.lang.reflect.Method;
   22   import java.lang.reflect.Proxy;
   23   import java.sql.CallableStatement;
   24   import java.sql.Connection;
   25   import java.sql.PreparedStatement;
   26   import java.sql.ResultSet;
   27   import java.sql.SQLException;
   28   import java.sql.SQLWarning;
   29   import java.sql.Statement;
   30   import java.util.ArrayList;
   31   import java.util.Collections;
   32   import java.util.HashMap;
   33   import java.util.LinkedHashMap;
   34   import java.util.List;
   35   import java.util.Map;
   36   
   37   import javax.sql.DataSource;
   38   
   39   import org.springframework.core.CollectionFactory;
   40   import org.springframework.dao.DataAccessException;
   41   import org.springframework.dao.InvalidDataAccessApiUsageException;
   42   import org.springframework.dao.support.DataAccessUtils;
   43   import org.springframework.jdbc.SQLWarningException;
   44   import org.springframework.jdbc.datasource.ConnectionProxy;
   45   import org.springframework.jdbc.datasource.DataSourceUtils;
   46   import org.springframework.jdbc.support.JdbcAccessor;
   47   import org.springframework.jdbc.support.JdbcUtils;
   48   import org.springframework.jdbc.support.KeyHolder;
   49   import org.springframework.jdbc.support.nativejdbc.NativeJdbcExtractor;
   50   import org.springframework.jdbc.support.rowset.SqlRowSet;
   51   import org.springframework.util.Assert;
   52   
   53   /**
   54    * <b>This is the central class in the JDBC core package.</b>
   55    * It simplifies the use of JDBC and helps to avoid common errors.
   56    * It executes core JDBC workflow, leaving application code to provide SQL
   57    * and extract results. This class executes SQL queries or updates, initiating
   58    * iteration over ResultSets and catching JDBC exceptions and translating
   59    * them to the generic, more informative exception hierarchy defined in the
   60    * <code>org.springframework.dao</code> package.
   61    *
   62    * <p>Code using this class need only implement callback interfaces, giving
   63    * them a clearly defined contract. The {@link PreparedStatementCreator} callback
   64    * interface creates a prepared statement given a Connection, providing SQL and
   65    * any necessary parameters. The {@link ResultSetExtractor} interface extracts
   66    * values from a ResultSet. See also {@link PreparedStatementSetter} and
   67    * {@link RowMapper} for two popular alternative callback interfaces.
   68    *
   69    * <p>Can be used within a service implementation via direct instantiation
   70    * with a DataSource reference, or get prepared in an application context
   71    * and given to services as bean reference. Note: The DataSource should
   72    * always be configured as a bean in the application context, in the first case
   73    * given to the service directly, in the second case to the prepared template.
   74    *
   75    * <p>Because this class is parameterizable by the callback interfaces and
   76    * the {@link org.springframework.jdbc.support.SQLExceptionTranslator}
   77    * interface, there should be no need to subclass it.
   78    *
   79    * <p>All SQL operations performed by this class are logged at debug level,
   80    * using "org.springframework.jdbc.core.JdbcTemplate" as log category.
   81    *
   82    * @author Rod Johnson
   83    * @author Juergen Hoeller
   84    * @author Thomas Risberg
   85    * @since May 3, 2001
   86    * @see PreparedStatementCreator
   87    * @see PreparedStatementSetter
   88    * @see CallableStatementCreator
   89    * @see PreparedStatementCallback
   90    * @see CallableStatementCallback
   91    * @see ResultSetExtractor
   92    * @see RowCallbackHandler
   93    * @see RowMapper
   94    * @see org.springframework.jdbc.support.SQLExceptionTranslator
   95    */
   96   public class JdbcTemplate extends JdbcAccessor implements JdbcOperations {
   97   
   98   	private static final String RETURN_RESULT_SET_PREFIX = "#result-set-";
   99   
  100   	private static final String RETURN_UPDATE_COUNT_PREFIX = "#update-count-";
  101   
  102   
  103   	/** Custom NativeJdbcExtractor */
  104   	private NativeJdbcExtractor nativeJdbcExtractor;
  105   
  106   	/** If this variable is false, we will throw exceptions on SQL warnings */
  107   	private boolean ignoreWarnings = true;
  108   
  109   	/**
  110   	 * If this variable is set to a non-zero value, it will be used for setting the
  111   	 * fetchSize property on statements used for query processing.
  112   	 */
  113   	private int fetchSize = 0;
  114   
  115   	/**
  116   	 * If this variable is set to a non-zero value, it will be used for setting the
  117   	 * maxRows property on statements used for query processing.
  118   	 */
  119   	private int maxRows = 0;
  120   
  121   	/**
  122   	 * If this variable is set to a non-zero value, it will be used for setting the
  123   	 * queryTimeout property on statements used for query processing.
  124   	 */
  125   	private int queryTimeout = 0;
  126   
  127   	/**
  128   	 * If this variable is set to true then all results checking will be bypassed for any
  129   	 * callable statement processing.  This can be used to avoid a bug in some older Oracle
  130   	 * JDBC drivers like 10.1.0.2.
  131   	 */
  132   	private boolean skipResultsProcessing = false;
  133   
  134   	/**
  135   	 * If this variable is set to true then all results from a stored procedure call
  136   	 * that don't have a corresponding SqlOutParameter declaration will be bypassed.
  137   	 * All other results processng will be take place unless the variable 
  138   	 * <code>skipResultsProcessing</code> is set to <code>true</code> 
  139   	 */
  140   	private boolean skipUndeclaredResults = false;
  141   
  142   	/**
  143   	 * If this variable is set to true then execution of a CallableStatement will return
  144   	 * the results in a Map that uses case insensitive names for the parameters if
  145   	 * Commons Collections is available on the classpath.
  146   	 */
  147   	private boolean resultsMapCaseInsensitive = false;
  148   
  149   
  150   	/**
  151   	 * Construct a new JdbcTemplate for bean usage.
  152   	 * <p>Note: The DataSource has to be set before using the instance.
  153   	 * @see #setDataSource
  154   	 */
  155   	public JdbcTemplate() {
  156   	}
  157   
  158   	/**
  159   	 * Construct a new JdbcTemplate, given a DataSource to obtain connections from.
  160   	 * <p>Note: This will not trigger initialization of the exception translator.
  161   	 * @param dataSource the JDBC DataSource to obtain connections from
  162   	 */
  163   	public JdbcTemplate(DataSource dataSource) {
  164   		setDataSource(dataSource);
  165   		afterPropertiesSet();
  166   	}
  167   
  168   	/**
  169   	 * Construct a new JdbcTemplate, given a DataSource to obtain connections from.
  170   	 * <p>Note: Depending on the "lazyInit" flag, initialization of the exception translator
  171   	 * will be triggered.
  172   	 * @param dataSource the JDBC DataSource to obtain connections from
  173   	 * @param lazyInit whether to lazily initialize the SQLExceptionTranslator
  174   	 */
  175   	public JdbcTemplate(DataSource dataSource, boolean lazyInit) {
  176   		setDataSource(dataSource);
  177   		setLazyInit(lazyInit);
  178   		afterPropertiesSet();
  179   	}
  180   
  181   
  182   	/**
  183   	 * Set a NativeJdbcExtractor to extract native JDBC objects from wrapped handles.
  184   	 * Useful if native Statement and/or ResultSet handles are expected for casting
  185   	 * to database-specific implementation classes, but a connection pool that wraps
  186   	 * JDBC objects is used (note: <i>any</i> pool will return wrapped Connections).
  187   	 */
  188   	public void setNativeJdbcExtractor(NativeJdbcExtractor extractor) {
  189   		this.nativeJdbcExtractor = extractor;
  190   	}
  191   
  192   	/**
  193   	 * Return the current NativeJdbcExtractor implementation.
  194   	 */
  195   	public NativeJdbcExtractor getNativeJdbcExtractor() {
  196   		return this.nativeJdbcExtractor;
  197   	}
  198   
  199   	/**
  200   	 * Set whether or not we want to ignore SQLWarnings.
  201   	 * <p>Default is "true", swallowing and logging all warnings. Switch this flag
  202   	 * to "false" to make the JdbcTemplate throw a SQLWarningException instead.
  203   	 * @see java.sql.SQLWarning
  204   	 * @see org.springframework.jdbc.SQLWarningException
  205   	 * @see #handleWarnings
  206   	 */
  207   	public void setIgnoreWarnings(boolean ignoreWarnings) {
  208   		this.ignoreWarnings = ignoreWarnings;
  209   	}
  210   
  211   	/**
  212   	 * Return whether or not we ignore SQLWarnings.
  213   	 */
  214   	public boolean isIgnoreWarnings() {
  215   		return this.ignoreWarnings;
  216   	}
  217   
  218   	/**
  219   	 * Set the fetch size for this JdbcTemplate. This is important for processing
  220   	 * large result sets: Setting this higher than the default value will increase
  221   	 * processing speed at the cost of memory consumption; setting this lower can
  222   	 * avoid transferring row data that will never be read by the application.
  223   	 * <p>Default is 0, indicating to use the JDBC driver's default.
  224   	 * @see java.sql.Statement#setFetchSize
  225   	 */
  226   	public void setFetchSize(int fetchSize) {
  227   		this.fetchSize = fetchSize;
  228   	}
  229   
  230   	/**
  231   	 * Return the fetch size specified for this JdbcTemplate.
  232   	 */
  233   	public int getFetchSize() {
  234   		return this.fetchSize;
  235   	}
  236   
  237   	/**
  238   	 * Set the maximum number of rows for this JdbcTemplate. This is important
  239   	 * for processing subsets of large result sets, avoiding to read and hold
  240   	 * the entire result set in the database or in the JDBC driver if we're
  241   	 * never interested in the entire result in the first place (for example,
  242   	 * when performing searches that might return a large number of matches).
  243   	 * <p>Default is 0, indicating to use the JDBC driver's default.
  244   	 * @see java.sql.Statement#setMaxRows
  245   	 */
  246   	public void setMaxRows(int maxRows) {
  247   		this.maxRows = maxRows;
  248   	}
  249   
  250   	/**
  251   	 * Return the maximum number of rows specified for this JdbcTemplate.
  252   	 */
  253   	public int getMaxRows() {
  254   		return this.maxRows;
  255   	}
  256   
  257   	/**
  258   	 * Set the query timeout for statements that this JdbcTemplate executes.
  259   	 * <p>Default is 0, indicating to use the JDBC driver's default.
  260   	 * <p>Note: Any timeout specified here will be overridden by the remaining
  261   	 * transaction timeout when executing within a transaction that has a
  262   	 * timeout specified at the transaction level.
  263   	 * @see java.sql.Statement#setQueryTimeout
  264   	 */
  265   	public void setQueryTimeout(int queryTimeout) {
  266   		this.queryTimeout = queryTimeout;
  267   	}
  268   
  269   	/**
  270   	 * Return the query timeout for statements that this JdbcTemplate executes.
  271   	 */
  272   	public int getQueryTimeout() {
  273   		return this.queryTimeout;
  274   	}
  275   
  276   	/**
  277   	 * Set whether results processing should be skipped.  Can be used to optimize callable
  278   	 * statement processing when we know that no results are being passed back - the processing
  279   	 * of out parameter will still take place.  This can be used to avoid a bug in some older
  280   	 * Oracle JDBC drivers like 10.1.0.2.
  281   	 */
  282   	public void setSkipResultsProcessing(boolean skipResultsProcessing) {
  283   		this.skipResultsProcessing = skipResultsProcessing;
  284   	}
  285   
  286   	/**
  287   	 * Return whether results processing should be skipped.
  288   	 */
  289   	public boolean isSkipResultsProcessing() {
  290   		return this.skipResultsProcessing;
  291   	}
  292   
  293   	/**
  294   	 * Set whether undelared results should be skipped.
  295   	 */
  296   	public void setSkipUndeclaredResults(boolean skipUndeclaredResults) {
  297   		this.skipUndeclaredResults = skipUndeclaredResults;
  298   	}
  299   
  300   	/**
  301   	 * Return whether undeclared results should be skipped.
  302   	 */
  303   	public boolean isSkipUndeclaredResults() {
  304   		return this.skipUndeclaredResults;
  305   	}
  306   
  307   	/**
  308   	 * Set whether execution of a CallableStatement will return the results in a Map
  309   	 * that uses case insensitive names for the parameters.
  310   	 */
  311   	public void setResultsMapCaseInsensitive(boolean resultsMapCaseInsensitive) {
  312   		this.resultsMapCaseInsensitive = resultsMapCaseInsensitive;
  313   	}
  314   
  315   	/**
  316   	 * Return whether execution of a CallableStatement will return the results in a Map
  317   	 * that uses case insensitive names for the parameters.
  318   	 */
  319   	public boolean isResultsMapCaseInsensitive() {
  320   		return this.resultsMapCaseInsensitive;
  321   	}
  322   
  323   
  324   	//-------------------------------------------------------------------------
  325   	// Methods dealing with a plain java.sql.Connection
  326   	//-------------------------------------------------------------------------
  327   
  328   	public Object execute(ConnectionCallback action) throws DataAccessException {
  329   		Assert.notNull(action, "Callback object must not be null");
  330   
  331   		Connection con = DataSourceUtils.getConnection(getDataSource());
  332   		try {
  333   			Connection conToUse = con;
  334   			if (this.nativeJdbcExtractor != null) {
  335   				// Extract native JDBC Connection, castable to OracleConnection or the like.
  336   				conToUse = this.nativeJdbcExtractor.getNativeConnection(con);
  337   			}
  338   			else {
  339   				// Create close-suppressing Connection proxy, also preparing returned Statements.
  340   				conToUse = createConnectionProxy(con);
  341   			}
  342   			return action.doInConnection(conToUse);
  343   		}
  344   		catch (SQLException ex) {
  345   			// Release Connection early, to avoid potential connection pool deadlock
  346   			// in the case when the exception translator hasn't been initialized yet.
  347   			DataSourceUtils.releaseConnection(con, getDataSource());
  348   			con = null;
  349   			throw getExceptionTranslator().translate("ConnectionCallback", getSql(action), ex);
  350   		}
  351   		finally {
  352   			DataSourceUtils.releaseConnection(con, getDataSource());
  353   		}
  354   	}
  355   
  356   	/**
  357   	 * Create a close-suppressing proxy for the given JDBC Connection.
  358   	 * Called by the <code>execute</code> method.
  359   	 * <p>The proxy also prepares returned JDBC Statements, applying
  360   	 * statement settings such as fetch size, max rows, and query timeout.
  361   	 * @param con the JDBC Connection to create a proxy for
  362   	 * @return the Connection proxy
  363   	 * @see java.sql.Connection#close()
  364   	 * @see #execute(ConnectionCallback)
  365   	 * @see #applyStatementSettings
  366   	 */
  367   	protected Connection createConnectionProxy(Connection con) {
  368   		return (Connection) Proxy.newProxyInstance(
  369   				ConnectionProxy.class.getClassLoader(),
  370   				new Class[] {ConnectionProxy.class},
  371   				new CloseSuppressingInvocationHandler(con));
  372   	}
  373   
  374   
  375   	//-------------------------------------------------------------------------
  376   	// Methods dealing with static SQL (java.sql.Statement)
  377   	//-------------------------------------------------------------------------
  378   
  379   	public Object execute(StatementCallback action) throws DataAccessException {
  380   		Assert.notNull(action, "Callback object must not be null");
  381   
  382   		Connection con = DataSourceUtils.getConnection(getDataSource());
  383   		Statement stmt = null;
  384   		try {
  385   			Connection conToUse = con;
  386   			if (this.nativeJdbcExtractor != null &&
  387   					this.nativeJdbcExtractor.isNativeConnectionNecessaryForNativeStatements()) {
  388   				conToUse = this.nativeJdbcExtractor.getNativeConnection(con);
  389   			}
  390   			stmt = conToUse.createStatement();
  391   			applyStatementSettings(stmt);
  392   			Statement stmtToUse = stmt;
  393   			if (this.nativeJdbcExtractor != null) {
  394   				stmtToUse = this.nativeJdbcExtractor.getNativeStatement(stmt);
  395   			}
  396   			Object result = action.doInStatement(stmtToUse);
  397   			handleWarnings(stmt);
  398   			return result;
  399   		}
  400   		catch (SQLException ex) {
  401   			// Release Connection early, to avoid potential connection pool deadlock
  402   			// in the case when the exception translator hasn't been initialized yet.
  403   			JdbcUtils.closeStatement(stmt);
  404   			stmt = null;
  405   			DataSourceUtils.releaseConnection(con, getDataSource());
  406   			con = null;
  407   			throw getExceptionTranslator().translate("StatementCallback", getSql(action), ex);
  408   		}
  409   		finally {
  410   			JdbcUtils.closeStatement(stmt);
  411   			DataSourceUtils.releaseConnection(con, getDataSource());
  412   		}
  413   	}
  414   
  415   	public void execute(final String sql) throws DataAccessException {
  416   		if (logger.isDebugEnabled()) {
  417   			logger.debug("Executing SQL statement [" + sql + "]");
  418   		}
  419   
  420   		class ExecuteStatementCallback implements StatementCallback, SqlProvider {
  421   			public Object doInStatement(Statement stmt) throws SQLException {
  422   				stmt.execute(sql);
  423   				return null;
  424   			}
  425   			public String getSql() {
  426   				return sql;
  427   			}
  428   		}
  429   		execute(new ExecuteStatementCallback());
  430   	}
  431   
  432   	public Object query(final String sql, final ResultSetExtractor rse) throws DataAccessException {
  433   		Assert.notNull(sql, "SQL must not be null");
  434   		Assert.notNull(rse, "ResultSetExtractor must not be null");
  435   		if (logger.isDebugEnabled()) {
  436   			logger.debug("Executing SQL query [" + sql + "]");
  437   		}
  438   
  439   		class QueryStatementCallback implements StatementCallback, SqlProvider {
  440   			public Object doInStatement(Statement stmt) throws SQLException {
  441   				ResultSet rs = null;
  442   				try {
  443   					rs = stmt.executeQuery(sql);
  444   					ResultSet rsToUse = rs;
  445   					if (nativeJdbcExtractor != null) {
  446   						rsToUse = nativeJdbcExtractor.getNativeResultSet(rs);
  447   					}
  448   					return rse.extractData(rsToUse);
  449   				}
  450   				finally {
  451   					JdbcUtils.closeResultSet(rs);
  452   				}
  453   			}
  454   			public String getSql() {
  455   				return sql;
  456   			}
  457   		}
  458   		return execute(new QueryStatementCallback());
  459   	}
  460   
  461   	public void query(String sql, RowCallbackHandler rch) throws DataAccessException {
  462   		query(sql, new RowCallbackHandlerResultSetExtractor(rch));
  463   	}
  464   
  465   	public List query(String sql, RowMapper rowMapper) throws DataAccessException {
  466   		return (List) query(sql, new RowMapperResultSetExtractor(rowMapper));
  467   	}
  468   
  469   	public Map queryForMap(String sql) throws DataAccessException {
  470   		return (Map) queryForObject(sql, getColumnMapRowMapper());
  471   	}
  472   
  473   	public Object queryForObject(String sql, RowMapper rowMapper) throws DataAccessException {
  474   		List results = query(sql, rowMapper);
  475   		return DataAccessUtils.requiredSingleResult(results);
  476   	}
  477   
  478   	public Object queryForObject(String sql, Class requiredType) throws DataAccessException {
  479   		return queryForObject(sql, getSingleColumnRowMapper(requiredType));
  480   	}
  481   
  482   	public long queryForLong(String sql) throws DataAccessException {
  483   		Number number = (Number) queryForObject(sql, Long.class);
  484   		return (number != null ? number.longValue() : 0);
  485   	}
  486   
  487   	public int queryForInt(String sql) throws DataAccessException {
  488   		Number number = (Number) queryForObject(sql, Integer.class);
  489   		return (number != null ? number.intValue() : 0);
  490   	}
  491   
  492   	public List queryForList(String sql, Class elementType) throws DataAccessException {
  493   		return query(sql, getSingleColumnRowMapper(elementType));
  494   	}
  495   
  496   	public List queryForList(String sql) throws DataAccessException {
  497   		return query(sql, getColumnMapRowMapper());
  498   	}
  499   
  500   	public SqlRowSet queryForRowSet(String sql) throws DataAccessException {
  501   		return (SqlRowSet) query(sql, new SqlRowSetResultSetExtractor());
  502   	}
  503   
  504   	public int update(final String sql) throws DataAccessException {
  505   		Assert.notNull(sql, "SQL must not be null");
  506   		if (logger.isDebugEnabled()) {
  507   			logger.debug("Executing SQL update [" + sql + "]");
  508   		}
  509   
  510   		class UpdateStatementCallback implements StatementCallback, SqlProvider {
  511   			public Object doInStatement(Statement stmt) throws SQLException {
  512   				int rows = stmt.executeUpdate(sql);
  513   				if (logger.isDebugEnabled()) {
  514   					logger.debug("SQL update affected " + rows + " rows");
  515   				}
  516   				return new Integer(rows);
  517   			}
  518   			public String getSql() {
  519   				return sql;
  520   			}
  521   		}
  522   		return ((Integer) execute(new UpdateStatementCallback())).intValue();
  523   	}
  524   
  525   	public int[] batchUpdate(final String[] sql) throws DataAccessException {
  526   		Assert.notEmpty(sql, "SQL array must not be empty");
  527   		if (logger.isDebugEnabled()) {
  528   			logger.debug("Executing SQL batch update of " + sql.length + " statements");
  529   		}
  530   
  531   		class BatchUpdateStatementCallback implements StatementCallback, SqlProvider {
  532   			private String currSql;
  533   			public Object doInStatement(Statement stmt) throws SQLException, DataAccessException {
  534   				int[] rowsAffected = new int[sql.length];
  535   				if (JdbcUtils.supportsBatchUpdates(stmt.getConnection())) {
  536   					for (int i = 0; i < sql.length; i++) {
  537   						this.currSql = sql[i];
  538   						stmt.addBatch(sql[i]);
  539   					}
  540   					rowsAffected = stmt.executeBatch();
  541   				}
  542   				else {
  543   					for (int i = 0; i < sql.length; i++) {
  544   						this.currSql = sql[i];
  545   						if (!stmt.execute(sql[i])) {
  546   							rowsAffected[i] = stmt.getUpdateCount();
  547   						}
  548   						else {
  549   							throw new InvalidDataAccessApiUsageException("Invalid batch SQL statement: " + sql[i]);
  550   						}
  551   					}
  552   				}
  553   				return rowsAffected;
  554   			}
  555   			public String getSql() {
  556   				return currSql;
  557   			}
  558   		}
  559   		return (int[]) execute(new BatchUpdateStatementCallback());
  560   	}
  561   
  562   
  563   	//-------------------------------------------------------------------------
  564   	// Methods dealing with prepared statements
  565   	//-------------------------------------------------------------------------
  566   
  567   	public Object execute(PreparedStatementCreator psc, PreparedStatementCallback action)
  568   			throws DataAccessException {
  569   
  570   		Assert.notNull(psc, "PreparedStatementCreator must not be null");
  571   		Assert.notNull(action, "Callback object must not be null");
  572   		if (logger.isDebugEnabled()) {
  573   			String sql = getSql(psc);
  574   			logger.debug("Executing prepared SQL statement" + (sql != null ? " [" + sql + "]" : ""));
  575   		}
  576   
  577   		Connection con = DataSourceUtils.getConnection(getDataSource());
  578   		PreparedStatement ps = null;
  579   		try {
  580   			Connection conToUse = con;
  581   			if (this.nativeJdbcExtractor != null &&
  582   					this.nativeJdbcExtractor.isNativeConnectionNecessaryForNativePreparedStatements()) {
  583   				conToUse = this.nativeJdbcExtractor.getNativeConnection(con);
  584   			}
  585   			ps = psc.createPreparedStatement(conToUse);
  586   			applyStatementSettings(ps);
  587   			PreparedStatement psToUse = ps;
  588   			if (this.nativeJdbcExtractor != null) {
  589   				psToUse = this.nativeJdbcExtractor.getNativePreparedStatement(ps);
  590   			}
  591   			Object result = action.doInPreparedStatement(psToUse);
  592   			handleWarnings(ps);
  593   			return result;
  594   		}
  595   		catch (SQLException ex) {
  596   			// Release Connection early, to avoid potential connection pool deadlock
  597   			// in the case when the exception translator hasn't been initialized yet.
  598   			if (psc instanceof ParameterDisposer) {
  599   				((ParameterDisposer) psc).cleanupParameters();
  600   			}
  601   			String sql = getSql(psc);
  602   			psc = null;
  603   			JdbcUtils.closeStatement(ps);
  604   			ps = null;
  605   			DataSourceUtils.releaseConnection(con, getDataSource());
  606   			con = null;
  607   			throw getExceptionTranslator().translate("PreparedStatementCallback", sql, ex);
  608   		}
  609   		finally {
  610   			if (psc instanceof ParameterDisposer) {
  611   				((ParameterDisposer) psc).cleanupParameters();
  612   			}
  613   			JdbcUtils.closeStatement(ps);
  614   			DataSourceUtils.releaseConnection(con, getDataSource());
  615   		}
  616   	}
  617   
  618   	public Object execute(String sql, PreparedStatementCallback action) throws DataAccessException {
  619   		return execute(new SimplePreparedStatementCreator(sql), action);
  620   	}
  621   
  622   	/**
  623   	 * Query using a prepared statement, allowing for a PreparedStatementCreator
  624   	 * and a PreparedStatementSetter. Most other query methods use this method,
  625   	 * but application code will always work with either a creator or a setter.
  626   	 * @param psc Callback handler that can create a PreparedStatement given a
  627   	 * Connection
  628   	 * @param pss object that knows how to set values on the prepared statement.
  629   	 * If this is null, the SQL will be assumed to contain no bind parameters.
  630   	 * @param rse object that will extract results.
  631   	 * @return an arbitrary result object, as returned by the ResultSetExtractor
  632   	 * @throws DataAccessException if there is any problem
  633   	 */
  634   	public Object query(
  635   			PreparedStatementCreator psc, final PreparedStatementSetter pss, final ResultSetExtractor rse)
  636   			throws DataAccessException {
  637   
  638   		Assert.notNull(rse, "ResultSetExtractor must not be null");
  639   		logger.debug("Executing prepared SQL query");
  640   
  641   		return execute(psc, new PreparedStatementCallback() {
  642   			public Object doInPreparedStatement(PreparedStatement ps) throws SQLException {
  643   				ResultSet rs = null;
  644   				try {
  645   					if (pss != null) {
  646   						pss.setValues(ps);
  647   					}
  648   					rs = ps.executeQuery();
  649   					ResultSet rsToUse = rs;
  650   					if (nativeJdbcExtractor != null) {
  651   						rsToUse = nativeJdbcExtractor.getNativeResultSet(rs);
  652   					}
  653   					return rse.extractData(rsToUse);
  654   				}
  655   				finally {
  656   					JdbcUtils.closeResultSet(rs);
  657   					if (pss instanceof ParameterDisposer) {
  658   						((ParameterDisposer) pss).cleanupParameters();
  659   					}
  660   				}
  661   			}
  662   		});
  663   	}
  664   
  665   	public Object query(PreparedStatementCreator psc, ResultSetExtractor rse) throws DataAccessException {
  666   		return query(psc, null, rse);
  667   	}
  668   
  669   	public Object query(String sql, PreparedStatementSetter pss, ResultSetExtractor rse) throws DataAccessException {
  670   		return query(new SimplePreparedStatementCreator(sql), pss, rse);
  671   	}
  672   
  673   	public Object query(String sql, Object[] args, int[] argTypes, ResultSetExtractor rse) throws DataAccessException {
  674   		return query(sql, new ArgTypePreparedStatementSetter(args, argTypes), rse);
  675   	}
  676   
  677   	public Object query(String sql, Object[] args, ResultSetExtractor rse) throws DataAccessException {
  678   		return query(sql, new ArgPreparedStatementSetter(args), rse);
  679   	}
  680   
  681   	public void query(PreparedStatementCreator psc, RowCallbackHandler rch) throws DataAccessException {
  682   		query(psc, new RowCallbackHandlerResultSetExtractor(rch));
  683   	}
  684   
  685   	public void query(String sql, PreparedStatementSetter pss, RowCallbackHandler rch) throws DataAccessException {
  686   		query(sql, pss, new RowCallbackHandlerResultSetExtractor(rch));
  687   	}
  688   
  689   	public void query(String sql, Object[] args, int[] argTypes, RowCallbackHandler rch) throws DataAccessException {
  690   		query(sql, new ArgTypePreparedStatementSetter(args, argTypes), rch);
  691   	}
  692   
  693   	public void query(String sql, Object[] args, RowCallbackHandler rch) throws DataAccessException {
  694   		query(sql, new ArgPreparedStatementSetter(args), rch);
  695   	}
  696   
  697   	public List query(PreparedStatementCreator psc, RowMapper rowMapper) throws DataAccessException {
  698   		return (List) query(psc, new RowMapperResultSetExtractor(rowMapper));
  699   	}
  700   
  701   	public List query(String sql, PreparedStatementSetter pss, RowMapper rowMapper) throws DataAccessException {
  702   		return (List) query(sql, pss, new RowMapperResultSetExtractor(rowMapper));
  703   	}
  704   
  705   	public List query(String sql, Object[] args, int[] argTypes, RowMapper rowMapper) throws DataAccessException {
  706   		return (List) query(sql, args, argTypes, new RowMapperResultSetExtractor(rowMapper));
  707   	}
  708   
  709   	public List query(String sql, Object[] args, RowMapper rowMapper) throws DataAccessException {
  710   		return (List) query(sql, args, new RowMapperResultSetExtractor(rowMapper));
  711   	}
  712   
  713   	public Object queryForObject(String sql, Object[] args, int[] argTypes, RowMapper rowMapper)
  714   			throws DataAccessException {
  715   
  716   		List results = (List) query(sql, args, argTypes, new RowMapperResultSetExtractor(rowMapper, 1));
  717   		return DataAccessUtils.requiredSingleResult(results);
  718   	}
  719   
  720   	public Object queryForObject(String sql, Object[] args, RowMapper rowMapper) throws DataAccessException {
  721   		List results = (List) query(sql, args, new RowMapperResultSetExtractor(rowMapper, 1));
  722   		return DataAccessUtils.requiredSingleResult(results);
  723   	}
  724   
  725   	public Object queryForObject(String sql, Object[] args, int[] argTypes, Class requiredType)
  726   			throws DataAccessException {
  727   
  728   		return queryForObject(sql, args, argTypes, getSingleColumnRowMapper(requiredType));
  729   	}
  730   
  731   	public Object queryForObject(String sql, Object[] args, Class requiredType) throws DataAccessException {
  732   		return queryForObject(sql, args, getSingleColumnRowMapper(requiredType));
  733   	}
  734   
  735   	public Map queryForMap(String sql, Object[] args, int[] argTypes) throws DataAccessException {
  736   		return (Map) queryForObject(sql, args, argTypes, getColumnMapRowMapper());
  737   	}
  738   
  739   	public Map queryForMap(String sql, Object[] args) throws DataAccessException {
  740   		return (Map) queryForObject(sql, args, getColumnMapRowMapper());
  741   	}
  742   
  743   	public long queryForLong(String sql, Object[] args, int[] argTypes) throws DataAccessException {
  744   		Number number = (Number) queryForObject(sql, args, argTypes, Long.class);
  745   		return (number != null ? number.longValue() : 0);
  746   	}
  747   
  748   	public long queryForLong(String sql, Object[] args) throws DataAccessException {
  749   		Number number = (Number) queryForObject(sql, args, Long.class);
  750   		return (number != null ? number.longValue() : 0);
  751   	}
  752   
  753   	public int queryForInt(String sql, Object[] args, int[] argTypes) throws DataAccessException {
  754   		Number number = (Number) queryForObject(sql, args, argTypes, Integer.class);
  755   		return (number != null ? number.intValue() : 0);
  756   	}
  757   
  758   	public int queryForInt(String sql, Object[] args) throws DataAccessException {
  759   		Number number = (Number) queryForObject(sql, args, Integer.class);
  760   		return (number != null ? number.intValue() : 0);
  761   	}
  762   
  763   	public List queryForList(String sql, Object[] args, int[] argTypes, Class elementType) throws DataAccessException {
  764   		return query(sql, args, argTypes, getSingleColumnRowMapper(elementType));
  765   	}
  766   
  767   	public List queryForList(String sql, Object[] args, Class elementType) throws DataAccessException {
  768   		return query(sql, args, getSingleColumnRowMapper(elementType));
  769   	}
  770   
  771   	public List queryForList(String sql, Object[] args, int[] argTypes) throws DataAccessException {
  772   		return query(sql, args, argTypes, getColumnMapRowMapper());
  773   	}
  774   
  775   	public List queryForList(String sql, Object[] args) throws DataAccessException {
  776   		return query(sql, args, getColumnMapRowMapper());
  777   	}
  778   
  779   	public SqlRowSet queryForRowSet(String sql, Object[] args, int[] argTypes) throws DataAccessException {
  780   		return (SqlRowSet) query(sql, args, argTypes, new SqlRowSetResultSetExtractor());
  781   	}
  782   
  783   	public SqlRowSet queryForRowSet(String sql, Object[] args) throws DataAccessException {
  784   		return (SqlRowSet) query(sql, args, new SqlRowSetResultSetExtractor());
  785   	}
  786   
  787   	protected int update(final PreparedStatementCreator psc, final PreparedStatementSetter pss)
  788   			throws DataAccessException {
  789   
  790   		logger.debug("Executing prepared SQL update");
  791   
  792   		Integer result = (Integer) execute(psc, new PreparedStatementCallback() {
  793   			public Object doInPreparedStatement(PreparedStatement ps) throws SQLException {
  794   				try {
  795   					if (pss != null) {
  796   						pss.setValues(ps);
  797   					}
  798   					int rows = ps.executeUpdate();
  799   					if (logger.isDebugEnabled()) {
  800   						logger.debug("SQL update affected " + rows + " rows");
  801   					}
  802   					return new Integer(rows);
  803   				}
  804   				finally {
  805   					if (pss instanceof ParameterDisposer) {
  806   						((ParameterDisposer) pss).cleanupParameters();
  807   					}
  808   				}
  809   			}
  810   		});
  811   		return result.intValue();
  812   	}
  813   
  814   	public int update(PreparedStatementCreator psc) throws DataAccessException {
  815   		return update(psc, (PreparedStatementSetter) null);
  816   	}
  817   
  818   	public int update(final PreparedStatementCreator psc, final KeyHolder generatedKeyHolder)
  819   			throws DataAccessException {
  820   
  821   		Assert.notNull(generatedKeyHolder, "KeyHolder must not be null");
  822   		logger.debug("Executing SQL update and returning generated keys");
  823   
  824   		Integer result = (Integer) execute(psc, new PreparedStatementCallback() {
  825   			public Object doInPreparedStatement(PreparedStatement ps) throws SQLException {
  826   				int rows = ps.executeUpdate();
  827   				List generatedKeys = generatedKeyHolder.getKeyList();
  828   				generatedKeys.clear();
  829   				ResultSet keys = ps.getGeneratedKeys();
  830   				if (keys != null) {
  831   					try {
  832   						RowMapper rowMapper = getColumnMapRowMapper();
  833   						RowMapperResultSetExtractor rse = new RowMapperResultSetExtractor(rowMapper, 1);
  834   						generatedKeys.addAll((List) rse.extractData(keys));
  835   					}
  836   					finally {
  837   						JdbcUtils.closeResultSet(keys);
  838   					}
  839   				}
  840   				if (logger.isDebugEnabled()) {
  841   					logger.debug("SQL update affected " + rows + " rows and returned " + generatedKeys.size() + " keys");
  842   				}
  843   				return new Integer(rows);
  844   			}
  845   		});
  846   		return result.intValue();
  847   	}
  848   
  849   	public int update(String sql, PreparedStatementSetter pss) throws DataAccessException {
  850   		return update(new SimplePreparedStatementCreator(sql), pss);
  851   	}
  852   
  853   	public int update(String sql, Object[] args, int[] argTypes) throws DataAccessException {
  854   		return update(sql, new ArgTypePreparedStatementSetter(args, argTypes));
  855   	}
  856   
  857   	public int update(String sql, Object[] args) throws DataAccessException {
  858   		return update(sql, new ArgPreparedStatementSetter(args));
  859   	}
  860   
  861   	public int[] batchUpdate(String sql, final BatchPreparedStatementSetter pss) throws DataAccessException {
  862   		if (logger.isDebugEnabled()) {
  863   			logger.debug("Executing SQL batch update [" + sql + "]");
  864   		}
  865   
  866   		return (int[]) execute(sql, new PreparedStatementCallback() {
  867   			public Object doInPreparedStatement(PreparedStatement ps) throws SQLException {
  868   				try {
  869   					int batchSize = pss.getBatchSize();
  870   					InterruptibleBatchPreparedStatementSetter ipss =
  871   							(pss instanceof InterruptibleBatchPreparedStatementSetter ?
  872   							(InterruptibleBatchPreparedStatementSetter) pss : null);
  873   					if (JdbcUtils.supportsBatchUpdates(ps.getConnection())) {
  874   						for (int i = 0; i < batchSize; i++) {
  875   							pss.setValues(ps, i);
  876   							if (ipss != null && ipss.isBatchExhausted(i)) {
  877   								break;
  878   							}
  879   							ps.addBatch();
  880   						}
  881   						return ps.executeBatch();
  882   					}
  883   					else {
  884   						List rowsAffected = new ArrayList();
  885   						for (int i = 0; i < batchSize; i++) {
  886   							pss.setValues(ps, i);
  887   							if (ipss != null && ipss.isBatchExhausted(i)) {
  888   								break;
  889   							}
  890   							rowsAffected.add(new Integer(ps.executeUpdate()));
  891   						}
  892   						int[] rowsAffectedArray = new int[rowsAffected.size()];
  893   						for (int i = 0; i < rowsAffectedArray.length; i++) {
  894   							rowsAffectedArray[i] = ((Integer) rowsAffected.get(i)).intValue();
  895   						}
  896   						return rowsAffectedArray;
  897   					}
  898   				}
  899   				finally {
  900   					if (pss instanceof ParameterDisposer) {
  901   						((ParameterDisposer) pss).cleanupParameters();
  902   					}
  903   				}
  904   			}
  905   		});
  906   	}
  907   
  908   
  909   	//-------------------------------------------------------------------------
  910   	// Methods dealing with callable statements
  911   	//-------------------------------------------------------------------------
  912   
  913   	public Object execute(CallableStatementCreator csc, CallableStatementCallback action)
  914   			throws DataAccessException {
  915   
  916   		Assert.notNull(csc, "CallableStatementCreator must not be null");
  917   		Assert.notNull(action, "Callback object must not be null");
  918   		if (logger.isDebugEnabled()) {
  919   			String sql = getSql(csc);
  920   			logger.debug("Calling stored procedure" + (sql != null ? " [" + sql  + "]" : ""));
  921   		}
  922   
  923   		Connection con = DataSourceUtils.getConnection(getDataSource());
  924   		CallableStatement cs = null;
  925   		try {
  926   			Connection conToUse = con;
  927   			if (this.nativeJdbcExtractor != null) {
  928   				conToUse = this.nativeJdbcExtractor.getNativeConnection(con);
  929   			}
  930   			cs = csc.createCallableStatement(conToUse);
  931   			applyStatementSettings(cs);
  932   			CallableStatement csToUse = cs;
  933   			if (this.nativeJdbcExtractor != null) {
  934   				csToUse = this.nativeJdbcExtractor.getNativeCallableStatement(cs);
  935   			}
  936   			Object result = action.doInCallableStatement(csToUse);
  937   			handleWarnings(cs);
  938   			return result;
  939   		}
  940   		catch (SQLException ex) {
  941   			// Release Connection early, to avoid potential connection pool deadlock
  942   			// in the case when the exception translator hasn't been initialized yet.
  943   			if (csc instanceof ParameterDisposer) {
  944   				((ParameterDisposer) csc).cleanupParameters();
  945   			}
  946   			String sql = getSql(csc);
  947   			csc = null;
  948   			JdbcUtils.closeStatement(cs);
  949   			cs = null;
  950   			DataSourceUtils.releaseConnection(con, getDataSource());
  951   			con = null;
  952   			throw getExceptionTranslator().translate("CallableStatementCallback", sql, ex);
  953   		}
  954   		finally {
  955   			if (csc instanceof ParameterDisposer) {
  956   				((ParameterDisposer) csc).cleanupParameters();
  957   			}
  958   			JdbcUtils.closeStatement(cs);
  959   			DataSourceUtils.releaseConnection(con, getDataSource());
  960   		}
  961   	}
  962   
  963   	public Object execute(String callString, CallableStatementCallback action) throws DataAccessException {
  964   		return execute(new SimpleCallableStatementCreator(callString), action);
  965   	}
  966   
  967   	public Map call(CallableStatementCreator csc, List declaredParameters) throws DataAccessException {
  968   		final List updateCountParameters = new ArrayList();
  969   		final List resultSetParameters = new ArrayList();
  970   		final List callParameters = new ArrayList();
  971   		for (int i = 0; i < declaredParameters.size(); i++) {
  972   			SqlParameter parameter = (SqlParameter) declaredParameters.get(i);
  973   			if (parameter.isResultsParameter()) {
  974   				if (parameter instanceof SqlReturnResultSet) {
  975   					resultSetParameters.add(parameter);
  976   				}
  977   				else {
  978   					updateCountParameters.add(parameter);					
  979   				}
  980   			}
  981   			else {
  982   				callParameters.add(parameter);
  983   			}
  984   		}
  985   		return (Map) execute(csc, new CallableStatementCallback() {
  986   			public Object doInCallableStatement(CallableStatement cs) throws SQLException {
  987   				boolean retVal = cs.execute();
  988   				int updateCount = cs.getUpdateCount();
  989   				if (logger.isDebugEnabled()) {
  990   					logger.debug("CallableStatement.execute() returned '" + retVal + "'");
  991   					logger.debug("CallableStatement.getUpdateCount() returned " + updateCount);
  992   				}
  993   				Map returnedResults = createResultsMap();
  994   				if (retVal || updateCount != -1) {
  995   					returnedResults.putAll(extractReturnedResults(cs, updateCountParameters, resultSetParameters, updateCount));
  996   				}
  997   				returnedResults.putAll(extractOutputParameters(cs, callParameters));
  998   				return returnedResults;
  999   			}
 1000   		});
 1001   	}
 1002   
 1003   	/**
 1004   	 * Extract returned ResultSets from the completed stored procedure.
 1005   	 * @param cs JDBC wrapper for the stored procedure
 1006   	 * @param updateCountParameters Parameter list of declared update count parameters for the stored procedure
 1007   	 * @param resultSetParameters Parameter list of declared resturn resultSet parameters for the stored procedure
 1008   	 * @return Map that contains returned results
 1009   	 */
 1010   	protected Map extractReturnedResults(
 1011   			CallableStatement cs, List updateCountParameters, List resultSetParameters, int updateCount)
 1012   			throws SQLException {
 1013   
 1014   		Map returnedResults = new HashMap();
 1015   		int rsIndex = 0;
 1016   		int updateIndex = 0;
 1017   		boolean moreResults;
 1018   		if (!skipResultsProcessing) {
 1019   			do {
 1020   				if (updateCount == -1) {
 1021   					if (resultSetParameters != null && resultSetParameters.size() > rsIndex) {
 1022   						SqlReturnResultSet declaredRsParam = (SqlReturnResultSet)resultSetParameters.get(rsIndex);
 1023   						returnedResults.putAll(processResultSet(cs.getResultSet(), declaredRsParam));
 1024   						rsIndex++;
 1025   					}
 1026   					else {
 1027   						if (!skipUndeclaredResults) {
 1028   							String rsName = RETURN_RESULT_SET_PREFIX + (rsIndex + 1);
 1029   							SqlReturnResultSet undeclaredRsParam = new SqlReturnResultSet(rsName, new ColumnMapRowMapper());
 1030   							logger.info("Added default SqlReturnResultSet parameter named " + rsName);
 1031   							returnedResults.putAll(processResultSet(cs.getResultSet(), undeclaredRsParam));
 1032   							rsIndex++;
 1033   						}
 1034   					}
 1035   				}
 1036   				else {
 1037   					if (updateCountParameters != null && updateCountParameters.size() > updateIndex) {
 1038   						SqlReturnUpdateCount ucParam = (SqlReturnUpdateCount)updateCountParameters.get(updateIndex);
 1039   						String declaredUcName = ucParam.getName();
 1040   						returnedResults.put(declaredUcName, new Integer(updateCount));
 1041   						updateIndex++;
 1042   					}
 1043   					else {
 1044   						if (!skipUndeclaredResults) {
 1045   							String undeclaredUcName = RETURN_UPDATE_COUNT_PREFIX + (updateIndex + 1);
 1046   							logger.info("Added default SqlReturnUpdateCount parameter named " + undeclaredUcName);
 1047   							returnedResults.put(undeclaredUcName, new Integer(updateCount));
 1048   							updateIndex++;
 1049   						}
 1050   					}
 1051   				}
 1052   				moreResults = cs.getMoreResults();
 1053   				updateCount = cs.getUpdateCount();
 1054   				if (logger.isDebugEnabled()) {
 1055   					logger.debug("CallableStatement.getUpdateCount() returned " + updateCount);
 1056   				}
 1057   			}
 1058   			while (moreResults || updateCount != -1);
 1059   		}
 1060   		return returnedResults;
 1061   	}
 1062   
 1063   	/**
 1064   	 * Extract output parameters from the completed stored procedure.
 1065   	 * @param cs JDBC wrapper for the stored procedure
 1066   	 * @param parameters parameter list for the stored procedure
 1067   	 * @return Map that contains returned results
 1068   	 */
 1069   	protected Map extractOutputParameters(CallableStatement cs, List parameters) throws SQLException {
 1070   		Map returnedResults = new HashMap();
 1071   		int sqlColIndex = 1;
 1072   		for (int i = 0; i < parameters.size(); i++) {
 1073   			SqlParameter param = (SqlParameter) parameters.get(i);
 1074   			if (param instanceof SqlOutParameter) {
 1075   				SqlOutParameter outParam = (SqlOutParameter) param;
 1076   				if (outParam.isReturnTypeSupported()) {
 1077   					Object out = outParam.getSqlReturnType().getTypeValue(
 1078   							cs, sqlColIndex, outParam.getSqlType(), outParam.getTypeName());
 1079   					returnedResults.put(outParam.getName(), out);
 1080   				}
 1081   				else {
 1082   					Object out = cs.getObject(sqlColIndex);
 1083   					if (out instanceof ResultSet) {
 1084   						if (outParam.isResultSetSupported()) {
 1085   							returnedResults.putAll(processResultSet((ResultSet) out, outParam));
 1086   						}
 1087   						else {
 1088   							String rsName = outParam.getName();
 1089   							SqlReturnResultSet rsParam = new SqlReturnResultSet(rsName, new ColumnMapRowMapper());
 1090   							returnedResults.putAll(processResultSet(cs.getResultSet(), rsParam));
 1091   							logger.info("Added default SqlReturnResultSet parameter named " + rsName);
 1092   						}
 1093   					}
 1094   					else {
 1095   						returnedResults.put(outParam.getName(), out);
 1096   					}
 1097   				}
 1098   			}
 1099   			if (!(param.isResultsParameter())) {
 1100   				sqlColIndex++;
 1101   			}
 1102   		}
 1103   		return returnedResults;
 1104   	}
 1105   
 1106   	/**
 1107   	 * Process the given ResultSet from a stored procedure.
 1108   	 * @param rs the ResultSet to process
 1109   	 * @param param the corresponding stored procedure parameter
 1110   	 * @return Map that contains returned results
 1111   	 */
 1112   	protected Map processResultSet(ResultSet rs, ResultSetSupportingSqlParameter param) throws SQLException {
 1113   		if (rs == null) {
 1114   			return Collections.EMPTY_MAP;
 1115   		}
 1116   		Map returnedResults = new HashMap();
 1117   		try {
 1118   			ResultSet rsToUse = rs;
 1119   			if (this.nativeJdbcExtractor != null) {
 1120   				rsToUse = this.nativeJdbcExtractor.getNativeResultSet(rs);
 1121   			}
 1122   			if (param.getRowMapper() != null) {
 1123   				RowMapper rowMapper = param.getRowMapper();
 1124   				Object result = (new RowMapperResultSetExtractor(rowMapper)).extractData(rsToUse);
 1125   				returnedResults.put(param.getName(), result);
 1126   			}
 1127   			else if (param.getRowCallbackHandler() != null) {
 1128   				RowCallbackHandler rch = param.getRowCallbackHandler();
 1129   				(new RowCallbackHandlerResultSetExtractor(rch)).extractData(rsToUse);
 1130   				returnedResults.put(param.getName(), "ResultSet returned from stored procedure was processed");
 1131   			}
 1132   			else if (param.getResultSetExtractor() != null) {
 1133   				Object result = param.getResultSetExtractor().extractData(rsToUse);
 1134   				returnedResults.put(param.getName(), result);
 1135   			}
 1136   		}
 1137   		finally {
 1138   			JdbcUtils.closeResultSet(rs);
 1139   		}
 1140   		return returnedResults;
 1141   	}
 1142   
 1143   
 1144   	//-------------------------------------------------------------------------
 1145   	// Implementation hooks and helper methods
 1146   	//-------------------------------------------------------------------------
 1147   
 1148   	/**
 1149   	 * Create a new RowMapper for reading columns as key-value pairs.
 1150   	 * @return the RowMapper to use
 1151   	 * @see ColumnMapRowMapper
 1152   	 */
 1153   	protected RowMapper getColumnMapRowMapper() {
 1154   		return new ColumnMapRowMapper();
 1155   	}
 1156   
 1157   	/**
 1158   	 * Create a new RowMapper for reading result objects from a single column.
 1159   	 * @param requiredType the type that each result object is expected to match
 1160   	 * @return the RowMapper to use
 1161   	 * @see SingleColumnRowMapper
 1162   	 */
 1163   	protected RowMapper getSingleColumnRowMapper(Class requiredType) {
 1164   		return new SingleColumnRowMapper(requiredType);
 1165   	}
 1166   
 1167   	/**
 1168   	 * Create a Map instance to be used as results map.
 1169   	 * <p>If "isResultsMapCaseInsensitive" has been set to true, a linked case-insensitive Map
 1170   	 * will be created if possible, else a plain HashMap (see Spring's CollectionFactory).
 1171   	 * @return the results Map instance
 1172   	 * @see #setResultsMapCaseInsensitive
 1173   	 * @see org.springframework.core.CollectionFactory#createLinkedCaseInsensitiveMapIfPossible
 1174   	 */
 1175   	protected Map createResultsMap() {
 1176   		if (isResultsMapCaseInsensitive()) {
 1177   			return CollectionFactory.createLinkedCaseInsensitiveMapIfPossible(10);
 1178   		}
 1179   		else {
 1180   			return new LinkedHashMap();
 1181   		}
 1182   	}
 1183   
 1184   	/**
 1185   	 * Prepare the given JDBC Statement (or PreparedStatement or CallableStatement),
 1186   	 * applying statement settings such as fetch size, max rows, and query timeout.
 1187   	 * @param stmt the JDBC Statement to prepare
 1188   	 * @throws SQLException if thrown by JDBC API
 1189   	 * @see #setFetchSize
 1190   	 * @see #setMaxRows
 1191   	 * @see #setQueryTimeout
 1192   	 * @see org.springframework.jdbc.datasource.DataSourceUtils#applyTransactionTimeout
 1193   	 */
 1194   	protected void applyStatementSettings(Statement stmt) throws SQLException {
 1195   		int fetchSize = getFetchSize();
 1196   		if (fetchSize > 0) {
 1197   			stmt.setFetchSize(fetchSize);
 1198   		}
 1199   		int maxRows = getMaxRows();
 1200   		if (maxRows > 0) {
 1201   			stmt.setMaxRows(maxRows);
 1202   		}
 1203   		DataSourceUtils.applyTimeout(stmt, getDataSource(), getQueryTimeout());
 1204   	}
 1205   
 1206   	/**
 1207   	 * Throw an SQLWarningException if we're not ignoring warnings,
 1208   	 * else log the warnings (at debug level).
 1209   	 * @param stmt the current JDBC statement
 1210   	 * @throws SQLWarningException if not ignoring warnings
 1211   	 * @see org.springframework.jdbc.SQLWarningException
 1212   	 */
 1213   	protected void handleWarnings(Statement stmt) throws SQLException {
 1214   		if (isIgnoreWarnings()) {
 1215   			if (logger.isDebugEnabled()) {
 1216   				SQLWarning warningToLog = stmt.getWarnings();
 1217   				while (warningToLog != null) {
 1218   					logger.debug("SQLWarning ignored: SQL state '" + warningToLog.getSQLState() + "', error code '" +
 1219   							warningToLog.getErrorCode() + "', message [" + warningToLog.getMessage() + "]");
 1220   					warningToLog = warningToLog.getNextWarning();
 1221   				}
 1222   			}
 1223   		}
 1224   		else {
 1225   			handleWarnings(stmt.getWarnings());
 1226   		}
 1227   	}
 1228   
 1229   	/**
 1230   	 * Throw an SQLWarningException if encountering an actual warning.
 1231   	 * @param warning the warnings object from the current statement.
 1232   	 * May be <code>null</code>, in which case this method does nothing.
 1233   	 * @throws SQLWarningException in case of an actual warning to be raised
 1234   	 */
 1235   	protected void handleWarnings(SQLWarning warning) throws SQLWarningException {
 1236   		if (warning != null) {
 1237   			throw new SQLWarningException("Warning not ignored", warning);
 1238   		}
 1239   	}
 1240   
 1241   	/**
 1242   	 * Determine SQL from potential provider object.
 1243   	 * @param sqlProvider object that's potentially a SqlProvider
 1244   	 * @return the SQL string, or <code>null</code>
 1245   	 * @see SqlProvider
 1246   	 */
 1247   	private static String getSql(Object sqlProvider) {
 1248   		if (sqlProvider instanceof SqlProvider) {
 1249   			return ((SqlProvider) sqlProvider).getSql();
 1250   		}
 1251   		else {
 1252   			return null;
 1253   		}
 1254   	}
 1255   
 1256   
 1257   	/**
 1258   	 * Invocation handler that suppresses close calls on JDBC COnnections.
 1259   	 * Also prepares returned Statement (Prepared/CallbackStatement) objects.
 1260   	 * @see java.sql.Connection#close()
 1261   	 */
 1262   	private class CloseSuppressingInvocationHandler implements InvocationHandler {
 1263   
 1264   		private final Connection target;
 1265   
 1266   		public CloseSuppressingInvocationHandler(Connection target) {
 1267   			this.target = target;
 1268   		}
 1269   
 1270   		public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
 1271   			// Invocation on ConnectionProxy interface coming in...
 1272   
 1273   			if (method.getName().equals("getTargetConnection")) {
 1274   				// Handle getTargetConnection method: return underlying Connection.
 1275   				return this.target;
 1276   			}
 1277   			else if (method.getName().equals("equals")) {
 1278   				// Only consider equal when proxies are identical.
 1279   				return (proxy == args[0] ? Boolean.TRUE : Boolean.FALSE);
 1280   			}
 1281   			else if (method.getName().equals("hashCode")) {
 1282   				// Use hashCode of PersistenceManager proxy.
 1283   				return new Integer(System.identityHashCode(proxy));
 1284   			}
 1285   			else if (method.getName().equals("close")) {
 1286   				// Handle close method: suppress, not valid.
 1287   				return null;
 1288   			}
 1289   
 1290   			// Invoke method on target Connection.
 1291   			try {
 1292   				Object retVal = method.invoke(this.target, args);
 1293   
 1294   				// If return value is a JDBC Statement, apply statement settings
 1295   				// (fetch size, max rows, transaction timeout).
 1296   				if (retVal instanceof Statement) {
 1297   					applyStatementSettings(((Statement) retVal));
 1298   				}
 1299   
 1300   				return retVal;
 1301   			}
 1302   			catch (InvocationTargetException ex) {
 1303   				throw ex.getTargetException();
 1304   			}
 1305   		}
 1306   	}
 1307   
 1308   
 1309   	/**
 1310   	 * Simple adapter for PreparedStatementCreator, allowing to use a plain SQL statement.
 1311   	 */
 1312   	private static class SimplePreparedStatementCreator implements PreparedStatementCreator, SqlProvider {
 1313   
 1314   		private final String sql;
 1315   
 1316   		public SimplePreparedStatementCreator(String sql) {
 1317   			Assert.notNull(sql, "SQL must not be null");
 1318   			this.sql = sql;
 1319   		}
 1320   
 1321   		public PreparedStatement createPreparedStatement(Connection con) throws SQLException {
 1322   			return con.prepareStatement(this.sql);
 1323   		}
 1324   
 1325   		public String getSql() {
 1326   			return this.sql;
 1327   		}
 1328   	}
 1329   
 1330   
 1331   	/**
 1332   	 * Simple adapter for CallableStatementCreator, allowing to use a plain SQL statement.
 1333   	 */
 1334   	private static class SimpleCallableStatementCreator implements CallableStatementCreator, SqlProvider {
 1335   
 1336   		private final String callString;
 1337   
 1338   		public SimpleCallableStatementCreator(String callString) {
 1339   			Assert.notNull(callString, "Call string must not be null");
 1340   			this.callString = callString;
 1341   		}
 1342   
 1343   		public CallableStatement createCallableStatement(Connection con) throws SQLException {
 1344   			return con.prepareCall(this.callString);
 1345   		}
 1346   
 1347   		public String getSql() {
 1348   			return this.callString;
 1349   		}
 1350   	}
 1351   
 1352   
 1353   	/**
 1354   	 * Adapter to enable use of a RowCallbackHandler inside a ResultSetExtractor.
 1355   	 * <p>Uses a regular ResultSet, so we have to be careful when using it:
 1356   	 * We don't use it for navigating since this could lead to unpredictable consequences.
 1357   	 */
 1358   	private static class RowCallbackHandlerResultSetExtractor implements ResultSetExtractor {
 1359   
 1360   		private final RowCallbackHandler rch;
 1361   
 1362   		public RowCallbackHandlerResultSetExtractor(RowCallbackHandler rch) {
 1363   			this.rch = rch;
 1364   		}
 1365   
 1366   		public Object extractData(ResultSet rs) throws SQLException {
 1367   			while (rs.next()) {
 1368   				this.rch.processRow(rs);
 1369   			}
 1370   			return null;
 1371   		}
 1372   	}
 1373   
 1374   }

Save This Page
Home » spring-framework-2.5.6-with-dependencies » org.springframework » jdbc » core » [javadoc | source]