Clover icon

sunshower-base

  1. Project Clover database Fri Apr 6 2018 03:41:27 UTC
  2. Package io.sunshower.test.persist

File ConnectionDetectingJDBCTemplate.java

 

Coverage histogram

../../../../img/srcFileCovDistChart0.png
50% of files have more coverage

Code metrics

178
452
113
9
1,558
1,078
215
0.48
4
12.56
1.9

Classes

Class Line # Actions
ConnectionDetectingJDBCTemplate 26 381 175
0.00%
ConnectionDetectingJDBCTemplate.ExecuteStatementCallback 320 3 2
0.00%
ConnectionDetectingJDBCTemplate.QueryStatementCallback 346 6 2
0.00%
ConnectionDetectingJDBCTemplate.UpdateStatementCallback 418 5 3
0.00%
ConnectionDetectingJDBCTemplate.BatchUpdateStatementCallback 444 22 11
0.00%
ConnectionDetectingJDBCTemplate.CloseSuppressingInvocationHandler 1437 23 13
0.00%
ConnectionDetectingJDBCTemplate.SimplePreparedStatementCreator 1492 4 3
0.00%
ConnectionDetectingJDBCTemplate.SimpleCallableStatementCreator 1514 4 3
0.00%
ConnectionDetectingJDBCTemplate.RowCallbackHandlerResultSetExtractor 1541 4 3
0.00%
 

Contributing tests

No tests hitting this source file were found.

Source view

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