mybatis源码解析9---执行器Executor解析

Posted jackion5

tags:

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

从前面分析我们知道了sql的具体执行是通过调用SqlSession接口的对应的方法去执行的,而SqlSession最终都是通过调用了自己的Executor对象的query和update去执行的。本文就分析下sql的执行器-----Executor

Executor是mybatis的sql执行器,SqlSession是面向程序的,而Executor则就是面向数据库的,先看下Executor接口的方法有哪些,源码如下:

 1 public interface Executor {
 2 
 3   ResultHandler NO_RESULT_HANDLER = null;
 4 
 5   int update(MappedStatement ms, Object parameter) throws SQLException;
 6 
 7   <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey cacheKey, BoundSql boundSql) throws SQLException;
 8 
 9   <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException;
10 
11   <E> Cursor<E> queryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds) throws SQLException;
12 
13   List<BatchResult> flushStatements() throws SQLException;
14 
15   void commit(boolean required) throws SQLException;
16 
17   void rollback(boolean required) throws SQLException;
18 
19   CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql);
20 
21   boolean isCached(MappedStatement ms, CacheKey key);
22 
23   void clearLocalCache();
24 
25   void deferLoad(MappedStatement ms, MetaObject resultObject, String property, CacheKey key, Class<?> targetType);
26 
27   Transaction getTransaction();
28 
29   void close(boolean forceRollback);
30 
31   boolean isClosed();
32 
33   void setExecutorWrapper(Executor executor);

 

和SqlSession一样定义了各种各样的sql执行的方法,有查询的query方法,有更新的update方法,以及和事务有关的commit方法和rollback方法等,接下来就以query方法为例,看下具体是如何执行的。

Executor接口共有两个实现类,分别是BaseExecutor和CachingExecutor,CachingExecutor是缓存执行器,后面会提到,现在先看下BaseExecutor

技术图片

BaseExecutor的属性有:

 1   protected Transaction transaction;//事务
 2   protected Executor wrapper;//执行器包装者
 3 
 4   protected ConcurrentLinkedQueue<DeferredLoad> deferredLoads;//线程安全队列
 5   protected PerpetualCache localCache;//本地缓存
 6   protected PerpetualCache localOutputParameterCache;
 7   protected Configuration configuration;
 8 
 9   protected int queryStack = 0;//查询次数栈
10   private boolean closed;//是否已关闭(回滚的时候会被关闭)

 

再看下BaseExecutor执行query方法的源码:

 1 public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
 2       ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());
 3       if (closed) {//判断执行器是否已关闭
 4         throw new ExecutorException("Executor was closed.");
 5       }
 6       //如果查询次数栈为0并且MappedStatement可以清除缓存,则清除本地缓存
 7       if (queryStack == 0 && ms.isFlushCacheRequired()) {
 8         clearLocalCache();
 9       }
10       List<E> list;
11       try {
12         queryStack++;//查询次数+1
13         list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;//从缓存中根据缓存key查询是否有缓存
14         if (list != null) {
15           //如果缓存中有数据,则处理缓存
16           handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
17         } else {
18           //如果缓存中没有数据,则从数据库查询数据
19           list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
20         }
21       } finally {
22           //查询次数-1
23         queryStack--;
24       }
25       if (queryStack == 0) {
26         for (DeferredLoad deferredLoad : deferredLoads) {
27           deferredLoad.load();
28         }
29         // issue #601
30         deferredLoads.clear();
31         if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
32           // issue #482
33           clearLocalCache();
34         }
35       }
36       return list;
37     }

 

可以看出BaseExecutor会优先从缓存中查询数据,如果缓存不为空再从数据库数据。在这里有一个queryStack会进行自增自减,它的作用是什么呢?

先看下如果没有缓存的话,BaseExecutor是怎么从数据库查询数据的:

 1 private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
 2     List<E> list;
 3     localCache.putObject(key, EXECUTION_PLACEHOLDER);
 4     try {
 5       list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);//执行doQuery方法
 6     } finally {
 7       localCache.removeObject(key);
 8     }
 9     localCache.putObject(key, list);//将查询结果放入缓存
10     if (ms.getStatementType() == StatementType.CALLABLE) {//如果callable类型查询
11       localOutputParameterCache.putObject(key, parameter);//将参数放入缓存中
12     }
13     return list;
14   }

 

可见该方法是调用了doQuery方法从数据库查询了数据,然后将查询的结果及查询用的参数放入了缓存中,而doQuery方法是BaseExecutor中的抽象方法,具体的实现是由BaseExecutor的子类进行实现

 1  protected abstract int doUpdate(MappedStatement ms, Object parameter)
 2       throws SQLException;
 3 
 4   protected abstract List<BatchResult> doFlushStatements(boolean isRollback)
 5       throws SQLException;
 6 
 7   protected abstract <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql)
 8       throws SQLException;
 9 
10   protected abstract <E> Cursor<E> doQueryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds, BoundSql boundSql)
11       throws SQLException;
12 
13   protected void closeStatement(Statement statement) {
14     if (statement != null) {
15       try {
16         statement.close();
17       } catch (SQLException e) {
18         // ignore
19       }
20     }
21   }

BaseExecutor共有SimpleExecutor、ReuseExecutor、BatchExecutor以及ClosedExecutor四个子类,这个后面再分析,现在我们以及知道了SqlSession是调用了Executor的方法来执行sql,而Executor的默认实现类的BaseExecutor,而BaseExecutor又是调用了其子类的方法。而BaseExecutor则对查询的结果进行了缓存处理以及查询的时候会从缓存中进行查询。

技术图片

 

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

MyBatis——源码解析MyBatis框架底层的执行原理

MyBatis——源码解析MyBatis框架底层的执行原理

MyBatis3源码解析-执行SQL流程

Mybatis源码解析MyBatis执行Sql的流程分析

Mybatis源码解析之执行SQL语句

MyBatis 源码解析:SQL 语句的执行机制