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 }