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