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