mybatis源码解析4---SqlSession解析

Posted Lucky帅小武

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了mybatis源码解析4---SqlSession解析相关的知识,希望对你有一定的参考价值。

由之前解析可知,mybatis启动的时候会加载XML配置文件解析生成全局配置对象Configuration对象,SqlSessionFactoryBuilder类会根据Configuration对象创建一个DefaultSqlSessionFactory对象,而DefaultSqlSessionFactory对象实现了SqlSessionFactory中的创建SqlSession的方法,最终新建了一个SqlSession接口的默认实现类DefaultSqlSession,现在先来了解下SqlSession以及它的实现类

SqlSession解析

SqlSession位于mybatis包的org.apache.ibatis.session目录下,字面意思就是sql的会话,用于程序和数据库直接的sql会话,程序执行一次数据库操作就需要创建一个sqlSession,操作结束即关闭sqlSession;

既然是程序和数据库之间的会话,那么sqlSession接口的方法应该是程序和数据库都容易理解的,sqlSession中定义的方法都是关于数据库操作的方法,源码如下:

 

 1 package org.apache.ibatis.session;
 2 
 3 import java.io.Closeable;
 4 import java.sql.Connection;
 5 import java.util.List;
 6 import java.util.Map;
 7 
 8 import org.apache.ibatis.cursor.Cursor;
 9 import org.apache.ibatis.executor.BatchResult;
10 
11 public interface SqlSession extends Closeable {
12 
13     //根据Sql语句查询单条记录
14   <T> T selectOne(String statement);
15   <T> T selectOne(String statement, Object parameter);//根据Sql语句和参数查询单条记录
16 
17   //根据Sql语句查询多条记录
18   <E> List<E> selectList(String statement);
19   <E> List<E> selectList(String statement, Object parameter);//根据Sql语句和参数查询多条记录
20   <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds);//根据Sql语句和参数以及分页参数查询多条记录
21 
22    //selectMap和selectList原理一样,只是将结果集映射成Map对象返回
23   <K, V> Map<K, V> selectMap(String statement, String mapKey);
24   <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey);
25   <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey, RowBounds rowBounds);
26 
27   //返回游标对象
28   <T> Cursor<T> selectCursor(String statement);
29   <T> Cursor<T> selectCursor(String statement, Object parameter);
30   <T> Cursor<T> selectCursor(String statement, Object parameter, RowBounds rowBounds);
31 
32   //查询的结果对象由指定的ResultHandler处理
33   void select(String statement, Object parameter, ResultHandler handler);
34   void select(String statement, ResultHandler handler);
35   void select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler);
36 
37   //执行insert语句
38   int insert(String statement);
39   int insert(String statement, Object parameter);
40 
41   //执行update语句
42   int update(String statement);
43   int update(String statement, Object parameter);
44 
45   //执行delete语句
46   int delete(String statement);
47   int delete(String statement, Object parameter);
48 
49   //提交事务
50   void commit();
51   void commit(boolean force);
52 
53   //事务回滚
54   void rollback();
55   void rollback(boolean force);
56 
57   //将请求刷新到数据库
58   List<BatchResult> flushStatements();
59 
60   //关闭sqlSession
61   @Override
62   void close();
63 
64   //清除缓存  
65   void clearCache();
66   
67   //获取Configuration对象
68   Configuration getConfiguration();
69 
70   //获取Type对象的Mapper对象
71   <T> T getMapper(Class<T> type);
72 
73   //获取sqlSession对象的数据库连接
74   Connection getConnection();
75 }

SqlSession中的方法全是和数据库相关的增删改查以及事务的提交方法。

SqlSession有三个实现类,除了默认的DefaultSqlSession之外,还有SqlSessionManager和SqlSessionTemplate

接下来先看下SqlSession默认的实现类DefaultSqlSession是如何实现的。

 

DefaultSqlSession类解析

DefaultSqlSession有5个属性和2个构造方法如下:

 1   private Configuration configuration;//全局配置
 2   private Executor executor;//执行器
 3 
 4   private boolean autoCommit;//自动提交标识
 5   private boolean dirty;
 6   private List<Cursor<?>> cursorList;//游标列表
 7 
 8   public DefaultSqlSession(Configuration configuration, Executor executor, boolean autoCommit) {
 9     this.configuration = configuration;
10     this.executor = executor;
11     this.dirty = false;
12     this.autoCommit = autoCommit;
13   }
14 
15   public DefaultSqlSession(Configuration configuration, Executor executor) {
16     this(configuration, executor, false);
17   }

