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