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.TransactionFailedException;
20  import org.skife.jdbi.v2.exceptions.UnableToCloseResourceException;
21  import org.skife.jdbi.v2.tweak.SQLLog;
22  import org.skife.jdbi.v2.tweak.StatementBuilder;
23  import org.skife.jdbi.v2.tweak.StatementCustomizer;
24  import org.skife.jdbi.v2.tweak.StatementLocator;
25  import org.skife.jdbi.v2.tweak.StatementRewriter;
26  import org.skife.jdbi.v2.tweak.TransactionHandler;
27  
28  import java.sql.Connection;
29  import java.sql.SQLException;
30  import java.util.Collections;
31  import java.util.HashMap;
32  import java.util.List;
33  import java.util.Map;
34  
35  class BasicHandle implements Handle
36  {
37      private final TransactionHandler transactions;
38      private final Connection connection;
39      private StatementRewriter statementRewriter;
40      private StatementLocator statementLocator;
41      private SQLLog log;
42      private TimingCollector timingCollector;
43      private StatementBuilder statementBuilder;
44      private final Map<String, Object> globalStatementAttributes;
45  
46      BasicHandle(TransactionHandler transactions,
47                         StatementLocator statementLocator,
48                         StatementBuilder preparedStatementCache,
49                         StatementRewriter statementRewriter,
50                         Connection connection,
51                         Map<String, Object> globalStatementAttributes,
52                         SQLLog log,
53                         TimingCollector timingCollector) {
54          this.statementBuilder = preparedStatementCache;
55          this.statementRewriter = statementRewriter;
56          this.transactions = transactions;
57          this.connection = connection;
58          this.statementLocator = statementLocator;
59          this.log = log;
60          this.timingCollector = timingCollector;
61          this.globalStatementAttributes = new HashMap<String, Object>();
62          this.globalStatementAttributes.putAll(globalStatementAttributes);
63      }
64  
65      public Query<Map<String, Object>> createQuery(String sql) {
66          return new Query<Map<String, Object>>(new Binding(),
67                                                new DefaultMapper(),
68                                                statementLocator,
69                                                statementRewriter,
70                                                connection,
71                                                statementBuilder,
72                                                sql,
73                                                new StatementContext(globalStatementAttributes),
74                                                log,
75                                                timingCollector,
76                                                Collections.<StatementCustomizer>emptyList());
77      }
78  
79      /**
80       * Get the JDBC Connection this Handle uses
81       *
82       * @return the JDBC Connection this Handle uses
83       */
84      public Connection getConnection() {
85          return this.connection;
86      }
87  
88      public void close() {
89          statementBuilder.close(getConnection());
90          try {
91              connection.close();
92              log.logReleaseHandle(this);
93          }
94          catch (SQLException e) {
95              throw new UnableToCloseResourceException("Unable to close Connection", e);
96          }
97      }
98  
99      public void define(String key, Object value) {
100         this.globalStatementAttributes.put(key, value);
101     }
102 
103     /**
104      * Start a transaction
105      */
106     public Handle begin() {
107         transactions.begin(this);
108         log.logBeginTransaction(this);
109         return this;
110     }
111 
112     /**
113      * Commit a transaction
114      */
115     public Handle commit() {
116         final long start = System.nanoTime();
117         transactions.commit(this);
118         log.logCommitTransaction((System.nanoTime() - start) / 1000000L, this);
119         return this;
120     }
121 
122     /**
123      * Rollback a transaction
124      */
125     public Handle rollback() {
126         final long start = System.nanoTime();
127         transactions.rollback(this);
128         log.logRollbackTransaction((System.nanoTime() - start) / 1000000L, this);
129         return this;
130     }
131 
132     /**
133      * Create a transaction checkpoint (savepoint in JDBC terminology) with the name provided.
134      *
135      * @param name The name of the checkpoint
136      *
137      * @return The same handle
138      */
139     public Handle checkpoint(String name) {
140         transactions.checkpoint(this, name);
141         log.logCheckpointTransaction(this, name);
142         return this;
143     }
144 
145     /**
146      * Release the named checkpoint, making rollback to it not possible.
147      *
148      * @return The same handle
149      */
150     public Handle release(String checkpointName) {
151         transactions.release(this, checkpointName);
152         log.logReleaseCheckpointTransaction(this, checkpointName);
153         return this;
154     }
155 
156     public void setStatementBuilder(StatementBuilder builder) {
157         this.statementBuilder = builder;
158     }
159 
160     public void setSQLLog(SQLLog log) {
161         this.log = log;
162     }
163 
164     public void setTimingCollector(final TimingCollector timingCollector) {
165         if (timingCollector == null) {
166             this.timingCollector = TimingCollector.NOP_TIMING_COLLECTOR;
167         }
168         else {
169             this.timingCollector = timingCollector;
170         }
171     }
172 
173 
174     /**
175      * Rollback a transaction to a named checkpoint
176      *
177      * @param checkpointName the name of the checkpoint, previously declared with {@see Handle#checkpoint}
178      */
179     public Handle rollback(String checkpointName) {
180         final long start = System.nanoTime();
181         transactions.rollback(this, checkpointName);
182         log.logRollbackToCheckpoint((System.nanoTime() - start) / 1000000L, this, checkpointName);
183         return this;
184     }
185 
186     public boolean isInTransaction() {
187         return transactions.isInTransaction(this);
188     }
189 
190     public Update createStatement(String sql) {
191         return new Update(connection,
192                           statementLocator,
193                           statementRewriter,
194                           statementBuilder,
195                           sql,
196                           new StatementContext(globalStatementAttributes),
197                           log,
198                           timingCollector);
199     }
200 
201     public Call createCall(String sql) {
202         return new Call(connection,
203                         statementLocator,
204                         statementRewriter,
205                         statementBuilder,
206                         sql,
207                         new StatementContext(globalStatementAttributes),
208                         log,
209                         timingCollector,
210                         Collections.<StatementCustomizer>emptyList());
211     }
212 
213     public int insert(String sql, Object... args) {
214         return update(sql, args);
215     }
216 
217     public int update(String sql, Object... args) {
218         Update stmt = createStatement(sql);
219         int position = 0;
220         for (Object arg : args) {
221             stmt.bind(position++, arg);
222         }
223         return stmt.execute();
224     }
225 
226     public PreparedBatch prepareBatch(String sql) {
227         return new PreparedBatch(statementLocator,
228                                  statementRewriter,
229                                  connection,
230                                  statementBuilder,
231                                  sql,
232                                  globalStatementAttributes,
233                                  log,
234                                  timingCollector);
235     }
236 
237     public Batch createBatch() {
238         return new Batch(this.statementRewriter,
239                          this.connection,
240                          globalStatementAttributes,
241                          log,
242                          timingCollector);
243     }
244 
245     public <ReturnType> ReturnType inTransaction(TransactionCallback<ReturnType> callback) throws TransactionFailedException {
246         final boolean[] failed = {false};
247         TransactionStatus status = new TransactionStatus()
248         {
249             public void setRollbackOnly() {
250                 failed[0] = true;
251             }
252         };
253         final ReturnType returnValue;
254         try {
255             this.begin();
256             returnValue = callback.inTransaction(this, status);
257             if (!failed[0]) {
258                 this.commit();
259             }
260         }
261         catch (RuntimeException e) {
262             this.rollback();
263             throw e;
264         }
265         catch (Exception e) {
266             this.rollback();
267             throw new TransactionFailedException("Transaction failed do to exception being thrown " +
268                                                  "from within the callback. See cause " +
269                                                  "for the original exception.", e);
270         }
271         if (failed[0]) {
272             this.rollback();
273             throw new TransactionFailedException("Transaction failed due to transaction status being set " +
274                                                  "to rollback only.");
275         }
276         else {
277             return returnValue;
278         }
279     }
280 
281     public List<Map<String, Object>> select(String sql, Object... args) {
282         Query<Map<String, Object>> query = this.createQuery(sql);
283         int position = 0;
284         for (Object arg : args) {
285             query.bind(position++, arg);
286         }
287         return query.list();
288     }
289 
290     public void setStatementLocator(StatementLocator locator) {
291         this.statementLocator = locator;
292     }
293 
294     public void setStatementRewriter(StatementRewriter rewriter) {
295         this.statementRewriter = rewriter;
296     }
297 
298     public Script createScript(String name) {
299         return new Script(this, statementLocator, name, globalStatementAttributes);
300     }
301 
302     public void execute(String sql, Object... args) {
303         this.update(sql, args);
304     }
305 }