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     */
017    package org.apache.commons.dbutils;
018    
019    import java.sql.Connection;
020    import java.sql.PreparedStatement;
021    import java.sql.ResultSet;
022    import java.sql.SQLException;
023    
024    import javax.sql.DataSource;
025    
026    /**
027     * Executes SQL queries with pluggable strategies for handling
028     * <code>ResultSet</code>s.  This class is thread safe.
029     *
030     * @see ResultSetHandler
031     */
032    public class QueryRunner extends AbstractQueryRunner {
033    
034        /**
035         * Constructor for QueryRunner.
036         */
037        public QueryRunner() {
038            super();
039        }
040    
041        /**
042         * Constructor for QueryRunner that controls the use of <code>ParameterMetaData</code>.
043         *
044         * @param pmdKnownBroken Some drivers don't support {@link java.sql.ParameterMetaData#getParameterType(int) };
045         * if <code>pmdKnownBroken</code> is set to true, we won't even try it; if false, we'll try it,
046         * and if it breaks, we'll remember not to use it again.
047         */
048        public QueryRunner(boolean pmdKnownBroken) {
049            super(pmdKnownBroken);
050        }
051    
052        /**
053         * Constructor for QueryRunner that takes a <code>DataSource</code> to use.
054         *
055         * Methods that do not take a <code>Connection</code> parameter will retrieve connections from this
056         * <code>DataSource</code>.
057         *
058         * @param ds The <code>DataSource</code> to retrieve connections from.
059         */
060        public QueryRunner(DataSource ds) {
061            super(ds);
062        }
063    
064        /**
065         * Constructor for QueryRunner that takes a <code>DataSource</code> and controls the use of <code>ParameterMetaData</code>.
066         * Methods that do not take a <code>Connection</code> parameter will retrieve connections from this
067         * <code>DataSource</code>.
068         *
069         * @param ds The <code>DataSource</code> to retrieve connections from.
070         * @param pmdKnownBroken Some drivers don't support {@link java.sql.ParameterMetaData#getParameterType(int) };
071         * if <code>pmdKnownBroken</code> is set to true, we won't even try it; if false, we'll try it,
072         * and if it breaks, we'll remember not to use it again.
073         */
074        public QueryRunner(DataSource ds, boolean pmdKnownBroken) {
075            super(ds, pmdKnownBroken);
076        }
077    
078        /**
079         * Execute a batch of SQL INSERT, UPDATE, or DELETE queries.
080         *
081         * @param conn The Connection to use to run the query.  The caller is
082         * responsible for closing this Connection.
083         * @param sql The SQL to execute.
084         * @param params An array of query replacement parameters.  Each row in
085         * this array is one set of batch replacement values.
086         * @return The number of rows updated per statement.
087         * @throws SQLException if a database access error occurs
088         * @since DbUtils 1.1
089         */
090        public int[] batch(Connection conn, String sql, Object[][] params) throws SQLException {
091            return this.batch(conn, false, sql, params);
092        }
093    
094        /**
095         * Execute a batch of SQL INSERT, UPDATE, or DELETE queries.  The
096         * <code>Connection</code> is retrieved from the <code>DataSource</code>
097         * set in the constructor.  This <code>Connection</code> must be in
098         * auto-commit mode or the update will not be saved.
099         *
100         * @param sql The SQL to execute.
101         * @param params An array of query replacement parameters.  Each row in
102         * this array is one set of batch replacement values.
103         * @return The number of rows updated per statement.
104         * @throws SQLException if a database access error occurs
105         * @since DbUtils 1.1
106         */
107        public int[] batch(String sql, Object[][] params) throws SQLException {
108            Connection conn = this.prepareConnection();
109    
110            return this.batch(conn, true, sql, params);
111        }
112    
113        /**
114         * Calls update after checking the parameters to ensure nothing is null.
115         * @param conn The connection to use for the batch call.
116         * @param closeConn True if the connection should be closed, false otherwise.
117         * @param sql The SQL statement to execute.
118         * @param params An array of query replacement parameters.  Each row in
119         * this array is one set of batch replacement values.
120         * @return The number of rows updated in the batch.
121         * @throws SQLException If there are database or parameter errors.
122         */
123        private int[] batch(Connection conn, boolean closeConn, String sql, Object[][] params) throws SQLException {
124            if (conn == null) {
125                throw new SQLException("Null connection");
126            }
127    
128            if (sql == null) {
129                if (closeConn) {
130                    close(conn);
131                }
132                throw new SQLException("Null SQL statement");
133            }
134    
135            if (params == null) {
136                if (closeConn) {
137                    close(conn);
138                }
139                throw new SQLException("Null parameters. If parameters aren't need, pass an empty array.");
140            }
141    
142            PreparedStatement stmt = null;
143            int[] rows = null;
144            try {
145                stmt = this.prepareStatement(conn, sql);
146    
147                for (int i = 0; i < params.length; i++) {
148                    this.fillStatement(stmt, params[i]);
149                    stmt.addBatch();
150                }
151                rows = stmt.executeBatch();
152    
153            } catch (SQLException e) {
154                this.rethrow(e, sql, (Object[])params);
155            } finally {
156                close(stmt);
157                if (closeConn) {
158                    close(conn);
159                }
160            }
161    
162            return rows;
163        }
164    
165        /**
166         * Execute an SQL SELECT query with a single replacement parameter. The
167         * caller is responsible for closing the connection.
168         * @param <T> The type of object that the handler returns
169         * @param conn The connection to execute the query in.
170         * @param sql The query to execute.
171         * @param param The replacement parameter.
172         * @param rsh The handler that converts the results into an object.
173         * @return The object returned by the handler.
174         * @throws SQLException if a database access error occurs
175         * @deprecated Use {@link #query(Connection, String, ResultSetHandler, Object...)}
176         */
177        @Deprecated
178        public <T> T query(Connection conn, String sql, Object param, ResultSetHandler<T> rsh) throws SQLException {
179            return this.<T>query(conn, false, sql, rsh, new Object[]{param});
180        }
181    
182        /**
183         * Execute an SQL SELECT query with replacement parameters.  The
184         * caller is responsible for closing the connection.
185         * @param <T> The type of object that the handler returns
186         * @param conn The connection to execute the query in.
187         * @param sql The query to execute.
188         * @param params The replacement parameters.
189         * @param rsh The handler that converts the results into an object.
190         * @return The object returned by the handler.
191         * @throws SQLException if a database access error occurs
192         * @deprecated Use {@link #query(Connection,String,ResultSetHandler,Object...)} instead
193         */
194        @Deprecated
195        public <T> T query(Connection conn, String sql, Object[] params, ResultSetHandler<T> rsh) throws SQLException {
196                    return this.<T>query(conn, false, sql, rsh, params);
197        }
198    
199        /**
200         * Execute an SQL SELECT query with replacement parameters.  The
201         * caller is responsible for closing the connection.
202         * @param <T> The type of object that the handler returns
203         * @param conn The connection to execute the query in.
204         * @param sql The query to execute.
205         * @param rsh The handler that converts the results into an object.
206         * @param params The replacement parameters.
207         * @return The object returned by the handler.
208         * @throws SQLException if a database access error occurs
209         */
210        public <T> T query(Connection conn, String sql, ResultSetHandler<T> rsh, Object... params) throws SQLException {
211            return this.<T>query(conn, false, sql, rsh, params);
212        }
213    
214        /**
215         * Execute an SQL SELECT query without any 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 rsh The handler that converts the results into an object.
221         * @return The object returned by the handler.
222         * @throws SQLException if a database access error occurs
223         */
224        public <T> T query(Connection conn, String sql, ResultSetHandler<T> rsh) throws SQLException {
225            return this.<T>query(conn, false, sql, rsh, (Object[]) null);
226        }
227    
228        /**
229         * Executes the given SELECT SQL with a single replacement parameter.
230         * The <code>Connection</code> is retrieved from the
231         * <code>DataSource</code> set in the constructor.
232         * @param <T> The type of object that the handler returns
233         * @param sql The SQL statement to execute.
234         * @param param The replacement parameter.
235         * @param rsh The handler used to create the result object from
236         * the <code>ResultSet</code>.
237         *
238         * @return An object generated by the handler.
239         * @throws SQLException if a database access error occurs
240         * @deprecated Use {@link #query(String, ResultSetHandler, Object...)}
241         */
242        @Deprecated
243        public <T> T query(String sql, Object param, ResultSetHandler<T> rsh) throws SQLException {
244            Connection conn = this.prepareConnection();
245    
246            return this.<T>query(conn, true, sql, rsh, new Object[]{param});
247        }
248    
249        /**
250         * Executes the given SELECT SQL query and returns a result object.
251         * The <code>Connection</code> is retrieved from the
252         * <code>DataSource</code> set in the constructor.
253         * @param <T> The type of object that the handler returns
254         * @param sql The SQL statement to execute.
255         * @param params Initialize the PreparedStatement's IN parameters with
256         * this array.
257         *
258         * @param rsh The handler used to create the result object from
259         * the <code>ResultSet</code>.
260         *
261         * @return An object generated by the handler.
262         * @throws SQLException if a database access error occurs
263         * @deprecated Use {@link #query(String, ResultSetHandler, Object...)}
264         */
265        @Deprecated
266        public <T> T query(String sql, Object[] params, ResultSetHandler<T> rsh) throws SQLException {
267            Connection conn = this.prepareConnection();
268    
269            return this.<T>query(conn, true, sql, rsh, params);
270        }
271    
272        /**
273         * Executes the given SELECT SQL query and returns a result object.
274         * The <code>Connection</code> is retrieved from the
275         * <code>DataSource</code> set in the constructor.
276         * @param <T> The type of object that the handler returns
277         * @param sql The SQL statement to execute.
278         * @param rsh The handler used to create the result object from
279         * the <code>ResultSet</code>.
280         * @param params Initialize the PreparedStatement's IN parameters with
281         * this array.
282         * @return An object generated by the handler.
283         * @throws SQLException if a database access error occurs
284         */
285        public <T> T query(String sql, ResultSetHandler<T> rsh, Object... params) throws SQLException {
286            Connection conn = this.prepareConnection();
287    
288            return this.<T>query(conn, true, sql, rsh, params);
289        }
290    
291        /**
292         * Executes the given SELECT SQL without any replacement parameters.
293         * The <code>Connection</code> is retrieved from the
294         * <code>DataSource</code> set in the constructor.
295         * @param <T> The type of object that the handler returns
296         * @param sql The SQL statement to execute.
297         * @param rsh The handler used to create the result object from
298         * the <code>ResultSet</code>.
299         *
300         * @return An object generated by the handler.
301         * @throws SQLException if a database access error occurs
302         */
303        public <T> T query(String sql, ResultSetHandler<T> rsh) throws SQLException {
304            Connection conn = this.prepareConnection();
305    
306            return this.<T>query(conn, true, sql, rsh, (Object[]) null);
307        }
308    
309        /**
310         * Calls query after checking the parameters to ensure nothing is null.
311         * @param conn The connection to use for the query call.
312         * @param closeConn True if the connection should be closed, false otherwise.
313         * @param sql The SQL statement to execute.
314         * @param params An array of query replacement parameters.  Each row in
315         * this array is one set of batch replacement values.
316         * @return The results of the query.
317         * @throws SQLException If there are database or parameter errors.
318         */
319        private <T> T query(Connection conn, boolean closeConn, String sql, ResultSetHandler<T> rsh, Object... params)
320                throws SQLException {
321            if (conn == null) {
322                throw new SQLException("Null connection");
323            }
324    
325            if (sql == null) {
326                if (closeConn) {
327                    close(conn);
328                }
329                throw new SQLException("Null SQL statement");
330            }
331    
332            if (rsh == null) {
333                if (closeConn) {
334                    close(conn);
335                }
336                throw new SQLException("Null ResultSetHandler");
337            }
338    
339            PreparedStatement stmt = null;
340            ResultSet rs = null;
341            T result = null;
342    
343            try {
344                stmt = this.prepareStatement(conn, sql);
345                this.fillStatement(stmt, params);
346                rs = this.wrap(stmt.executeQuery());
347                result = rsh.handle(rs);
348    
349            } catch (SQLException e) {
350                this.rethrow(e, sql, params);
351    
352            } finally {
353                try {
354                    close(rs);
355                } finally {
356                    close(stmt);
357                    if (closeConn) {
358                        close(conn);
359                    }
360                }
361            }
362    
363            return result;
364        }
365    
366        /**
367         * Execute an SQL INSERT, UPDATE, or DELETE query without replacement
368         * parameters.
369         *
370         * @param conn The connection to use to run the query.
371         * @param sql The SQL to execute.
372         * @return The number of rows updated.
373         * @throws SQLException if a database access error occurs
374         */
375        public int update(Connection conn, String sql) throws SQLException {
376            return this.update(conn, false, sql, (Object[]) null);
377        }
378    
379        /**
380         * Execute an SQL INSERT, UPDATE, or DELETE query with a single replacement
381         * parameter.
382         *
383         * @param conn The connection to use to run the query.
384         * @param sql The SQL to execute.
385         * @param param The replacement parameter.
386         * @return The number of rows updated.
387         * @throws SQLException if a database access error occurs
388         */
389        public int update(Connection conn, String sql, Object param) throws SQLException {
390            return this.update(conn, false, sql, new Object[]{param});
391        }
392    
393        /**
394         * Execute an SQL INSERT, UPDATE, or DELETE query.
395         *
396         * @param conn The connection to use to run the query.
397         * @param sql The SQL to execute.
398         * @param params The query replacement parameters.
399         * @return The number of rows updated.
400         * @throws SQLException if a database access error occurs
401         */
402        public int update(Connection conn, String sql, Object... params) throws SQLException {
403            return update(conn, false, sql, params);
404        }
405    
406        /**
407         * Executes the given INSERT, UPDATE, or DELETE SQL statement without
408         * any replacement parameters. The <code>Connection</code> is retrieved
409         * from the <code>DataSource</code> set in the constructor.  This
410         * <code>Connection</code> must be in auto-commit mode or the update will
411         * not be saved.
412         *
413         * @param sql The SQL statement to execute.
414         * @throws SQLException if a database access error occurs
415         * @return The number of rows updated.
416         */
417        public int update(String sql) throws SQLException {
418            Connection conn = this.prepareConnection();
419    
420            return this.update(conn, true, sql, (Object[]) null);
421        }
422    
423        /**
424         * Executes the given INSERT, UPDATE, or DELETE SQL statement with
425         * a single replacement parameter.  The <code>Connection</code> is
426         * retrieved from the <code>DataSource</code> set in the constructor.
427         * This <code>Connection</code> must be in auto-commit mode or the
428         * update will not be saved.
429         *
430         * @param sql The SQL statement to execute.
431         * @param param The replacement parameter.
432         * @throws SQLException if a database access error occurs
433         * @return The number of rows updated.
434         */
435        public int update(String sql, Object param) throws SQLException {
436            Connection conn = this.prepareConnection();
437    
438            return this.update(conn, true, sql, new Object[]{param});
439        }
440    
441        /**
442         * Executes the given INSERT, UPDATE, or DELETE SQL statement.  The
443         * <code>Connection</code> is retrieved from the <code>DataSource</code>
444         * set in the constructor.  This <code>Connection</code> must be in
445         * auto-commit mode or the update will not be saved.
446         *
447         * @param sql The SQL statement to execute.
448         * @param params Initializes the PreparedStatement's IN (i.e. '?')
449         * parameters.
450         * @throws SQLException if a database access error occurs
451         * @return The number of rows updated.
452         */
453        public int update(String sql, Object... params) throws SQLException {
454            Connection conn = this.prepareConnection();
455    
456            return this.update(conn, true, sql, params);
457        }
458    
459        /**
460         * Calls update after checking the parameters to ensure nothing is null.
461         * @param conn The connection to use for the update call.
462         * @param closeConn True if the connection should be closed, false otherwise.
463         * @param sql The SQL statement to execute.
464         * @param params An array of update replacement parameters.  Each row in
465         * this array is one set of update replacement values.
466         * @return The number of rows updated.
467         * @throws SQLException If there are database or parameter errors.
468         */
469        private int update(Connection conn, boolean closeConn, String sql, Object... params) throws SQLException {
470            if (conn == null) {
471                throw new SQLException("Null connection");
472            }
473    
474            if (sql == null) {
475                if (closeConn) {
476                    close(conn);
477                }
478                throw new SQLException("Null SQL statement");
479            }
480    
481            PreparedStatement stmt = null;
482            int rows = 0;
483    
484            try {
485                stmt = this.prepareStatement(conn, sql);
486                this.fillStatement(stmt, params);
487                rows = stmt.executeUpdate();
488    
489            } catch (SQLException e) {
490                this.rethrow(e, sql, params);
491    
492            } finally {
493                close(stmt);
494                if (closeConn) {
495                    close(conn);
496                }
497            }
498    
499            return rows;
500        }
501    
502    }