View Javadoc

1   /*
2    * Copyright 2004-2007 Brian McCallister
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package org.skife.jdbi.v2;
18  
19  import org.skife.jdbi.v2.exceptions.UnableToCreateStatementException;
20  import org.skife.jdbi.v2.exceptions.UnableToExecuteStatementException;
21  import org.skife.jdbi.v2.tweak.SQLLog;
22  import org.skife.jdbi.v2.tweak.StatementRewriter;
23  
24  import java.sql.Connection;
25  import java.sql.SQLException;
26  import java.sql.Statement;
27  import java.util.ArrayList;
28  import java.util.List;
29  import java.util.Map;
30  
31  /**
32   * Represents a group of non-prepared statements to be sent to the RDMBS in one "request"
33   */
34  public class Batch
35  {
36      private List<String> parts = new ArrayList<String>();
37      private final StatementRewriter rewriter;
38      private final Connection connection;
39      private final SQLLog log;
40      private final StatementContext context;
41      private final TimingCollector timingCollector;
42  
43      Batch(StatementRewriter rewriter, Connection connection, Map<String, Object> globalStatementAttributes, SQLLog log, TimingCollector timingCollector)
44      {
45          this.rewriter = rewriter;
46          this.connection = connection;
47          this.log = log;
48          this.timingCollector = timingCollector;
49          this.context = new StatementContext(globalStatementAttributes);
50      }
51  
52      /**
53       * Add a statement to the batch
54       *
55       * @param sql SQL to be added to the batch, possibly a named statement
56       * @return the same Batch statement
57       */
58      public Batch add(String sql)
59      {
60          parts.add(sql);
61          return this;
62      }
63  
64      /**
65       * Specify a value on the statement context for this batch
66       *
67       * @return self
68       */
69      public Batch define(String key, Object value) {
70          this.context.setAttribute(key, value);
71          return this;
72      }
73  
74      /**
75       * Execute all the queued up statements
76       *
77       * @return an array of integers representing the return values from each statement's execution
78       */
79      public int[] execute()
80      {
81          // short circuit empty batch
82          if (parts.size() == 0) return new int[] {};
83  
84          Binding empty = new Binding();
85          Statement stmt = null;
86          try
87          {
88              try
89              {
90                  stmt = connection.createStatement();
91              }
92              catch (SQLException e)
93              {
94                  throw new UnableToCreateStatementException(e, context);
95              }
96  
97              final SQLLog.BatchLogger logger = log.logBatch();
98              try
99              {
100                 for (String part : parts)
101                 {
102                     final String sql= rewriter.rewrite(part, empty, context).getSql();
103                     logger.add(sql);
104                     stmt.addBatch(sql);
105                 }
106             }
107             catch (SQLException e)
108             {
109                 throw new UnableToExecuteStatementException("Unable to configure JDBC statement", e, context);
110             }
111 
112             try
113             {
114                 final long start = System.nanoTime();
115                 final int[] rs = stmt.executeBatch();
116                 final long elapsedTime = (System.nanoTime() - start);
117                 logger.log(elapsedTime / 1000000L);
118                 // Null for statement, because for batches, we don't really have a good way to keep the sql around.
119                 timingCollector.collect(elapsedTime, context);
120                 return rs;
121 
122             }
123             catch (SQLException e)
124             {
125                 throw new UnableToExecuteStatementException(e, context);
126             }
127         }
128         finally
129         {
130             QueryPostMungeCleanup.CLOSE_RESOURCES_QUIETLY.cleanup(null, stmt, null);
131         }
132 
133     }
134 
135 }