View Javadoc
1   /**
2    *    Copyright 2009-2015 the original author or authors.
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  package org.apache.ibatis.session.defaults;
17  
18  import java.sql.Connection;
19  import java.sql.SQLException;
20  import java.util.Collection;
21  import java.util.HashMap;
22  import java.util.List;
23  import java.util.Map;
24  
25  import org.apache.ibatis.binding.BindingException;
26  import org.apache.ibatis.exceptions.ExceptionFactory;
27  import org.apache.ibatis.exceptions.TooManyResultsException;
28  import org.apache.ibatis.executor.BatchResult;
29  import org.apache.ibatis.executor.ErrorContext;
30  import org.apache.ibatis.executor.Executor;
31  import org.apache.ibatis.executor.result.DefaultMapResultHandler;
32  import org.apache.ibatis.executor.result.DefaultResultContext;
33  import org.apache.ibatis.mapping.MappedStatement;
34  import org.apache.ibatis.session.Configuration;
35  import org.apache.ibatis.session.ResultHandler;
36  import org.apache.ibatis.session.RowBounds;
37  import org.apache.ibatis.session.SqlSession;
38  
39  /**
40   *
41   * The default implementation for {@link SqlSession}.
42   * Note that this class is not Thread-Safe.
43   *
44   * @author Clinton Begin
45   */
46  public class DefaultSqlSession implements SqlSession {
47  
48    private Configuration configuration;
49    private Executor executor;
50  
51    private boolean autoCommit;
52    private boolean dirty;
53  
54    public DefaultSqlSession(Configuration configuration, Executor executor, boolean autoCommit) {
55      this.configuration = configuration;
56      this.executor = executor;
57      this.dirty = false;
58      this.autoCommit = autoCommit;
59    }
60  
61    public DefaultSqlSession(Configuration configuration, Executor executor) {
62      this(configuration, executor, false);
63    }
64  
65    @Override
66    public <T> T selectOne(String statement) {
67      return this.<T>selectOne(statement, null);
68    }
69  
70    @Override
71    public <T> T selectOne(String statement, Object parameter) {
72      // Popular vote was to return null on 0 results and throw exception on too many.
73      List<T> list = this.<T>selectList(statement, parameter);
74      if (list.size() == 1) {
75        return list.get(0);
76      } else if (list.size() > 1) {
77        throw new TooManyResultsException("Expected one result (or null) to be returned by selectOne(), but found: " + list.size());
78      } else {
79        return null;
80      }
81    }
82  
83    @Override
84    public <K, V> Map<K, V> selectMap(String statement, String mapKey) {
85      return this.selectMap(statement, null, mapKey, RowBounds.DEFAULT);
86    }
87  
88    @Override
89    public <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey) {
90      return this.selectMap(statement, parameter, mapKey, RowBounds.DEFAULT);
91    }
92  
93    @Override
94    public <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey, RowBounds rowBounds) {
95      final List<? extends V> list = selectList(statement, parameter, rowBounds);
96      final DefaultMapResultHandler<K, V> mapResultHandler = new DefaultMapResultHandler<K, V>(mapKey,
97          configuration.getObjectFactory(), configuration.getObjectWrapperFactory(), configuration.getReflectorFactory());
98      final DefaultResultContext<V> context = new DefaultResultContext<V>();
99      for (V o : list) {
100       context.nextResultObject(o);
101       mapResultHandler.handleResult(context);
102     }
103     return mapResultHandler.getMappedResults();
104   }
105 
106   @Override
107   public <E> List<E> selectList(String statement) {
108     return this.selectList(statement, null);
109   }
110 
111   @Override
112   public <E> List<E> selectList(String statement, Object parameter) {
113     return this.selectList(statement, parameter, RowBounds.DEFAULT);
114   }
115 
116   @Override
117   public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
118     try {
119       MappedStatement ms = configuration.getMappedStatement(statement);
120       return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
121     } catch (Exception e) {
122       throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);
123     } finally {
124       ErrorContext.instance().reset();
125     }
126   }
127 
128   @Override
129   public void select(String statement, Object parameter, ResultHandler handler) {
130     select(statement, parameter, RowBounds.DEFAULT, handler);
131   }
132 
133   @Override
134   public void select(String statement, ResultHandler handler) {
135     select(statement, null, RowBounds.DEFAULT, handler);
136   }
137 
138   @Override
139   public void select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) {
140     try {
141       MappedStatement ms = configuration.getMappedStatement(statement);
142       executor.query(ms, wrapCollection(parameter), rowBounds, handler);
143     } catch (Exception e) {
144       throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);
145     } finally {
146       ErrorContext.instance().reset();
147     }
148   }
149 
150   @Override
151   public int insert(String statement) {
152     return insert(statement, null);
153   }
154 
155   @Override
156   public int insert(String statement, Object parameter) {
157     return update(statement, parameter);
158   }
159 
160   @Override
161   public int update(String statement) {
162     return update(statement, null);
163   }
164 
165   @Override
166   public int update(String statement, Object parameter) {
167     try {
168       dirty = true;
169       MappedStatement ms = configuration.getMappedStatement(statement);
170       return executor.update(ms, wrapCollection(parameter));
171     } catch (Exception e) {
172       throw ExceptionFactory.wrapException("Error updating database.  Cause: " + e, e);
173     } finally {
174       ErrorContext.instance().reset();
175     }
176   }
177 
178   @Override
179   public int delete(String statement) {
180     return update(statement, null);
181   }
182 
183   @Override
184   public int delete(String statement, Object parameter) {
185     return update(statement, parameter);
186   }
187 
188   @Override
189   public void commit() {
190     commit(false);
191   }
192 
193   @Override
194   public void commit(boolean force) {
195     try {
196       executor.commit(isCommitOrRollbackRequired(force));
197       dirty = false;
198     } catch (Exception e) {
199       throw ExceptionFactory.wrapException("Error committing transaction.  Cause: " + e, e);
200     } finally {
201       ErrorContext.instance().reset();
202     }
203   }
204 
205   @Override
206   public void rollback() {
207     rollback(false);
208   }
209 
210   @Override
211   public void rollback(boolean force) {
212     try {
213       executor.rollback(isCommitOrRollbackRequired(force));
214       dirty = false;
215     } catch (Exception e) {
216       throw ExceptionFactory.wrapException("Error rolling back transaction.  Cause: " + e, e);
217     } finally {
218       ErrorContext.instance().reset();
219     }
220   }
221 
222   @Override
223   public List<BatchResult> flushStatements() {
224     try {
225       return executor.flushStatements();
226     } catch (Exception e) {
227       throw ExceptionFactory.wrapException("Error flushing statements.  Cause: " + e, e);
228     } finally {
229       ErrorContext.instance().reset();
230     }
231   }
232 
233   @Override
234   public void close() {
235     try {
236       executor.close(isCommitOrRollbackRequired(false));
237       dirty = false;
238     } finally {
239       ErrorContext.instance().reset();
240     }
241   }
242 
243   @Override
244   public Configuration getConfiguration() {
245     return configuration;
246   }
247 
248   @Override
249   public <T> T getMapper(Class<T> type) {
250     return configuration.<T>getMapper(type, this);
251   }
252 
253   @Override
254   public Connection getConnection() {
255     try {
256       return executor.getTransaction().getConnection();
257     } catch (SQLException e) {
258       throw ExceptionFactory.wrapException("Error getting a new connection.  Cause: " + e, e);
259     }
260   }
261 
262   @Override
263   public void clearCache() {
264     executor.clearLocalCache();
265   }
266 
267   private boolean isCommitOrRollbackRequired(boolean force) {
268     return (!autoCommit && dirty) || force;
269   }
270 
271   private Object wrapCollection(final Object object) {
272     if (object instanceof Collection) {
273       StrictMap<Object> map = new StrictMap<Object>();
274       map.put("collection", object);
275       if (object instanceof List) {
276         map.put("list", object);
277       }
278       return map;
279     } else if (object != null && object.getClass().isArray()) {
280       StrictMap<Object> map = new StrictMap<Object>();
281       map.put("array", object);
282       return map;
283     }
284     return object;
285   }
286 
287   public static class StrictMap<V> extends HashMap<String, V> {
288 
289     private static final long serialVersionUID = -5741767162221585340L;
290 
291     @Override
292     public V get(Object key) {
293       if (!super.containsKey(key)) {
294         throw new BindingException("Parameter '" + key + "' not found. Available parameters are " + this.keySet());
295       }
296       return super.get(key);
297     }
298 
299   }
300 
301 }