下面以select方法为例

 1 @Override
 2   public <T> T selectOne(String statement) {
 3     return this.<T>selectOne(statement, null);
 4   }
 5 
 6   @Override
 7   public <T> T selectOne(String statement, Object parameter) {
 8     // Popular vote was to return null on 0 results and throw exception on too many.
 9     List<T> list = this.<T>selectList(statement, parameter);
10     if (list.size() == 1) {
11       return list.get(0);
12     } else if (list.size() > 1) {
13       throw new TooManyResultsException("Expected one result (or null) to be returned by selectOne(), but found: " + list.size());
14     } else {
15       return null;
16     }
17   }

很明显selectOne的方法最终都是调用了selectList方法,然后取唯一的一条数据返回。那在看看selectList相关的代码

 1 @Override
 2   public <E> List<E> selectList(String statement) {
 3     return this.selectList(statement, null);
 4   }
 5 
 6   @Override
 7   public <E> List<E> selectList(String statement, Object parameter) {
 8     return this.selectList(statement, parameter, RowBounds.DEFAULT);
 9   }
10 
11   @Override
12   public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
13     try {
14       MappedStatement ms = configuration.getMappedStatement(statement);
15       return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
16     } catch (Exception e) {
17       throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);
18     } finally {
19       ErrorContext.instance().reset();
20     }
21   }

共有三个selectList方法,最终都是调用了最后一个selectList方法,这里有三个参数:statement是sql语句;parameter是传入的参数;RowBounds是和分页相关的参数

RowBounds源码如下:

 1 package org.apache.ibatis.session;
 2 
 3 /**
 4  * @author Clinton Begin
 5  */
 6 public class RowBounds {
 7 
 8   public static final int NO_ROW_OFFSET = 0;
 9   public static final int NO_ROW_LIMIT = Integer.MAX_VALUE; //int的最大值
10   public static final RowBounds DEFAULT = new RowBounds();
11 
12   private int offset;
13   private int limit;
14 
15   public RowBounds() {
16     this.offset = NO_ROW_OFFSET;
17     this.limit = NO_ROW_LIMIT;
18   }
19 
20   public RowBounds(int offset, int limit) {
21     this.offset = offset;
22     this.limit = limit;
23   }
24 
25   public int getOffset() {
26     return offset;
27   }
28 
29   public int getLimit() {
30     return limit;
31   }
32 
33 }

RowBounds的两个属性:offSet是指查询数据时从多少位置开始查询,limit是指返回数据的调试,默认是从0位置开始查询到Integer的最大值,相关于默认是不做分页处理;

回到正题,selectList最后执行的方法中执行了两行代码

1 MappedStatement ms = configuration.getMappedStatement(statement);
2 return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);

调用configuration的getMapped的Statement方法获取MappedStatement对象,然后调用执行器executor的query方法获取查询结果。

这是select方法,再来看看其他的insert、update、delete方法

 1 @Override
 2   public int insert(String statement) {
 3     return insert(statement, null);
 4   }
 5 
 6   @Override
 7   public int insert(String statement, Object parameter) {
 8     return update(statement, parameter);
 9   }
10 
11   @Override
12   public int update(String statement) {
13     return update(statement, null);
14   }
15 
16   @Override
17   public int update(String statement, Object parameter) {
18     try {
19       dirty = true;
20       MappedStatement ms = configuration.getMappedStatement(statement);
21       return executor.update(ms, wrapCollection(parameter));
22     } catch (Exception e) {
23       throw ExceptionFactory.wrapException("Error updating database.  Cause: " + e, e);
24     } finally {
25       ErrorContext.instance().reset();
26     }
27   }
28 
29   @Override
30   public int delete(String statement) {
31     return update(statement, null);
32   }
33 
34   @Override
35   public int delete(String statement, Object parameter) {
36     return update(statement, parameter);
37   }

可以看出SqlSession的insert和delete的方法最终都是调用了update方法,而update方法最终也是调用了Executor的update

 

由此可得出结论sqlSession虽然叫程序和数据库之间的SQL会话,但是它并没有具体去执行sql语句,最终的sql语句的执行是由执行器Executor执行的,而SqlSession的作用只是创建了MappedStatement对象以及调用执行器去执行SQL

其他的commit、rollback方法同样最终都是调用的执行器Executor的对应的方法,那么接下来就去了解下执行器Executor是干嘛的,以及SqlSession创建的MappedStatement又是什么?

 

DefaultSqlSession完整源码如下:

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

 

以上是关于mybatis源码解析4---SqlSession解析的主要内容,如果未能解决你的问题,请参考以下文章

Mybatis源码解析Mybatis源码体系结构

Mybatis源码解析MyBatis的二级缓存源码解析

Mybatis 源码:Mybatis配置解析

MyBatis源码解析 - 解析器模块

怒肝一夜 | Mybatis源码深度解析

Mybatis源码解析-mybatis-spring原理解析