001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.commons.dbutils;
018
019import java.sql.CallableStatement;
020import java.sql.Connection;
021import java.sql.PreparedStatement;
022import java.sql.ResultSet;
023import java.sql.SQLException;
024import java.sql.Statement;
025import java.util.LinkedList;
026import java.util.List;
027import javax.sql.DataSource;
028
029/**
030 * Executes SQL queries with pluggable strategies for handling
031 * {@code ResultSet}s.  This class is thread safe.
032 *
033 * @see ResultSetHandler
034 */
035public class QueryRunner extends AbstractQueryRunner {
036
037    /**
038     * Constructor for QueryRunner.
039     */
040    public QueryRunner() {
041        super();
042    }
043
044    /**
045     * Constructor for QueryRunner that controls the use of {@code ParameterMetaData}.
046     *
047     * @param pmdKnownBroken Some drivers don't support {@link java.sql.ParameterMetaData#getParameterType(int) };
048     * if {@code pmdKnownBroken} is set to true, we won't even try it; if false, we'll try it,
049     * and if it breaks, we'll remember not to use it again.
050     */
051    public QueryRunner(final boolean pmdKnownBroken) {
052        super(pmdKnownBroken);
053    }
054
055    /**
056     * Constructor for QueryRunner that takes a {@code DataSource} to use.
057     *
058     * Methods that do not take a {@code Connection} parameter will retrieve connections from this
059     * {@code DataSource}.
060     *
061     * @param ds The {@code DataSource} to retrieve connections from.
062     */
063    public QueryRunner(final DataSource ds) {
064        super(ds);
065    }
066
067    /**
068     * Constructor for QueryRunner that takes a {@code StatementConfiguration} to configure statements when
069     * preparing them.
070     *
071     * @param stmtConfig The configuration to apply to statements when they are prepared.
072     */
073    public QueryRunner(final StatementConfiguration stmtConfig) {
074        super(stmtConfig);
075    }
076
077    /**
078     * Constructor for QueryRunner that takes a {@code DataSource} and controls the use of {@code ParameterMetaData}.
079     * Methods that do not take a {@code Connection} parameter will retrieve connections from this
080     * {@code DataSource}.
081     *
082     * @param ds The {@code DataSource} to retrieve connections from.
083     * @param pmdKnownBroken Some drivers don't support {@link java.sql.ParameterMetaData#getParameterType(int) };
084     * if {@code pmdKnownBroken} is set to true, we won't even try it; if false, we'll try it,
085     * and if it breaks, we'll remember not to use it again.
086     */
087    public QueryRunner(final DataSource ds, final boolean pmdKnownBroken) {
088        super(ds, pmdKnownBroken);
089    }
090
091    /**
092     * Constructor for QueryRunner that takes a {@code DataSource} to use and a {@code StatementConfiguration}.
093     *
094     * Methods that do not take a {@code Connection} parameter will retrieve connections from this
095     * {@code DataSource}.
096     *
097     * @param ds The {@code DataSource} to retrieve connections from.
098     * @param stmtConfig The configuration to apply to statements when they are prepared.
099     */
100    public QueryRunner(final DataSource ds, final StatementConfiguration stmtConfig) {
101        super(ds, stmtConfig);
102    }
103
104    /**
105     * Constructor for QueryRunner that takes a {@code DataSource}, a {@code StatementConfiguration}, and
106     * controls the use of {@code ParameterMetaData}.  Methods that do not take a {@code Connection} parameter
107     * will retrieve connections from this {@code DataSource}.
108     *
109     * @param ds The {@code DataSource} to retrieve connections from.
110     * @param pmdKnownBroken Some drivers don't support {@link java.sql.ParameterMetaData#getParameterType(int) };
111     * if {@code pmdKnownBroken} is set to true, we won't even try it; if false, we'll try it,
112     * and if it breaks, we'll remember not to use it again.
113     * @param stmtConfig The configuration to apply to statements when they are prepared.
114     */
115    public QueryRunner(final DataSource ds, final boolean pmdKnownBroken, final StatementConfiguration stmtConfig) {
116        super(ds, pmdKnownBroken, stmtConfig);
117    }
118
119    /**
120     * Execute a batch of SQL INSERT, UPDATE, or DELETE queries.
121     *
122     * @param conn The Connection to use to run the query.  The caller is
123     * responsible for closing this Connection.
124     * @param sql The SQL to execute.
125     * @param params An array of query replacement parameters.  Each row in
126     * this array is one set of batch replacement values.
127     * @return The number of rows updated per statement.
128     * @throws SQLException if a database access error occurs
129     * @since DbUtils 1.1
130     */
131    public int[] batch(final Connection conn, final String sql, final Object[][] params) throws SQLException {
132        return this.batch(conn, false, sql, params);
133    }
134
135    /**
136     * Execute a batch of SQL INSERT, UPDATE, or DELETE queries.  The
137     * {@code Connection} is retrieved from the {@code DataSource}
138     * set in the constructor.  This {@code Connection} must be in
139     * auto-commit mode or the update will not be saved.
140     *
141     * @param sql The SQL to execute.
142     * @param params An array of query replacement parameters.  Each row in
143     * this array is one set of batch replacement values.
144     * @return The number of rows updated per statement.
145     * @throws SQLException if a database access error occurs
146     * @since DbUtils 1.1
147     */
148    public int[] batch(final String sql, final Object[][] params) throws SQLException {
149        try (final Connection conn = this.prepareConnection()) {
150            return this.batch(conn, true, sql, params);
151        }
152    }
153
154    /**
155     * Calls update after checking the parameters to ensure nothing is null.
156     * @param conn The connection to use for the batch call.
157     * @param closeConn True if the connection should be closed, false otherwise.
158     * @param sql The SQL statement to execute.
159     * @param params An array of query replacement parameters.  Each row in
160     * this array is one set of batch replacement values.
161     * @return The number of rows updated in the batch.
162     * @throws SQLException If there are database or parameter errors.
163     */
164    private int[] batch(final Connection conn, final boolean closeConn, final String sql, final Object[][] params) throws SQLException {
165        if (conn == null) {
166            throw new SQLException("Null connection");
167        }
168
169        if (sql == null) {
170            throw new SQLException("Null SQL statement");
171        }
172
173        if (params == null) {
174            throw new SQLException("Null parameters. If parameters aren't need, pass an empty array.");
175        }
176
177        PreparedStatement stmt = null;
178        int[] rows = null;
179        try {
180            stmt = this.prepareStatement(conn, sql);
181
182            for (Object[] param : params) {
183                this.fillStatement(stmt, param);
184                stmt.addBatch();
185            }
186            rows = stmt.executeBatch();
187
188        } catch (final SQLException e) {
189            this.rethrow(e, sql, (Object[])params);
190        } finally {
191            close(stmt);
192        }
193
194        return rows;
195    }
196
197    /**
198     * Execute an SQL SELECT query with a single replacement parameter. The
199     * caller is responsible for closing the connection.
200     * @param <T> The type of object that the handler returns
201     * @param conn The connection to execute the query in.
202     * @param sql The query to execute.
203     * @param param The replacement parameter.
204     * @param rsh The handler that converts the results into an object.
205     * @return The object returned by the handler.
206     * @throws SQLException if a database access error occurs
207     * @deprecated Use {@link #query(Connection, String, ResultSetHandler, Object...)}
208     */
209    @Deprecated
210    public <T> T query(final Connection conn, final String sql, final Object param, final ResultSetHandler<T> rsh) throws SQLException {
211        return this.<T>query(conn, false, sql, rsh, param);
212    }
213
214    /**
215     * Execute an SQL SELECT query with replacement parameters.  The
216     * caller is responsible for closing the connection.
217     * @param <T> The type of object that the handler returns
218     * @param conn The connection to execute the query in.
219     * @param sql The query to execute.
220     * @param params The replacement parameters.
221     * @param rsh The handler that converts the results into an object.
222     * @return The object returned by the handler.
223     * @throws SQLException if a database access error occurs
224     * @deprecated Use {@link #query(Connection,String,ResultSetHandler,Object...)} instead
225     */
226    @Deprecated
227    public <T> T query(final Connection conn, final String sql, final Object[] params, final ResultSetHandler<T> rsh) throws SQLException {
228        return this.<T>query(conn, false, sql, rsh, params);
229    }
230
231    /**
232     * Execute an SQL SELECT query with replacement parameters.  The
233     * caller is responsible for closing the connection.
234     * @param <T> The type of object that the handler returns
235     * @param conn The connection to execute the query in.
236     * @param sql The query to execute.
237     * @param rsh The handler that converts the results into an object.
238     * @param params The replacement parameters.
239     * @return The object returned by the handler.
240     * @throws SQLException if a database access error occurs
241     */
242    public <T> T query(final Connection conn, final String sql, final ResultSetHandler<T> rsh, final Object... params) throws SQLException {
243        return this.<T>query(conn, false, sql, rsh, params);
244    }
245
246    /**
247     * Execute an SQL SELECT query without any replacement parameters.  The
248     * caller is responsible for closing the connection.
249     * @param <T> The type of object that the handler returns
250     * @param conn The connection to execute the query in.
251     * @param sql The query to execute.
252     * @param rsh The handler that converts the results into an object.
253     * @return The object returned by the handler.
254     * @throws SQLException if a database access error occurs
255     */
256    public <T> T query(final Connection conn, final String sql, final ResultSetHandler<T> rsh) throws SQLException {
257        return this.<T>query(conn, false, sql, rsh, (Object[]) null);
258    }
259
260    /**
261     * Executes the given SELECT SQL with a single replacement parameter.
262     * The {@code Connection} is retrieved from the
263     * {@code DataSource} set in the constructor.
264     * @param <T> The type of object that the handler returns
265     * @param sql The SQL statement to execute.
266     * @param param The replacement parameter.
267     * @param rsh The handler used to create the result object from
268     * the {@code ResultSet}.
269     *
270     * @return An object generated by the handler.
271     * @throws SQLException if a database access error occurs
272     * @deprecated Use {@link #query(String, ResultSetHandler, Object...)}
273     */
274    @Deprecated
275    public <T> T query(final String sql, final Object param, final ResultSetHandler<T> rsh) throws SQLException {
276        try (final Connection conn = this.prepareConnection()) {
277            return this.<T>query(conn, true, sql, rsh, param);
278        }
279    }
280
281    /**
282     * Executes the given SELECT SQL query and returns a result object.
283     * The {@code Connection} is retrieved from the
284     * {@code DataSource} set in the constructor.
285     * @param <T> The type of object that the handler returns
286     * @param sql The SQL statement to execute.
287     * @param params Initialize the PreparedStatement's IN parameters with
288     * this array.
289     *
290     * @param rsh The handler used to create the result object from
291     * the {@code ResultSet}.
292     *
293     * @return An object generated by the handler.
294     * @throws SQLException if a database access error occurs
295     * @deprecated Use {@link #query(String, ResultSetHandler, Object...)}
296     */
297    @Deprecated
298    public <T> T query(final String sql, final Object[] params, final ResultSetHandler<T> rsh) throws SQLException {
299        try (final Connection conn = this.prepareConnection()) {
300            return this.<T>query(conn, true, sql, rsh, params);
301        }
302    }
303
304    /**
305     * Executes the given SELECT SQL query and returns a result object.
306     * The {@code Connection} is retrieved from the
307     * {@code DataSource} set in the constructor.
308     * @param <T> The type of object that the handler returns
309     * @param sql The SQL statement to execute.
310     * @param rsh The handler used to create the result object from
311     * the {@code ResultSet}.
312     * @param params Initialize the PreparedStatement's IN parameters with
313     * this array.
314     * @return An object generated by the handler.
315     * @throws SQLException if a database access error occurs
316     */
317    public <T> T query(final String sql, final ResultSetHandler<T> rsh, final Object... params) throws SQLException {
318        try (final Connection conn = this.prepareConnection()) {
319            return this.<T>query(conn, true, sql, rsh, params);
320        }
321    }
322
323    /**
324     * Executes the given SELECT SQL without any replacement parameters.
325     * The {@code Connection} is retrieved from the
326     * {@code DataSource} set in the constructor.
327     * @param <T> The type of object that the handler returns
328     * @param sql The SQL statement to execute.
329     * @param rsh The handler used to create the result object from
330     * the {@code ResultSet}.
331     *
332     * @return An object generated by the handler.
333     * @throws SQLException if a database access error occurs
334     */
335    public <T> T query(final String sql, final ResultSetHandler<T> rsh) throws SQLException {
336        try (final Connection conn = this.prepareConnection()) {
337            return this.<T>query(conn, true, sql, rsh, (Object[]) null);
338        }
339    }
340
341    /**
342     * Calls query after checking the parameters to ensure nothing is null.
343     * @param conn The connection to use for the query call.
344     * @param closeConn True if the connection should be closed, false otherwise.
345     * @param sql The SQL statement to execute.
346     * @param params An array of query replacement parameters.  Each row in
347     * this array is one set of batch replacement values.
348     * @return The results of the query.
349     * @throws SQLException If there are database or parameter errors.
350     */
351    private <T> T query(final Connection conn, final boolean closeConn, final String sql, final ResultSetHandler<T> rsh, final Object... params)
352            throws SQLException {
353        if (conn == null) {
354            throw new SQLException("Null connection");
355        }
356
357        if (sql == null) {
358            throw new SQLException("Null SQL statement");
359        }
360
361        if (rsh == null) {
362            throw new SQLException("Null ResultSetHandler");
363        }
364
365        Statement stmt = null;
366        ResultSet rs = null;
367        T result = null;
368
369        try {
370            if (params != null && params.length > 0) {
371                PreparedStatement ps = this.prepareStatement(conn, sql);
372                stmt = ps;
373                this.fillStatement(ps, params);
374                rs = this.wrap(ps.executeQuery());
375            } else {
376                stmt = conn.createStatement();
377                rs = this.wrap(stmt.executeQuery(sql));
378            }
379            result = rsh.handle(rs);
380
381        } catch (final SQLException e) {
382            this.rethrow(e, sql, params);
383
384        } finally {
385            closeQuietly(rs);
386            closeQuietly(stmt);
387        }
388
389        return result;
390    }
391
392    /**
393     * Execute an SQL INSERT, UPDATE, or DELETE query without replacement
394     * parameters.
395     *
396     * @param conn The connection to use to run the query.
397     * @param sql The SQL to execute.
398     * @return The number of rows updated.
399     * @throws SQLException if a database access error occurs
400     */
401    public int update(final Connection conn, final String sql) throws SQLException {
402        return this.update(conn, false, sql, (Object[]) null);
403    }
404
405    /**
406     * Execute an SQL INSERT, UPDATE, or DELETE query with a single replacement
407     * parameter.
408     *
409     * @param conn The connection to use to run the query.
410     * @param sql The SQL to execute.
411     * @param param The replacement parameter.
412     * @return The number of rows updated.
413     * @throws SQLException if a database access error occurs
414     */
415    public int update(final Connection conn, final String sql, final Object param) throws SQLException {
416        return this.update(conn, false, sql, param);
417    }
418
419    /**
420     * Execute an SQL INSERT, UPDATE, or DELETE query.
421     *
422     * @param conn The connection to use to run the query.
423     * @param sql The SQL to execute.
424     * @param params The query replacement parameters.
425     * @return The number of rows updated.
426     * @throws SQLException if a database access error occurs
427     */
428    public int update(final Connection conn, final String sql, final Object... params) throws SQLException {
429        return update(conn, false, sql, params);
430    }
431
432    /**
433     * Executes the given INSERT, UPDATE, or DELETE SQL statement without
434     * any replacement parameters. The {@code Connection} is retrieved
435     * from the {@code DataSource} set in the constructor.  This
436     * {@code Connection} must be in auto-commit mode or the update will
437     * not be saved.
438     *
439     * @param sql The SQL statement to execute.
440     * @throws SQLException if a database access error occurs
441     * @return The number of rows updated.
442     */
443    public int update(final String sql) throws SQLException {
444        try (final Connection conn = this.prepareConnection()) {
445            return this.update(conn, true, sql, (Object[]) null);
446        }
447    }
448
449    /**
450     * Executes the given INSERT, UPDATE, or DELETE SQL statement with
451     * a single replacement parameter.  The {@code Connection} is
452     * retrieved from the {@code DataSource} set in the constructor.
453     * This {@code Connection} must be in auto-commit mode or the
454     * update will not be saved.
455     *
456     * @param sql The SQL statement to execute.
457     * @param param The replacement parameter.
458     * @throws SQLException if a database access error occurs
459     * @return The number of rows updated.
460     */
461    public int update(final String sql, final Object param) throws SQLException {
462        try (final Connection conn = this.prepareConnection()) {
463            return this.update(conn, true, sql, param);
464        }
465    }
466
467    /**
468     * Executes the given INSERT, UPDATE, or DELETE SQL statement.  The
469     * {@code Connection} is retrieved from the {@code DataSource}
470     * set in the constructor.  This {@code Connection} must be in
471     * auto-commit mode or the update will not be saved.
472     *
473     * @param sql The SQL statement to execute.
474     * @param params Initializes the PreparedStatement's IN (i.e. '?')
475     * parameters.
476     * @throws SQLException if a database access error occurs
477     * @return The number of rows updated.
478     */
479    public int update(final String sql, final Object... params) throws SQLException {
480        try (final Connection conn = this.prepareConnection()) {
481            return this.update(conn, true, sql, params);
482        }
483    }
484
485    /**
486     * Calls update after checking the parameters to ensure nothing is null.
487     * @param conn The connection to use for the update call.
488     * @param closeConn True if the connection should be closed, false otherwise.
489     * @param sql The SQL statement to execute.
490     * @param params An array of update replacement parameters.  Each row in
491     * this array is one set of update replacement values.
492     * @return The number of rows updated.
493     * @throws SQLException If there are database or parameter errors.
494     */
495    private int update(final Connection conn, final boolean closeConn, final String sql, final Object... params) throws SQLException {
496        if (conn == null) {
497            throw new SQLException("Null connection");
498        }
499
500        if (sql == null) {
501            throw new SQLException("Null SQL statement");
502        }
503
504        Statement stmt = null;
505        int rows = 0;
506
507        try {
508            if (params != null && params.length > 0) {
509                PreparedStatement ps = this.prepareStatement(conn, sql);
510                stmt = ps;
511                this.fillStatement(ps, params);
512                rows = ps.executeUpdate();
513            } else {
514                stmt = conn.createStatement();
515                rows = stmt.executeUpdate(sql);
516            }
517
518        } catch (final SQLException e) {
519            this.rethrow(e, sql, params);
520
521        } finally {
522            close(stmt);
523        }
524
525        return rows;
526    }
527
528    /**
529     * Executes the given INSERT SQL without any replacement parameters.
530     * The {@code Connection} is retrieved from the
531     * {@code DataSource} set in the constructor.
532     * @param <T> The type of object that the handler returns
533     * @param sql The SQL statement to execute.
534     * @param rsh The handler used to create the result object from
535     * the {@code ResultSet} of auto-generated keys.
536     * @return An object generated by the handler.
537     * @throws SQLException if a database access error occurs
538     * @since 1.6
539     */
540    public <T> T insert(final String sql, final ResultSetHandler<T> rsh) throws SQLException {
541        try (final Connection conn = this.prepareConnection()) {
542            return insert(conn, true, sql, rsh, (Object[]) null);
543        }
544    }
545
546    /**
547     * Executes the given INSERT SQL statement. The
548     * {@code Connection} is retrieved from the {@code DataSource}
549     * set in the constructor.  This {@code Connection} must be in
550     * auto-commit mode or the insert will not be saved.
551     * @param <T> The type of object that the handler returns
552     * @param sql The SQL statement to execute.
553     * @param rsh The handler used to create the result object from
554     * the {@code ResultSet} of auto-generated keys.
555     * @param params Initializes the PreparedStatement's IN (i.e. '?')
556     * @return An object generated by the handler.
557     * @throws SQLException if a database access error occurs
558     * @since 1.6
559     */
560    public <T> T insert(final String sql, final ResultSetHandler<T> rsh, final Object... params) throws SQLException {
561        try (final Connection conn = this.prepareConnection()) {
562            return insert(conn, true, sql, rsh, params);
563        }
564    }
565
566    /**
567     * Execute an SQL INSERT query without replacement parameters.
568     * @param <T> The type of object that the handler returns
569     * @param conn The connection to use to run the query.
570     * @param sql The SQL to execute.
571     * @param rsh The handler used to create the result object from
572     * the {@code ResultSet} of auto-generated keys.
573     * @return An object generated by the handler.
574     * @throws SQLException if a database access error occurs
575     * @since 1.6
576     */
577    public <T> T insert(final Connection conn, final String sql, final ResultSetHandler<T> rsh) throws SQLException {
578        return insert(conn, false, sql, rsh, (Object[]) null);
579    }
580
581    /**
582     * Execute an SQL INSERT query.
583     * @param <T> The type of object that the handler returns
584     * @param conn The connection to use to run the query.
585     * @param sql The SQL to execute.
586     * @param rsh The handler used to create the result object from
587     * the {@code ResultSet} of auto-generated keys.
588     * @param params The query replacement parameters.
589     * @return An object generated by the handler.
590     * @throws SQLException if a database access error occurs
591     * @since 1.6
592     */
593    public <T> T insert(final Connection conn, final String sql, final ResultSetHandler<T> rsh, final Object... params) throws SQLException {
594        return insert(conn, false, sql, rsh, params);
595    }
596
597    /**
598     * Executes the given INSERT SQL statement.
599     * @param conn The connection to use for the query call.
600     * @param closeConn True if the connection should be closed, false otherwise.
601     * @param sql The SQL statement to execute.
602     * @param rsh The handler used to create the result object from
603     * the {@code ResultSet} of auto-generated keys.
604     * @param params The query replacement parameters.
605     * @return An object generated by the handler.
606     * @throws SQLException If there are database or parameter errors.
607     * @since 1.6
608     */
609    private <T> T insert(final Connection conn, final boolean closeConn, final String sql, final ResultSetHandler<T> rsh, final Object... params)
610            throws SQLException {
611        if (conn == null) {
612            throw new SQLException("Null connection");
613        }
614
615        if (sql == null) {
616            throw new SQLException("Null SQL statement");
617        }
618
619        if (rsh == null) {
620            throw new SQLException("Null ResultSetHandler");
621        }
622
623        Statement stmt = null;
624        T generatedKeys = null;
625
626        try {
627            if (params != null && params.length > 0) {
628                PreparedStatement ps = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
629                stmt = ps;
630                this.fillStatement(ps, params);
631                ps.executeUpdate();
632            } else {
633                stmt = conn.createStatement();
634                stmt.executeUpdate(sql, Statement.RETURN_GENERATED_KEYS);
635            }
636            final ResultSet resultSet = stmt.getGeneratedKeys();
637            generatedKeys = rsh.handle(resultSet);
638        } catch (final SQLException e) {
639            this.rethrow(e, sql, params);
640        } finally {
641            close(stmt);
642        }
643
644        return generatedKeys;
645    }
646
647    /**
648     * Executes the given batch of INSERT SQL statements. The
649     * {@code Connection} is retrieved from the {@code DataSource}
650     * set in the constructor.  This {@code Connection} must be in
651     * auto-commit mode or the insert will not be saved.
652     * @param <T> The type of object that the handler returns
653     * @param sql The SQL statement to execute.
654     * @param rsh The handler used to create the result object from
655     * the {@code ResultSet} of auto-generated keys.
656     * @param params Initializes the PreparedStatement's IN (i.e. '?')
657     * @return The result generated by the handler.
658     * @throws SQLException if a database access error occurs
659     * @since 1.6
660     */
661    public <T> T insertBatch(final String sql, final ResultSetHandler<T> rsh, final Object[][] params) throws SQLException {
662        try (final Connection conn = this.prepareConnection()) {
663            return insertBatch(conn, true, sql, rsh, params);
664        }
665    }
666
667    /**
668     * Executes the given batch of INSERT SQL statements.
669     * @param <T> The type of object that the handler returns
670     * @param conn The connection to use to run the query.
671     * @param sql The SQL to execute.
672     * @param rsh The handler used to create the result object from
673     * the {@code ResultSet} of auto-generated keys.
674     * @param params The query replacement parameters.
675     * @return The result generated by the handler.
676     * @throws SQLException if a database access error occurs
677     * @since 1.6
678     */
679    public <T> T insertBatch(final Connection conn, final String sql, final ResultSetHandler<T> rsh, final Object[][] params) throws SQLException {
680        return insertBatch(conn, false, sql, rsh, params);
681    }
682
683    /**
684     * Executes the given batch of INSERT SQL statements.
685     * @param conn The connection to use for the query call.
686     * @param closeConn True if the connection should be closed, false otherwise.
687     * @param sql The SQL statement to execute.
688     * @param rsh The handler used to create the result object from
689     * the {@code ResultSet} of auto-generated keys.
690     * @param params The query replacement parameters.
691     * @return The result generated by the handler.
692     * @throws SQLException If there are database or parameter errors.
693     * @since 1.6
694     */
695    private <T> T insertBatch(final Connection conn, final boolean closeConn, final String sql, final ResultSetHandler<T> rsh, final Object[][] params)
696            throws SQLException {
697        if (conn == null) {
698            throw new SQLException("Null connection");
699        }
700
701        if (sql == null) {
702            throw new SQLException("Null SQL statement");
703        }
704
705        if (params == null) {
706            throw new SQLException("Null parameters. If parameters aren't need, pass an empty array.");
707        }
708
709        PreparedStatement stmt = null;
710        T generatedKeys = null;
711        try {
712            stmt = this.prepareStatement(conn, sql, Statement.RETURN_GENERATED_KEYS);
713
714            for (Object[] param : params) {
715                this.fillStatement(stmt, param);
716                stmt.addBatch();
717            }
718            stmt.executeBatch();
719            final ResultSet rs = stmt.getGeneratedKeys();
720            generatedKeys = rsh.handle(rs);
721
722        } catch (final SQLException e) {
723            this.rethrow(e, sql, (Object[])params);
724        } finally {
725            close(stmt);
726        }
727
728        return generatedKeys;
729    }
730
731    /**
732     * Execute an SQL statement, including a stored procedure call, which does
733     * not return any result sets.
734     * Any parameters which are instances of {@link OutParameter} will be
735     * registered as OUT parameters.
736     * <p>
737     * Use this method when invoking a stored procedure with OUT parameters
738     * that does not return any result sets.  If you are not invoking a stored
739     * procedure, or the stored procedure has no OUT parameters, consider using
740     * {@link #update(java.sql.Connection, java.lang.String, java.lang.Object...) }.
741     * If the stored procedure returns result sets, use
742     * {@link #execute(java.sql.Connection, java.lang.String, org.apache.commons.dbutils.ResultSetHandler, java.lang.Object...) }.
743     *
744     * @param conn The connection to use to run the query.
745     * @param sql The SQL to execute.
746     * @param params The query replacement parameters.
747     * @return The number of rows updated.
748     * @throws SQLException if a database access error occurs
749     */
750    public int execute(final Connection conn, final String sql, final Object... params) throws SQLException {
751        return this.execute(conn, false, sql, params);
752    }
753
754    /**
755     * Execute an SQL statement, including a stored procedure call, which does
756     * not return any result sets.
757     * Any parameters which are instances of {@link OutParameter} will be
758     * registered as OUT parameters.
759     * <p>
760     * Use this method when invoking a stored procedure with OUT parameters
761     * that does not return any result sets.  If you are not invoking a stored
762     * procedure, or the stored procedure has no OUT parameters, consider using
763     * {@link #update(java.lang.String, java.lang.Object...) }.
764     * If the stored procedure returns result sets, use
765     * {@link #execute(java.lang.String, org.apache.commons.dbutils.ResultSetHandler, java.lang.Object...) }.
766     * <p>
767     * The {@code Connection} is retrieved from the {@code DataSource}
768     * set in the constructor.  This {@code Connection} must be in
769     * auto-commit mode or the update will not be saved.
770     *
771     * @param sql The SQL statement to execute.
772     * @param params Initializes the CallableStatement's parameters (i.e. '?').
773     * @throws SQLException if a database access error occurs
774     * @return The number of rows updated.
775     */
776    public int execute(final String sql, final Object... params) throws SQLException {
777        try (final Connection conn = this.prepareConnection()) {
778            return this.execute(conn, true, sql, params);
779        }
780    }
781
782    /**
783     * Execute an SQL statement, including a stored procedure call, which
784     * returns one or more result sets.
785     * Any parameters which are instances of {@link OutParameter} will be
786     * registered as OUT parameters.
787     * <p>
788     * Use this method when: a) running SQL statements that return multiple
789     * result sets; b) invoking a stored procedure that return result
790     * sets and OUT parameters.  Otherwise you may wish to use
791     * {@link #query(java.sql.Connection, java.lang.String, org.apache.commons.dbutils.ResultSetHandler, java.lang.Object...) }
792     * (if there are no OUT parameters) or
793     * {@link #execute(java.sql.Connection, java.lang.String, java.lang.Object...) }
794     * (if there are no result sets).
795     *
796     * @param <T> The type of object that the handler returns
797     * @param conn The connection to use to run the query.
798     * @param sql The SQL to execute.
799     * @param rsh The result set handler
800     * @param params The query replacement parameters.
801     * @return A list of objects generated by the handler
802     * @throws SQLException if a database access error occurs
803     */
804    public <T> List<T> execute(final Connection conn, final String sql, final ResultSetHandler<T> rsh, final Object... params) throws SQLException {
805        return this.execute(conn, false, sql, rsh, params);
806    }
807
808    /**
809     * Execute an SQL statement, including a stored procedure call, which
810     * returns one or more result sets.
811     * Any parameters which are instances of {@link OutParameter} will be
812     * registered as OUT parameters.
813     * <p>
814     * Use this method when: a) running SQL statements that return multiple
815     * result sets; b) invoking a stored procedure that return result
816     * sets and OUT parameters.  Otherwise you may wish to use
817     * {@link #query(java.lang.String, org.apache.commons.dbutils.ResultSetHandler, java.lang.Object...) }
818     * (if there are no OUT parameters) or
819     * {@link #execute(java.lang.String, java.lang.Object...) }
820     * (if there are no result sets).
821     *
822     * @param <T> The type of object that the handler returns
823     * @param sql The SQL to execute.
824     * @param rsh The result set handler
825     * @param params The query replacement parameters.
826     * @return A list of objects generated by the handler
827     * @throws SQLException if a database access error occurs
828     */
829    public <T> List<T> execute(final String sql, final ResultSetHandler<T> rsh, final Object... params) throws SQLException {
830        try (final Connection conn = this.prepareConnection()) {
831            return this.execute(conn, true, sql, rsh, params);
832        }
833    }
834
835    /**
836     * Invokes the stored procedure via update after checking the parameters to
837     * ensure nothing is null.
838     * @param conn The connection to use for the update call.
839     * @param closeConn True if the connection should be closed, false otherwise.
840     * @param sql The SQL statement to execute.
841     * @param params An array of update replacement parameters.  Each row in
842     * this array is one set of update replacement values.
843     * @return The number of rows updated.
844     * @throws SQLException If there are database or parameter errors.
845     */
846    private int execute(final Connection conn, final boolean closeConn, final String sql, final Object... params) throws SQLException {
847        if (conn == null) {
848            throw new SQLException("Null connection");
849        }
850
851        if (sql == null) {
852            throw new SQLException("Null SQL statement");
853        }
854
855        CallableStatement stmt = null;
856        int rows = 0;
857
858        try {
859            stmt = this.prepareCall(conn, sql);
860            this.fillStatement(stmt, params);
861            stmt.execute();
862            rows = stmt.getUpdateCount();
863            this.retrieveOutParameters(stmt, params);
864
865        } catch (final SQLException e) {
866            this.rethrow(e, sql, params);
867
868        } finally {
869            close(stmt);
870        }
871
872        return rows;
873    }
874
875    /**
876     * Invokes the stored procedure via update after checking the parameters to
877     * ensure nothing is null.
878     * @param conn The connection to use for the update call.
879     * @param closeConn True if the connection should be closed, false otherwise.
880     * @param sql The SQL statement to execute.
881     * @param rsh The result set handler
882     * @param params An array of update replacement parameters.  Each row in
883     * this array is one set of update replacement values.
884     * @return List of all objects generated by the ResultSetHandler for all result sets handled.
885     * @throws SQLException If there are database or parameter errors.
886     */
887    private <T> List<T> execute(final Connection conn, final boolean closeConn, final String sql, final ResultSetHandler<T> rsh, final Object... params) throws SQLException {
888        if (conn == null) {
889            throw new SQLException("Null connection");
890        }
891
892        if (sql == null) {
893            throw new SQLException("Null SQL statement");
894        }
895
896        if (rsh == null) {
897            throw new SQLException("Null ResultSetHandler");
898        }
899
900        CallableStatement stmt = null;
901        final List<T> results = new LinkedList<>();
902
903        try {
904            stmt = this.prepareCall(conn, sql);
905            this.fillStatement(stmt, params);
906            boolean moreResultSets = stmt.execute();
907            // Handle multiple result sets by passing them through the handler
908            // retaining the final result
909            ResultSet rs = null;
910            while (moreResultSets) {
911                try {
912                    rs = this.wrap(stmt.getResultSet());
913                    results.add(rsh.handle(rs));
914                    moreResultSets = stmt.getMoreResults();
915
916                } finally {
917                    close(rs);
918                }
919            }
920            this.retrieveOutParameters(stmt, params);
921
922        } catch (final SQLException e) {
923            this.rethrow(e, sql, params);
924
925        } finally {
926            close(stmt);
927        }
928
929        return results;
930    }
931
932    /**
933     * Set the value on all the {@link OutParameter} instances in the
934     * {@code params} array using the OUT parameter values from the
935     * {@code stmt}.
936     * @param stmt the statement from which to retrieve OUT parameter values
937     * @param params the parameter array for the statement invocation
938     * @throws SQLException when the value could not be retrieved from the
939     * statement.
940     */
941    private void retrieveOutParameters(final CallableStatement stmt, final Object[] params) throws SQLException {
942        if (params != null) {
943            for (int i = 0; i < params.length; i++) {
944                if (params[i] instanceof OutParameter) {
945                    ((OutParameter)params[i]).setValue(stmt, i + 1);
946                }
947            }
948        }
949    }
950}