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.beans.IntrospectionException;
020 import java.beans.Introspector;
021 import java.beans.PropertyDescriptor;
022 import java.lang.reflect.InvocationTargetException;
023 import java.lang.reflect.Method;
024 import java.sql.Connection;
025 import java.sql.ParameterMetaData;
026 import java.sql.PreparedStatement;
027 import java.sql.ResultSet;
028 import java.sql.SQLException;
029 import java.sql.Statement;
030 import java.sql.Types;
031 import java.util.Arrays;
032
033 import javax.sql.DataSource;
034
035 /**
036 * Executes SQL queries with pluggable strategies for handling
037 * <code>ResultSet</code>s. This class is thread safe.
038 *
039 * @see ResultSetHandler
040 */
041 public class QueryRunner {
042
043 /**
044 * Is {@link ParameterMetaData#getParameterType(int)} broken (have we tried it yet)?
045 */
046 private volatile boolean pmdKnownBroken = false;
047
048 /**
049 * The DataSource to retrieve connections from.
050 */
051 protected final DataSource ds;
052
053 /**
054 * Constructor for QueryRunner.
055 */
056 public QueryRunner() {
057 super();
058 ds = null;
059 }
060
061 /**
062 * Constructor for QueryRunner, allows workaround for Oracle drivers
063 * @param pmdKnownBroken Oracle drivers don't support {@link ParameterMetaData#getParameterType(int) };
064 * if <code>pmdKnownBroken</code> is set to true, we won't even try it; if false, we'll try it,
065 * and if it breaks, we'll remember not to use it again.
066 */
067 public QueryRunner(boolean pmdKnownBroken) {
068 super();
069 this.pmdKnownBroken = pmdKnownBroken;
070 ds = null;
071 }
072
073 /**
074 * Constructor for QueryRunner, allows workaround for Oracle drivers. Methods that do not take a
075 * <code>Connection</code> parameter will retrieve connections from this
076 * <code>DataSource</code>.
077 *
078 * @param ds The <code>DataSource</code> to retrieve connections from.
079 */
080 public QueryRunner(DataSource ds) {
081 super();
082 this.ds = ds;
083 }
084
085 /**
086 * Constructor for QueryRunner, allows workaround for Oracle drivers. Methods that do not take a
087 * <code>Connection</code> parameter will retrieve connections from this
088 * <code>DataSource</code>.
089 *
090 * @param ds The <code>DataSource</code> to retrieve connections from.
091 * @param pmdKnownBroken Oracle drivers don't support {@link ParameterMetaData#getParameterType(int) };
092 * if <code>pmdKnownBroken</code> is set to true, we won't even try it; if false, we'll try it,
093 * and if it breaks, we'll remember not to use it again.
094 */
095 public QueryRunner(DataSource ds, boolean pmdKnownBroken) {
096 super();
097 this.pmdKnownBroken = pmdKnownBroken;
098 this.ds = ds;
099 }
100
101 /**
102 * Execute a batch of SQL INSERT, UPDATE, or DELETE queries.
103 *
104 * @param conn The Connection to use to run the query. The caller is
105 * responsible for closing this Connection.
106 * @param sql The SQL to execute.
107 * @param params An array of query replacement parameters. Each row in
108 * this array is one set of batch replacement values.
109 * @return The number of rows updated per statement.
110 * @throws SQLException if a database access error occurs
111 * @since DbUtils 1.1
112 */
113 public int[] batch(Connection conn, String sql, Object[][] params)
114 throws SQLException {
115
116 PreparedStatement stmt = null;
117 int[] rows = null;
118 try {
119 stmt = this.prepareStatement(conn, sql);
120
121 for (int i = 0; i < params.length; i++) {
122 this.fillStatement(stmt, params[i]);
123 stmt.addBatch();
124 }
125 rows = stmt.executeBatch();
126
127 } catch (SQLException e) {
128 this.rethrow(e, sql, (Object[])params);
129 } finally {
130 close(stmt);
131 }
132
133 return rows;
134 }
135
136 /**
137 * Execute a batch of SQL INSERT, UPDATE, or DELETE queries. The
138 * <code>Connection</code> is retrieved from the <code>DataSource</code>
139 * set in the constructor. This <code>Connection</code> must be in
140 * auto-commit mode or the update will not be saved.
141 *
142 * @param sql The SQL to execute.
143 * @param params An array of query replacement parameters. Each row in
144 * this array is one set of batch replacement values.
145 * @return The number of rows updated per statement.
146 * @throws SQLException if a database access error occurs
147 * @since DbUtils 1.1
148 */
149 public int[] batch(String sql, Object[][] params) throws SQLException {
150 Connection conn = this.prepareConnection();
151
152 try {
153 return this.batch(conn, sql, params);
154 } finally {
155 close(conn);
156 }
157 }
158
159 /**
160 * Fill the <code>PreparedStatement</code> replacement parameters with
161 * the given objects.
162 * @param stmt PreparedStatement to fill
163 * @param params Query replacement parameters; <code>null</code> is a valid
164 * value to pass in.
165 * @throws SQLException if a database access error occurs
166 */
167 public void fillStatement(PreparedStatement stmt, Object... params)
168 throws SQLException {
169
170 if (params == null) {
171 return;
172 }
173
174 ParameterMetaData pmd = null;
175 if (!pmdKnownBroken) {
176 pmd = stmt.getParameterMetaData();
177 if (pmd.getParameterCount() < params.length) {
178 throw new SQLException("Too many parameters: expected "
179 + pmd.getParameterCount() + ", was given " + params.length);
180 }
181 }
182 for (int i = 0; i < params.length; i++) {
183 if (params[i] != null) {
184 stmt.setObject(i + 1, params[i]);
185 } else {
186 // VARCHAR works with many drivers regardless
187 // of the actual column type. Oddly, NULL and
188 // OTHER don't work with Oracle's drivers.
189 int sqlType = Types.VARCHAR;
190 if (!pmdKnownBroken) {
191 try {
192 sqlType = pmd.getParameterType(i + 1);
193 } catch (SQLException e) {
194 pmdKnownBroken = true;
195 }
196 }
197 stmt.setNull(i + 1, sqlType);
198 }
199 }
200 }
201
202 /**
203 * Fill the <code>PreparedStatement</code> replacement parameters with the
204 * given object's bean property values.
205 *
206 * @param stmt
207 * PreparedStatement to fill
208 * @param bean
209 * a JavaBean object
210 * @param properties
211 * an ordered array of properties; this gives the order to insert
212 * values in the statement
213 * @throws SQLException
214 * if a database access error occurs
215 */
216 public void fillStatementWithBean(PreparedStatement stmt, Object bean,
217 PropertyDescriptor[] properties) throws SQLException {
218 Object[] params = new Object[properties.length];
219 for (int i = 0; i < properties.length; i++) {
220 PropertyDescriptor property = properties[i];
221 Object value = null;
222 Method method = property.getReadMethod();
223 if (method == null) {
224 throw new RuntimeException("No read method for bean property "
225 + bean.getClass() + " " + property.getName());
226 }
227 try {
228 value = method.invoke(bean, new Object[0]);
229 } catch (InvocationTargetException e) {
230 throw new RuntimeException("Couldn't invoke method: " + method, e);
231 } catch (IllegalArgumentException e) {
232 throw new RuntimeException("Couldn't invoke method with 0 arguments: " + method, e);
233 } catch (IllegalAccessException e) {
234 throw new RuntimeException("Couldn't invoke method: " + method, e);
235 }
236 params[i] = value;
237 }
238 fillStatement(stmt, params);
239 }
240
241 /**
242 * Fill the <code>PreparedStatement</code> replacement parameters with the
243 * given object's bean property values.
244 *
245 * @param stmt
246 * PreparedStatement to fill
247 * @param bean
248 * a JavaBean object
249 * @param propertyNames
250 * an ordered array of property names (these should match the
251 * getters/setters); this gives the order to insert values in the
252 * statement
253 * @throws SQLException
254 * if a database access error occurs
255 */
256 public void fillStatementWithBean(PreparedStatement stmt, Object bean,
257 String... propertyNames) throws SQLException {
258 PropertyDescriptor[] descriptors;
259 try {
260 descriptors = Introspector.getBeanInfo(bean.getClass())
261 .getPropertyDescriptors();
262 } catch (IntrospectionException e) {
263 throw new RuntimeException("Couldn't introspect bean " + bean.getClass().toString(), e);
264 }
265 PropertyDescriptor[] sorted = new PropertyDescriptor[propertyNames.length];
266 for (int i = 0; i < propertyNames.length; i++) {
267 String propertyName = propertyNames[i];
268 if (propertyName == null) {
269 throw new NullPointerException("propertyName can't be null: " + i);
270 }
271 boolean found = false;
272 for (int j = 0; j < descriptors.length; j++) {
273 PropertyDescriptor descriptor = descriptors[j];
274 if (propertyName.equals(descriptor.getName())) {
275 sorted[i] = descriptor;
276 found = true;
277 break;
278 }
279 }
280 if (!found) {
281 throw new RuntimeException("Couldn't find bean property: "
282 + bean.getClass() + " " + propertyName);
283 }
284 }
285 fillStatementWithBean(stmt, bean, sorted);
286 }
287
288 /**
289 * Returns the <code>DataSource</code> this runner is using.
290 * <code>QueryRunner</code> methods always call this method to get the
291 * <code>DataSource</code> so subclasses can provide specialized
292 * behavior.
293 *
294 * @return DataSource the runner is using
295 */
296 public DataSource getDataSource() {
297 return this.ds;
298 }
299
300 /**
301 * Factory method that creates and initializes a
302 * <code>PreparedStatement</code> object for the given SQL.
303 * <code>QueryRunner</code> methods always call this method to prepare
304 * statements for them. Subclasses can override this method to provide
305 * special PreparedStatement configuration if needed. This implementation
306 * simply calls <code>conn.prepareStatement(sql)</code>.
307 *
308 * @param conn The <code>Connection</code> used to create the
309 * <code>PreparedStatement</code>
310 * @param sql The SQL statement to prepare.
311 * @return An initialized <code>PreparedStatement</code>.
312 * @throws SQLException if a database access error occurs
313 */
314 protected PreparedStatement prepareStatement(Connection conn, String sql)
315 throws SQLException {
316
317 return conn.prepareStatement(sql);
318 }
319
320 /**
321 * Factory method that creates and initializes a
322 * <code>Connection</code> object. <code>QueryRunner</code> methods
323 * always call this method to retrieve connections from its DataSource.
324 * Subclasses can override this method to provide
325 * special <code>Connection</code> configuration if needed. This
326 * implementation simply calls <code>ds.getConnection()</code>.
327 *
328 * @return An initialized <code>Connection</code>.
329 * @throws SQLException if a database access error occurs
330 * @since DbUtils 1.1
331 */
332 protected Connection prepareConnection() throws SQLException {
333 if(this.getDataSource() == null) {
334 throw new SQLException("QueryRunner requires a DataSource to be " +
335 "invoked in this way, or a Connection should be passed in");
336 }
337 return this.getDataSource().getConnection();
338 }
339
340 /**
341 * Execute an SQL SELECT query with a single replacement parameter. The
342 * caller is responsible for closing the connection.
343 * @param <T> The type of object that the handler returns
344 * @param conn The connection to execute the query in.
345 * @param sql The query to execute.
346 * @param param The replacement parameter.
347 * @param rsh The handler that converts the results into an object.
348 * @return The object returned by the handler.
349 * @throws SQLException if a database access error occurs
350 * @deprecated Use {@link #query(Connection, String, ResultSetHandler, Object...)}
351 */
352 public <T> T query(Connection conn, String sql, Object param,
353 ResultSetHandler<T> rsh) throws SQLException {
354
355 return this.query(conn, sql, rsh, new Object[] { param });
356 }
357
358 /**
359 * Execute an SQL SELECT query with replacement parameters. The
360 * caller is responsible for closing the connection.
361 * @param <T> The type of object that the handler returns
362 * @param conn The connection to execute the query in.
363 * @param sql The query to execute.
364 * @param params The replacement parameters.
365 * @param rsh The handler that converts the results into an object.
366 * @return The object returned by the handler.
367 * @throws SQLException if a database access error occurs
368 * @deprecated Use {@link #query(Connection,String,ResultSetHandler,Object...)} instead
369 */
370 public <T> T query(Connection conn, String sql, Object[] params,
371 ResultSetHandler<T> rsh) throws SQLException {
372 return query(conn, sql, rsh, params);
373 }
374 /**
375 * Execute an SQL SELECT query with replacement parameters. The
376 * caller is responsible for closing the connection.
377 * @param <T> The type of object that the handler returns
378 * @param conn The connection to execute the query in.
379 * @param sql The query to execute.
380 * @param rsh The handler that converts the results into an object.
381 * @param params The replacement parameters.
382 * @return The object returned by the handler.
383 * @throws SQLException if a database access error occurs
384 */
385 public <T> T query(Connection conn, String sql, ResultSetHandler<T> rsh,
386 Object... params) throws SQLException {
387
388 PreparedStatement stmt = null;
389 ResultSet rs = null;
390 T result = null;
391
392 try {
393 stmt = this.prepareStatement(conn, sql);
394 this.fillStatement(stmt, params);
395 rs = this.wrap(stmt.executeQuery());
396 result = rsh.handle(rs);
397
398 } catch (SQLException e) {
399 this.rethrow(e, sql, params);
400
401 } finally {
402 try {
403 close(rs);
404 } finally {
405 close(stmt);
406 }
407 }
408
409 return result;
410 }
411
412 /**
413 * Execute an SQL SELECT query without any replacement parameters. The
414 * caller is responsible for closing the connection.
415 * @param <T> The type of object that the handler returns
416 * @param conn The connection to execute the query in.
417 * @param sql The query to execute.
418 * @param rsh The handler that converts the results into an object.
419 * @return The object returned by the handler.
420 * @throws SQLException if a database access error occurs
421 */
422 public <T> T query(Connection conn, String sql, ResultSetHandler<T> rsh)
423 throws SQLException {
424
425 return this.query(conn, sql, rsh, (Object[]) null);
426 }
427
428 /**
429 * Executes the given SELECT SQL with a single replacement parameter.
430 * The <code>Connection</code> is retrieved from the
431 * <code>DataSource</code> set in the constructor.
432 * @param <T> The type of object that the handler returns
433 * @param sql The SQL statement to execute.
434 * @param param The replacement parameter.
435 * @param rsh The handler used to create the result object from
436 * the <code>ResultSet</code>.
437 *
438 * @return An object generated by the handler.
439 * @throws SQLException if a database access error occurs
440 * @deprecated Use {@link #query(String, ResultSetHandler, Object...)}
441 */
442 public <T> T query(String sql, Object param, ResultSetHandler<T> rsh)
443 throws SQLException {
444
445 return this.query(sql, rsh, new Object[] { param });
446 }
447
448 /**
449 * Executes the given SELECT SQL query and returns a result object.
450 * The <code>Connection</code> is retrieved from the
451 * <code>DataSource</code> set in the constructor.
452 * @param <T> The type of object that the handler returns
453 * @param sql The SQL statement to execute.
454 * @param params Initialize the PreparedStatement's IN parameters with
455 * this array.
456 *
457 * @param rsh The handler used to create the result object from
458 * the <code>ResultSet</code>.
459 *
460 * @return An object generated by the handler.
461 * @throws SQLException if a database access error occurs
462 * @deprecated Use {@link #query(String, ResultSetHandler, Object...)}
463 */
464 public <T> T query(String sql, Object[] params, ResultSetHandler<T> rsh)
465 throws SQLException {
466 return query(sql, rsh, params);
467 }
468
469 /**
470 * Executes the given SELECT SQL query and returns a result object.
471 * The <code>Connection</code> is retrieved from the
472 * <code>DataSource</code> set in the constructor.
473 * @param <T> The type of object that the handler returns
474 * @param sql The SQL statement to execute.
475 * @param rsh The handler used to create the result object from
476 * the <code>ResultSet</code>.
477 * @param params Initialize the PreparedStatement's IN parameters with
478 * this array.
479 * @return An object generated by the handler.
480 * @throws SQLException if a database access error occurs
481 */
482 public <T> T query(String sql, ResultSetHandler<T> rsh, Object... params)
483 throws SQLException {
484
485 Connection conn = this.prepareConnection();
486
487 try {
488 return this.query(conn, sql, rsh, params);
489 } finally {
490 close(conn);
491 }
492 }
493
494 /**
495 * Executes the given SELECT SQL without any replacement parameters.
496 * The <code>Connection</code> is retrieved from the
497 * <code>DataSource</code> set in the constructor.
498 * @param <T> The type of object that the handler returns
499 * @param sql The SQL statement to execute.
500 * @param rsh The handler used to create the result object from
501 * the <code>ResultSet</code>.
502 *
503 * @return An object generated by the handler.
504 * @throws SQLException if a database access error occurs
505 */
506 public <T> T query(String sql, ResultSetHandler<T> rsh) throws SQLException {
507 return this.query(sql, rsh, (Object[]) null);
508 }
509
510 /**
511 * Throws a new exception with a more informative error message.
512 *
513 * @param cause The original exception that will be chained to the new
514 * exception when it's rethrown.
515 *
516 * @param sql The query that was executing when the exception happened.
517 *
518 * @param params The query replacement parameters; <code>null</code> is a
519 * valid value to pass in.
520 *
521 * @throws SQLException if a database access error occurs
522 */
523 protected void rethrow(SQLException cause, String sql, Object... params)
524 throws SQLException {
525
526 String causeMessage = cause.getMessage();
527 if (causeMessage == null) {
528 causeMessage = "";
529 }
530 StringBuffer msg = new StringBuffer(causeMessage);
531
532 msg.append(" Query: ");
533 msg.append(sql);
534 msg.append(" Parameters: ");
535
536 if (params == null) {
537 msg.append("[]");
538 } else {
539 msg.append(Arrays.deepToString(params));
540 }
541
542 SQLException e = new SQLException(msg.toString(), cause.getSQLState(),
543 cause.getErrorCode());
544 e.setNextException(cause);
545
546 throw e;
547 }
548
549 /**
550 * Execute an SQL INSERT, UPDATE, or DELETE query without replacement
551 * parameters.
552 *
553 * @param conn The connection to use to run the query.
554 * @param sql The SQL to execute.
555 * @return The number of rows updated.
556 * @throws SQLException if a database access error occurs
557 */
558 public int update(Connection conn, String sql) throws SQLException {
559 return this.update(conn, sql, (Object[]) null);
560 }
561
562 /**
563 * Execute an SQL INSERT, UPDATE, or DELETE query with a single replacement
564 * parameter.
565 *
566 * @param conn The connection to use to run the query.
567 * @param sql The SQL to execute.
568 * @param param The replacement parameter.
569 * @return The number of rows updated.
570 * @throws SQLException if a database access error occurs
571 */
572 public int update(Connection conn, String sql, Object param)
573 throws SQLException {
574
575 return this.update(conn, sql, new Object[] { param });
576 }
577
578 /**
579 * Execute an SQL INSERT, UPDATE, or DELETE query.
580 *
581 * @param conn The connection to use to run the query.
582 * @param sql The SQL to execute.
583 * @param params The query replacement parameters.
584 * @return The number of rows updated.
585 * @throws SQLException if a database access error occurs
586 */
587 public int update(Connection conn, String sql, Object... params)
588 throws SQLException {
589
590 PreparedStatement stmt = null;
591 int rows = 0;
592
593 try {
594 stmt = this.prepareStatement(conn, sql);
595 this.fillStatement(stmt, params);
596 rows = stmt.executeUpdate();
597
598 } catch (SQLException e) {
599 this.rethrow(e, sql, params);
600
601 } finally {
602 close(stmt);
603 }
604
605 return rows;
606 }
607
608 /**
609 * Executes the given INSERT, UPDATE, or DELETE SQL statement without
610 * any replacement parameters. The <code>Connection</code> is retrieved
611 * from the <code>DataSource</code> set in the constructor. This
612 * <code>Connection</code> must be in auto-commit mode or the update will
613 * not be saved.
614 *
615 * @param sql The SQL statement to execute.
616 * @throws SQLException if a database access error occurs
617 * @return The number of rows updated.
618 */
619 public int update(String sql) throws SQLException {
620 return this.update(sql, (Object[]) null);
621 }
622
623 /**
624 * Executes the given INSERT, UPDATE, or DELETE SQL statement with
625 * a single replacement parameter. The <code>Connection</code> is
626 * retrieved from the <code>DataSource</code> set in the constructor.
627 * This <code>Connection</code> must be in auto-commit mode or the
628 * update will not be saved.
629 *
630 * @param sql The SQL statement to execute.
631 * @param param The replacement parameter.
632 * @throws SQLException if a database access error occurs
633 * @return The number of rows updated.
634 */
635 public int update(String sql, Object param) throws SQLException {
636 return this.update(sql, new Object[] { param });
637 }
638
639 /**
640 * Executes the given INSERT, UPDATE, or DELETE SQL statement. The
641 * <code>Connection</code> is retrieved from the <code>DataSource</code>
642 * set in the constructor. This <code>Connection</code> must be in
643 * auto-commit mode or the update will not be saved.
644 *
645 * @param sql The SQL statement to execute.
646 * @param params Initializes the PreparedStatement's IN (i.e. '?')
647 * parameters.
648 * @throws SQLException if a database access error occurs
649 * @return The number of rows updated.
650 */
651 public int update(String sql, Object... params) throws SQLException {
652 Connection conn = this.prepareConnection();
653
654 try {
655 return this.update(conn, sql, params);
656 } finally {
657 close(conn);
658 }
659 }
660
661 /**
662 * Wrap the <code>ResultSet</code> in a decorator before processing it.
663 * This implementation returns the <code>ResultSet</code> it is given
664 * without any decoration.
665 *
666 * <p>
667 * Often, the implementation of this method can be done in an anonymous
668 * inner class like this:
669 * </p>
670 * <pre>
671 * QueryRunner run = new QueryRunner() {
672 * protected ResultSet wrap(ResultSet rs) {
673 * return StringTrimmedResultSet.wrap(rs);
674 * }
675 * };
676 * </pre>
677 *
678 * @param rs The <code>ResultSet</code> to decorate; never
679 * <code>null</code>.
680 * @return The <code>ResultSet</code> wrapped in some decorator.
681 */
682 protected ResultSet wrap(ResultSet rs) {
683 return rs;
684 }
685
686 /**
687 * Close a <code>Connection</code>. This implementation avoids closing if
688 * null and does <strong>not</strong> suppress any exceptions. Subclasses
689 * can override to provide special handling like logging.
690 * @param conn Connection to close
691 * @throws SQLException if a database access error occurs
692 * @since DbUtils 1.1
693 */
694 protected void close(Connection conn) throws SQLException {
695 DbUtils.close(conn);
696 }
697
698 /**
699 * Close a <code>Statement</code>. This implementation avoids closing if
700 * null and does <strong>not</strong> suppress any exceptions. Subclasses
701 * can override to provide special handling like logging.
702 * @param stmt Statement to close
703 * @throws SQLException if a database access error occurs
704 * @since DbUtils 1.1
705 */
706 protected void close(Statement stmt) throws SQLException {
707 DbUtils.close(stmt);
708 }
709
710 /**
711 * Close a <code>ResultSet</code>. This implementation avoids closing if
712 * null and does <strong>not</strong> suppress any exceptions. Subclasses
713 * can override to provide special handling like logging.
714 * @param rs ResultSet to close
715 * @throws SQLException if a database access error occurs
716 * @since DbUtils 1.1
717 */
718 protected void close(ResultSet rs) throws SQLException {
719 DbUtils.close(rs);
720 }
721
722 }