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)
581             throws SQLException {
582         if (conn == null) {
583             throw new SQLException("Null connection");
584         }
585 
586         if (sql == null) {
587             if (closeConn) {
588                 close(conn);
589             }
590             throw new SQLException("Null SQL statement");
591         }
592 
593         if (rsh == null) {
594             if (closeConn) {
595                 close(conn);
596             }
597             throw new SQLException("Null ResultSetHandler");
598         }
599 
600         PreparedStatement stmt = null;
601         T generatedKeys = null;
602 
603         try {
604             stmt = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
605             this.fillStatement(stmt, params);
606             stmt.executeUpdate();
607             ResultSet resultSet = stmt.getGeneratedKeys();
608             generatedKeys = rsh.handle(resultSet);
609         } catch (SQLException e) {
610             this.rethrow(e, sql, params);
611         } finally {
612             close(stmt);
613             if (closeConn) {
614                 close(conn);
615             }
616         }
617 
618         return generatedKeys;
619     }
620 
621     /**
622      * Executes the given batch of INSERT SQL statements. The
623      * <code>Connection</code> is retrieved from the <code>DataSource</code>
624      * set in the constructor.  This <code>Connection</code> must be in
625      * auto-commit mode or the insert will not be saved.
626      * @param <T> The type of object that the handler returns
627      * @param sql The SQL statement to execute.
628      * @param rsh The handler used to create the result object from
629      * the <code>ResultSet</code> of auto-generated keys.
630      * @param params Initializes the PreparedStatement's IN (i.e. '?')
631      * @return The result generated by the handler.
632      * @throws SQLException if a database access error occurs
633      * @since 1.6
634      */
635     public <T> T insertBatch(String sql, ResultSetHandler<T> rsh, Object[][] params) throws SQLException {
636         return insertBatch(this.prepareConnection(), true, sql, rsh, params);
637     }
638 
639     /**
640      * Executes the given batch of INSERT SQL statements.
641      * @param <T> The type of object that the handler returns
642      * @param conn The connection to use to run the query.
643      * @param sql The SQL to execute.
644      * @param rsh The handler used to create the result object from
645      * the <code>ResultSet</code> of auto-generated keys.
646      * @param params The query replacement parameters.
647      * @return The result generated by the handler.
648      * @throws SQLException if a database access error occurs
649      * @since 1.6
650      */
651     public <T> T insertBatch(Connection conn, String sql, ResultSetHandler<T> rsh, Object[][] params) throws SQLException {
652         return insertBatch(conn, false, sql, rsh, params);
653     }
654 
655     /**
656      * Executes the given batch of INSERT SQL statements.
657      * @param conn The connection to use for the query call.
658      * @param closeConn True if the connection should be closed, false otherwise.
659      * @param sql The SQL statement to execute.
660      * @param rsh The handler used to create the result object from
661      * the <code>ResultSet</code> of auto-generated keys.
662      * @param params The query replacement parameters.
663      * @return The result generated by the handler.
664      * @throws SQLException If there are database or parameter errors.
665      * @since 1.6
666      */
667     private <T> T insertBatch(Connection conn, boolean closeConn, String sql, ResultSetHandler<T> rsh, Object[][] params)
668             throws SQLException {
669         if (conn == null) {
670             throw new SQLException("Null connection");
671         }
672 
673         if (sql == null) {
674             if (closeConn) {
675                 close(conn);
676             }
677             throw new SQLException("Null SQL statement");
678         }
679 
680         if (params == null) {
681             if (closeConn) {
682                 close(conn);
683             }
684             throw new SQLException("Null parameters. If parameters aren't need, pass an empty array.");
685         }
686 
687         PreparedStatement stmt = null;
688         T generatedKeys = null;
689         try {
690             stmt = this.prepareStatement(conn, sql, Statement.RETURN_GENERATED_KEYS);
691 
692             for (int i = 0; i < params.length; i++) {
693                 this.fillStatement(stmt, params[i]);
694                 stmt.addBatch();
695             }
696             stmt.executeBatch();
697             ResultSet rs = stmt.getGeneratedKeys();
698             generatedKeys = rsh.handle(rs);
699 
700         } catch (SQLException e) {
701             this.rethrow(e, sql, (Object[])params);
702         } finally {
703             close(stmt);
704             if (closeConn) {
705                 close(conn);
706             }
707         }
708 
709         return generatedKeys;
710     }
711 }