View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.dbutils;
18  
19  import java.sql.Connection;
20  import java.sql.PreparedStatement;
21  import java.sql.ResultSet;
22  import java.sql.SQLException;
23  import java.sql.Statement;
24  
25  import javax.sql.DataSource;
26  
27  /**
28   * Executes SQL queries with pluggable strategies for handling
29   * <code>ResultSet</code>s.  This class is thread safe.
30   *
31   * @see ResultSetHandler
32   */
33  public class QueryRunner extends AbstractQueryRunner {
34  
35      /**
36       * Constructor for QueryRunner.
37       */
38      public QueryRunner() {
39          super();
40      }
41  
42      /**
43       * Constructor for QueryRunner that controls the use of <code>ParameterMetaData</code>.
44       *
45       * @param pmdKnownBroken Some drivers don't support {@link java.sql.ParameterMetaData#getParameterType(int) };
46       * if <code>pmdKnownBroken</code> is set to true, we won't even try it; if false, we'll try it,
47       * and if it breaks, we'll remember not to use it again.
48       */
49      public QueryRunner(boolean pmdKnownBroken) {
50          super(pmdKnownBroken);
51      }
52  
53      /**
54       * Constructor for QueryRunner that takes a <code>DataSource</code> to use.
55       *
56       * Methods that do not take a <code>Connection</code> parameter will retrieve connections from this
57       * <code>DataSource</code>.
58       *
59       * @param ds The <code>DataSource</code> to retrieve connections from.
60       */
61      public QueryRunner(DataSource ds) {
62          super(ds);
63      }
64  
65      /**
66       * Constructor for QueryRunner that takes a <code>DataSource</code> and controls the use of <code>ParameterMetaData</code>.
67       * Methods that do not take a <code>Connection</code> parameter will retrieve connections from this
68       * <code>DataSource</code>.
69       *
70       * @param ds The <code>DataSource</code> to retrieve connections from.
71       * @param pmdKnownBroken Some drivers don't support {@link java.sql.ParameterMetaData#getParameterType(int) };
72       * if <code>pmdKnownBroken</code> is set to true, we won't even try it; if false, we'll try it,
73       * and if it breaks, we'll remember not to use it again.
74       */
75      public QueryRunner(DataSource ds, boolean pmdKnownBroken) {
76          super(ds, pmdKnownBroken);
77      }
78  
79      /**
80       * Execute a batch of SQL INSERT, UPDATE, or DELETE queries.
81       *
82       * @param conn The Connection to use to run the query.  The caller is
83       * responsible for closing this Connection.
84       * @param sql The SQL to execute.
85       * @param params An array of query replacement parameters.  Each row in
86       * this array is one set of batch replacement values.
87       * @return The number of rows updated per statement.
88       * @throws SQLException if a database access error occurs
89       * @since DbUtils 1.1
90       */
91      public int[] batch(Connection conn, String sql, Object[][] params) throws SQLException {
92          return this.batch(conn, false, sql, params);
93      }
94  
95      /**
96       * Execute a batch of SQL INSERT, UPDATE, or DELETE queries.  The
97       * <code>Connection</code> is retrieved from the <code>DataSource</code>
98       * set in the constructor.  This <code>Connection</code> must be in
99       * auto-commit mode or the update will not be saved.
100      *
101      * @param sql The SQL to execute.
102      * @param params An array of query replacement parameters.  Each row in
103      * this array is one set of batch replacement values.
104      * @return The number of rows updated per statement.
105      * @throws SQLException if a database access error occurs
106      * @since DbUtils 1.1
107      */
108     public int[] batch(String sql, Object[][] params) throws SQLException {
109         Connection conn = this.prepareConnection();
110 
111         return this.batch(conn, true, sql, params);
112     }
113 
114     /**
115      * Calls update after checking the parameters to ensure nothing is null.
116      * @param conn The connection to use for the batch call.
117      * @param closeConn True if the connection should be closed, false otherwise.
118      * @param sql The SQL statement to execute.
119      * @param params An array of query replacement parameters.  Each row in
120      * this array is one set of batch replacement values.
121      * @return The number of rows updated in the batch.
122      * @throws SQLException If there are database or parameter errors.
123      */
124     private int[] batch(Connection conn, boolean closeConn, String sql, Object[][] params) throws SQLException {
125         if (conn == null) {
126             throw new SQLException("Null connection");
127         }
128 
129         if (sql == null) {
130             if (closeConn) {
131                 close(conn);
132             }
133             throw new SQLException("Null SQL statement");
134         }
135 
136         if (params == null) {
137             if (closeConn) {
138                 close(conn);
139             }
140             throw new SQLException("Null parameters. If parameters aren't need, pass an empty array.");
141         }
142 
143         PreparedStatement stmt = null;
144         int[] rows = null;
145         try {
146             stmt = this.prepareStatement(conn, sql);
147 
148             for (int i = 0; i < params.length; i++) {
149                 this.fillStatement(stmt, params[i]);
150                 stmt.addBatch();
151             }
152             rows = stmt.executeBatch();
153 
154         } catch (SQLException e) {
155             this.rethrow(e, sql, (Object[])params);
156         } finally {
157             close(stmt);
158             if (closeConn) {
159                 close(conn);
160             }
161         }
162 
163         return rows;
164     }
165 
166     /**
167      * Execute an SQL SELECT query with a single replacement parameter. The
168      * caller is responsible for closing the connection.
169      * @param <T> The type of object that the handler returns
170      * @param conn The connection to execute the query in.
171      * @param sql The query to execute.
172      * @param param The replacement parameter.
173      * @param rsh The handler that converts the results into an object.
174      * @return The object returned by the handler.
175      * @throws SQLException if a database access error occurs
176      * @deprecated Use {@link #query(Connection, String, ResultSetHandler, Object...)}
177      */
178     @Deprecated
179     public <T> T query(Connection conn, String sql, Object param, ResultSetHandler<T> rsh) throws SQLException {
180         return this.<T>query(conn, false, sql, rsh, new Object[]{param});
181     }
182 
183     /**
184      * Execute an SQL SELECT query with replacement parameters.  The
185      * caller is responsible for closing the connection.
186      * @param <T> The type of object that the handler returns
187      * @param conn The connection to execute the query in.
188      * @param sql The query to execute.
189      * @param params The replacement parameters.
190      * @param rsh The handler that converts the results into an object.
191      * @return The object returned by the handler.
192      * @throws SQLException if a database access error occurs
193      * @deprecated Use {@link #query(Connection,String,ResultSetHandler,Object...)} instead
194      */
195     @Deprecated
196     public <T> T query(Connection conn, String sql, Object[] params, ResultSetHandler<T> rsh) throws SQLException {
197         return this.<T>query(conn, false, sql, rsh, params);
198     }
199 
200     /**
201      * Execute an SQL SELECT query with replacement parameters.  The
202      * caller is responsible for closing the connection.
203      * @param <T> The type of object that the handler returns
204      * @param conn The connection to execute the query in.
205      * @param sql The query to execute.
206      * @param rsh The handler that converts the results into an object.
207      * @param params The replacement parameters.
208      * @return The object returned by the handler.
209      * @throws SQLException if a database access error occurs
210      */
211     public <T> T query(Connection conn, String sql, ResultSetHandler<T> rsh, Object... params) throws SQLException {
212         return this.<T>query(conn, false, sql, rsh, params);
213     }
214 
215     /**
216      * Execute an SQL SELECT query without any replacement parameters.  The
217      * caller is responsible for closing the connection.
218      * @param <T> The type of object that the handler returns
219      * @param conn The connection to execute the query in.
220      * @param sql The query to execute.
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      */
225     public <T> T query(Connection conn, String sql, ResultSetHandler<T> rsh) throws SQLException {
226         return this.<T>query(conn, false, sql, rsh, (Object[]) null);
227     }
228 
229     /**
230      * Executes the given SELECT SQL with a single replacement parameter.
231      * The <code>Connection</code> is retrieved from the
232      * <code>DataSource</code> set in the constructor.
233      * @param <T> The type of object that the handler returns
234      * @param sql The SQL statement to execute.
235      * @param param The replacement parameter.
236      * @param rsh The handler used to create the result object from
237      * the <code>ResultSet</code>.
238      *
239      * @return An object generated by the handler.
240      * @throws SQLException if a database access error occurs
241      * @deprecated Use {@link #query(String, ResultSetHandler, Object...)}
242      */
243     @Deprecated
244     public <T> T query(String sql, Object param, ResultSetHandler<T> rsh) throws SQLException {
245         Connection conn = this.prepareConnection();
246 
247         return this.<T>query(conn, true, sql, rsh, new Object[]{param});
248     }
249 
250     /**
251      * Executes the given SELECT SQL query and returns a result object.
252      * The <code>Connection</code> is retrieved from the
253      * <code>DataSource</code> set in the constructor.
254      * @param <T> The type of object that the handler returns
255      * @param sql The SQL statement to execute.
256      * @param params Initialize the PreparedStatement's IN parameters with
257      * this array.
258      *
259      * @param rsh The handler used to create the result object from
260      * the <code>ResultSet</code>.
261      *
262      * @return An object generated by the handler.
263      * @throws SQLException if a database access error occurs
264      * @deprecated Use {@link #query(String, ResultSetHandler, Object...)}
265      */
266     @Deprecated
267     public <T> T query(String sql, Object[] params, ResultSetHandler<T> rsh) throws SQLException {
268         Connection conn = this.prepareConnection();
269 
270         return this.<T>query(conn, true, sql, rsh, params);
271     }
272 
273     /**
274      * Executes the given SELECT SQL query and returns a result object.
275      * The <code>Connection</code> is retrieved from the
276      * <code>DataSource</code> set in the constructor.
277      * @param <T> The type of object that the handler returns
278      * @param sql The SQL statement to execute.
279      * @param rsh The handler used to create the result object from
280      * the <code>ResultSet</code>.
281      * @param params Initialize the PreparedStatement's IN parameters with
282      * this array.
283      * @return An object generated by the handler.
284      * @throws SQLException if a database access error occurs
285      */
286     public <T> T query(String sql, ResultSetHandler<T> rsh, Object... params) throws SQLException {
287         Connection conn = this.prepareConnection();
288 
289         return this.<T>query(conn, true, sql, rsh, params);
290     }
291 
292     /**
293      * Executes the given SELECT SQL without any replacement parameters.
294      * The <code>Connection</code> is retrieved from the
295      * <code>DataSource</code> set in the constructor.
296      * @param <T> The type of object that the handler returns
297      * @param sql The SQL statement to execute.
298      * @param rsh The handler used to create the result object from
299      * the <code>ResultSet</code>.
300      *
301      * @return An object generated by the handler.
302      * @throws SQLException if a database access error occurs
303      */
304     public <T> T query(String sql, ResultSetHandler<T> rsh) throws SQLException {
305         Connection conn = this.prepareConnection();
306 
307         return this.<T>query(conn, true, sql, rsh, (Object[]) null);
308     }
309 
310     /**
311      * Calls query after checking the parameters to ensure nothing is null.
312      * @param conn The connection to use for the query call.
313      * @param closeConn True if the connection should be closed, false otherwise.
314      * @param sql The SQL statement to execute.
315      * @param params An array of query replacement parameters.  Each row in
316      * this array is one set of batch replacement values.
317      * @return The results of the query.
318      * @throws SQLException If there are database or parameter errors.
319      */
320     private <T> T query(Connection conn, boolean closeConn, String sql, ResultSetHandler<T> rsh, Object... params)
321             throws SQLException {
322         if (conn == null) {
323             throw new SQLException("Null connection");
324         }
325 
326         if (sql == null) {
327             if (closeConn) {
328                 close(conn);
329             }
330             throw new SQLException("Null SQL statement");
331         }
332 
333         if (rsh == null) {
334             if (closeConn) {
335                 close(conn);
336             }
337             throw new SQLException("Null ResultSetHandler");
338         }
339 
340         PreparedStatement stmt = null;
341         ResultSet rs = null;
342         T result = null;
343 
344         try {
345             stmt = this.prepareStatement(conn, sql);
346             this.fillStatement(stmt, params);
347             rs = this.wrap(stmt.executeQuery());
348             result = rsh.handle(rs);
349 
350         } catch (SQLException e) {
351             this.rethrow(e, sql, params);
352 
353         } finally {
354             try {
355                 close(rs);
356             } finally {
357                 close(stmt);
358                 if (closeConn) {
359                     close(conn);
360                 }
361             }
362         }
363 
364         return result;
365     }
366 
367     /**
368      * Execute an SQL INSERT, UPDATE, or DELETE query without replacement
369      * parameters.
370      *
371      * @param conn The connection to use to run the query.
372      * @param sql The SQL to execute.
373      * @return The number of rows updated.
374      * @throws SQLException if a database access error occurs
375      */
376     public int update(Connection conn, String sql) throws SQLException {
377         return this.update(conn, false, sql, (Object[]) null);
378     }
379 
380     /**
381      * Execute an SQL INSERT, UPDATE, or DELETE query with a single replacement
382      * parameter.
383      *
384      * @param conn The connection to use to run the query.
385      * @param sql The SQL to execute.
386      * @param param The replacement parameter.
387      * @return The number of rows updated.
388      * @throws SQLException if a database access error occurs
389      */
390     public int update(Connection conn, String sql, Object param) throws SQLException {
391         return this.update(conn, false, sql, new Object[]{param});
392     }
393 
394     /**
395      * Execute an SQL INSERT, UPDATE, or DELETE query.
396      *
397      * @param conn The connection to use to run the query.
398      * @param sql The SQL to execute.
399      * @param params The query replacement parameters.
400      * @return The number of rows updated.
401      * @throws SQLException if a database access error occurs
402      */
403     public int update(Connection conn, String sql, Object... params) throws SQLException {
404         return update(conn, false, sql, params);
405     }
406 
407     /**
408      * Executes the given INSERT, UPDATE, or DELETE SQL statement without
409      * any replacement parameters. The <code>Connection</code> is retrieved
410      * from the <code>DataSource</code> set in the constructor.  This
411      * <code>Connection</code> must be in auto-commit mode or the update will
412      * not be saved.
413      *
414      * @param sql The SQL statement to execute.
415      * @throws SQLException if a database access error occurs
416      * @return The number of rows updated.
417      */
418     public int update(String sql) throws SQLException {
419         Connection conn = this.prepareConnection();
420 
421         return this.update(conn, true, sql, (Object[]) null);
422     }
423 
424     /**
425      * Executes the given INSERT, UPDATE, or DELETE SQL statement with
426      * a single replacement parameter.  The <code>Connection</code> is
427      * retrieved from the <code>DataSource</code> set in the constructor.
428      * This <code>Connection</code> must be in auto-commit mode or the
429      * update will not be saved.
430      *
431      * @param sql The SQL statement to execute.
432      * @param param The replacement parameter.
433      * @throws SQLException if a database access error occurs
434      * @return The number of rows updated.
435      */
436     public int update(String sql, Object param) throws SQLException {
437         Connection conn = this.prepareConnection();
438 
439         return this.update(conn, true, sql, new Object[]{param});
440     }
441 
442     /**
443      * Executes the given INSERT, UPDATE, or DELETE SQL statement.  The
444      * <code>Connection</code> is retrieved from the <code>DataSource</code>
445      * set in the constructor.  This <code>Connection</code> must be in
446      * auto-commit mode or the update will not be saved.
447      *
448      * @param sql The SQL statement to execute.
449      * @param params Initializes the PreparedStatement's IN (i.e. '?')
450      * parameters.
451      * @throws SQLException if a database access error occurs
452      * @return The number of rows updated.
453      */
454     public int update(String sql, Object... params) throws SQLException {
455         Connection conn = this.prepareConnection();
456 
457         return this.update(conn, true, sql, params);
458     }
459 
460     /**
461      * Calls update after checking the parameters to ensure nothing is null.
462      * @param conn The connection to use for the update call.
463      * @param closeConn True if the connection should be closed, false otherwise.
464      * @param sql The SQL statement to execute.
465      * @param params An array of update replacement parameters.  Each row in
466      * this array is one set of update replacement values.
467      * @return The number of rows updated.
468      * @throws SQLException If there are database or parameter errors.
469      */
470     private int update(Connection conn, boolean closeConn, String sql, Object... params) throws SQLException {
471         if (conn == null) {
472             throw new SQLException("Null connection");
473         }
474 
475         if (sql == null) {
476             if (closeConn) {
477                 close(conn);
478             }
479             throw new SQLException("Null SQL statement");
480         }
481 
482         PreparedStatement stmt = null;
483         int rows = 0;
484 
485         try {
486             stmt = this.prepareStatement(conn, sql);
487             this.fillStatement(stmt, params);
488             rows = stmt.executeUpdate();
489 
490         } catch (SQLException e) {
491             this.rethrow(e, sql, params);
492 
493         } finally {
494             close(stmt);
495             if (closeConn) {
496                 close(conn);
497             }
498         }
499 
500         return rows;
501     }
502 
503     /**
504      * Executes the given INSERT SQL without any replacement parameters.
505      * The <code>Connection</code> is retrieved from the
506      * <code>DataSource</code> set in the constructor.
507      * @param <T> The type of object that the handler returns
508      * @param sql The SQL statement to execute.
509      * @param rsh The handler used to create the result object from
510      * the <code>ResultSet</code> of auto-generated keys.
511      * @return An object generated by the handler.
512      * @throws SQLException if a database access error occurs
513      * @since 1.6
514      */
515     public <T> T insert(String sql, ResultSetHandler<T> rsh) throws SQLException {
516         return insert(this.prepareConnection(), true, sql, rsh, (Object[]) null);
517     }
518 
519     /**
520      * Executes the given INSERT SQL statement. The
521      * <code>Connection</code> is retrieved from the <code>DataSource</code>
522      * set in the constructor.  This <code>Connection</code> must be in
523      * auto-commit mode or the insert will not be saved.
524      * @param <T> The type of object that the handler returns
525      * @param sql The SQL statement to execute.
526      * @param rsh The handler used to create the result object from
527      * the <code>ResultSet</code> of auto-generated keys.
528      * @param params Initializes the PreparedStatement's IN (i.e. '?')
529      * @return An object generated by the handler.
530      * @throws SQLException if a database access error occurs
531      * @since 1.6
532      */
533     public <T> T insert(String sql, ResultSetHandler<T> rsh, Object... params) throws SQLException {
534         return insert(this.prepareConnection(), true, sql, rsh, params);
535     }
536 
537     /**
538      * Execute an SQL INSERT query without replacement parameters.
539      * @param <T> The type of object that the handler returns
540      * @param conn The connection to use to run the query.
541      * @param sql The SQL to execute.
542      * @param rsh The handler used to create the result object from
543      * the <code>ResultSet</code> of auto-generated keys.
544      * @return An object generated by the handler.
545      * @throws SQLException if a database access error occurs
546      * @since 1.6
547      */
548     public <T> T insert(Connection conn, String sql, ResultSetHandler<T> rsh) throws SQLException {
549         return insert(conn, false, sql, rsh, (Object[]) null);
550     }
551 
552     /**
553      * Execute an SQL INSERT query.
554      * @param <T> The type of object that the handler returns
555      * @param conn The connection to use to run the query.
556      * @param sql The SQL to execute.
557      * @param rsh The handler used to create the result object from
558      * the <code>ResultSet</code> of auto-generated keys.
559      * @param params The query replacement parameters.
560      * @return An object generated by the handler.
561      * @throws SQLException if a database access error occurs
562      * @since 1.6
563      */
564     public <T> T insert(Connection conn, String sql, ResultSetHandler<T> rsh, Object... params) throws SQLException {
565         return insert(conn, false, sql, rsh, params);
566     }
567 
568     /**
569      * Executes the given INSERT SQL statement.
570      * @param conn The connection to use for the query call.
571      * @param closeConn True if the connection should be closed, false otherwise.
572      * @param sql The SQL statement to execute.
573      * @param rsh The handler used to create the result object from
574      * the <code>ResultSet</code> of auto-generated keys.
575      * @param params The query replacement parameters.
576      * @return An object generated by the handler.
577      * @throws SQLException If there are database or parameter errors.
578      * @since 1.6
579      */
580     private <T> T insert(Connection conn, boolean closeConn, String sql, ResultSetHandler<T> rsh, Object... params) throws SQLException {
581         if (conn == null) {
582             throw new SQLException("Null connection");
583         }
584 
585         if (sql == null) {
586             if (closeConn) {
587                 close(conn);
588             }
589             throw new SQLException("Null SQL statement");
590         }
591 
592         if (rsh == null) {
593             if (closeConn) {
594                 close(conn);
595             }
596             throw new SQLException("Null ResultSetHandler");
597         }
598 
599         PreparedStatement stmt = null;
600         T generatedKeys = null;
601 
602         try {
603             stmt = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
604             this.fillStatement(stmt, params);
605             stmt.executeUpdate();
606             ResultSet resultSet = stmt.getGeneratedKeys();
607             generatedKeys = rsh.handle(resultSet);
608         } catch (SQLException e) {
609             this.rethrow(e, sql, params);
610         } finally {
611             close(stmt);
612             if (closeConn) {
613                 close(conn);
614             }
615         }
616 
617         return generatedKeys;
618     }
619 
620     /**
621      * Executes the given batch of INSERT SQL statements. The
622      * <code>Connection</code> is retrieved from the <code>DataSource</code>
623      * set in the constructor.  This <code>Connection</code> must be in
624      * auto-commit mode or the insert will not be saved.
625      * @param <T> The type of object that the handler returns
626      * @param sql The SQL statement to execute.
627      * @param rsh The handler used to create the result object from
628      * the <code>ResultSet</code> of auto-generated keys.
629      * @param params Initializes the PreparedStatement's IN (i.e. '?')
630      * @return The result generated by the handler.
631      * @throws SQLException if a database access error occurs
632      * @since 1.6
633      */
634     public <T> T insertBatch(String sql, ResultSetHandler<T> rsh, Object[][] params) throws SQLException {
635         return insertBatch(this.prepareConnection(), true, sql, rsh, params);
636     }
637 
638     /**
639      * Executes the given batch of INSERT SQL statements.
640      * @param <T> The type of object that the handler returns
641      * @param conn The connection to use to run the query.
642      * @param sql The SQL to execute.
643      * @param rsh The handler used to create the result object from
644      * the <code>ResultSet</code> of auto-generated keys.
645      * @param params The query replacement parameters.
646      * @return The result generated by the handler.
647      * @throws SQLException if a database access error occurs
648      * @since 1.6
649      */
650     public <T> T insertBatch(Connection conn, String sql, ResultSetHandler<T> rsh, Object[][] params) throws SQLException {
651         return insertBatch(conn, false, sql, rsh, params);
652     }
653 
654     /**
655      * Executes the given batch of INSERT SQL statements.
656      * @param conn The connection to use for the query call.
657      * @param closeConn True if the connection should be closed, false otherwise.
658      * @param sql The SQL statement to execute.
659      * @param rsh The handler used to create the result object from
660      * the <code>ResultSet</code> of auto-generated keys.
661      * @param params The query replacement parameters.
662      * @return The result generated by the handler.
663      * @throws SQLException If there are database or parameter errors.
664      * @since 1.6
665      */
666     private <T> T insertBatch(Connection conn, boolean closeConn, String sql, ResultSetHandler<T> rsh, Object[][] params) throws SQLException {
667         if (conn == null) {
668             throw new SQLException("Null connection");
669         }
670 
671         if (sql == null) {
672             if (closeConn) {
673                 close(conn);
674             }
675             throw new SQLException("Null SQL statement");
676         }
677 
678         if (params == null) {
679             if (closeConn) {
680                 close(conn);
681             }
682             throw new SQLException("Null parameters. If parameters aren't need, pass an empty array.");
683         }
684 
685         PreparedStatement stmt = null;
686         T generatedKeys = null;
687         try {
688             stmt = this.prepareStatement(conn, sql, Statement.RETURN_GENERATED_KEYS);
689 
690             for (int i = 0; i < params.length; i++) {
691                 this.fillStatement(stmt, params[i]);
692                 stmt.addBatch();
693             }
694             stmt.executeBatch();
695             ResultSet rs = stmt.getGeneratedKeys();
696             generatedKeys = rsh.handle(rs);
697 
698         } catch (SQLException e) {
699             this.rethrow(e, sql, (Object[])params);
700         } finally {
701             close(stmt);
702             if (closeConn) {
703                 close(conn);
704             }
705         }
706 
707         return generatedKeys;
708     }
709 }