Executor

来源:互联网 发布:淘宝代运营收费 编辑:程序博客网 时间:2024/06/06 03:53

前言

之前在用到Mybatis插件的时候就有提到过要好好分析下Mybatis的四大组件,但是由于之前一直在弄公司的消息服务器集群搭建的事情,就耽搁了好一阵子。现在终于有时间了,我们就来好好看看Mybatis的源码。

从一次查询过程说起

上一次我们我们讲述了Mapper接口以及命令模式的具体命令类MapperMethod。

我们接着之前的分析继续往下走这个查询过程。

接下来就该来到DefaultSqlSession类了。

SqlSession.selectList接MapperMethod

  public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {    try {      //1.从confguration(构造DefaultSQLSession使用了建造者模式,configuration对象就是实际具体的建造者)对象中得到MappedStatement对象      MappedStatement ms = configuration.getMappedStatement(statement);      //2.使用执行器来进行查询的工作,现在我们的重心就到了Executor执行器上了      List<E> result = executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);      return result;    } catch (Exception e) {      throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);    } finally {      ErrorContext.instance().reset();    }  }

上述分析,我们得知了,真正的查询操作是通过Executor执行器代劳的。

Executor

在上述代码中,我们可以知道Executor对象是DefaultSqlSession类中的一个属性,那么首先,我们就得知道Executor是怎么初始化的。

我们从得到DefatulSqlSession的

SqlSession session= sessionFactory.openSession();

这行代码为入口分析

 sessionFactory.openSession();  public SqlSession openSession() {    return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);  }private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {    Transaction tx = null;    try {      final Environment environment = configuration.getEnvironment();      final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);      tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);      //1、在这里初始化了executor      final Executor executor = configuration.newExecutor(tx, execType);      //2、并将executor作为DefaultSqlSession构造方法的实参传入,Executor和SqlSession的联系就是在这里生成的。      return new DefaultSqlSession(configuration, executor, autoCommit);    } catch (Exception e) {      closeTransaction(tx); // may have fetched a connection so lets call close()      throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);    } finally {      ErrorContext.instance().reset();    }  }
  • configuration.newExecutor(tx, execType);
  public Executor newExecutor(Transaction transaction, ExecutorType executorType) {    executorType = executorType == null ? defaultExecutorType : executorType;    executorType = executorType == null ? ExecutorType.SIMPLE : executorType;    Executor executor;    if (ExecutorType.BATCH == executorType) {      executor = new BatchExecutor(this, transaction);    } else if (ExecutorType.REUSE == executorType) {      executor = new ReuseExecutor(this, transaction);    } else {      //如果没有显式的设置,则使用SimpleExecutor,一般都是使用这个Executor      executor = new SimpleExecutor(this, transaction);    }    //如果是缓存了的执行器,就新建缓存专用的执行器。    if (cacheEnabled) {      executor = new CachingExecutor(executor);    }    executor = (Executor) interceptorChain.pluginAll(executor);    return executor;  }

该方法通过Mybatis相关配置文件中设置的ExecutorType来决定(如果没有显式的设置,则使用默认的Type)到底生成哪种Executor。

这里对几种Executor进行介绍一下,并说明用使用显式配置时该如何配置

SimpleExecutor – SIMPLE 就是普通的执行器。

ReuseExecutor -执行器会重用预处理语句(prepared statements)

BatchExecutor –它是批量执行器

这些就是mybatis的三种执行器。你可以通过配置文件的settings里面的元素defaultExecutorType,配置它,默认是采用SimpleExecutor

  <setting name="defaultExecutorType" value="SIMPLE"/>

如果你在Spring运用它,那么你可以这么配置它:

<bean id="sqlSessionTemplateBatch" class="org.mybatis.spring.SqlSessionTemplate">       <constructor-arg index="0" ref="sqlSessionFactory" />    <!--更新采用批量的executor -->    <constructor-arg index="1" value="BATCH"/>    </bean>    

继续分析查询过程

  public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {    try {      //1.从confguration(构造DefaultSQLSession使用了建造者模式,configuration对象就是实际具体的建造者)对象中得到MappedStatement对象      MappedStatement ms = configuration.getMappedStatement(statement);      //2.使用执行器来进行查询的工作,现在我们的重心就到了Executor执行器上了      List<E> result = executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);      return result;    } catch (Exception e) {      throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);    } finally {      ErrorContext.instance().reset();    }  }
  • 1.warpCollection是对Method参数的又一次处理,我们看看具体做了些什么
  private Object wrapCollection(final Object object) {    //我们看到如果经过MapperMethod封装的参数的类型是List类型的话    if (object instanceof List) {      StrictMap<Object> map = new StrictMap<Object>();      //将object作为Value存入map中,而且Key为list,StrictMap继承了HashMap,并且该HashMap的Key是String类型的,这一层封装的意义就在于限定该map的Key只能为String类型      //这里更需要引起我们注意的是这里的Key为list,当你第一眼看到这个list的时候会想到什么呢,      //反正我是一下就想到了在使用动态sql的foreach节点时候出现过list,所以,为了能够对Mapper配置文件中的list对应的对象群出来,这里先将list设置好,这就为什么当参数是List类型时候放入Map时的Key设为list了。       map.put("list", object);      return map;    } else if (object != null && object.getClass().isArray()) {      StrictMap<Object> map = new StrictMap<Object>();     //当数组类型时,放入map中,Key为array,理由同上      map.put("array", object);      return map;    }    return object;  }

warpCollection这个方法的作用就是:

将List或者Array类型转化为Map类型的,并且存入Map时一定要给限定相应的Key,至于为什么要转为Map类型的,当我们分析到后面的时候自然就会有答案了,现在不用深究。

public static class StrictMap<V> extends HashMap<String, V> {    private static final long serialVersionUID = -5741767162221585340L;    @Override    public V get(Object key) {      if (!super.containsKey(key)) {        throw new BindingException("Parameter '" + key + "' not found. Available parameters are " + this.keySet());      }      return super.get(key);    }  }}
  • 2.query方法
  public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {    //1.  BoundSql是对sql的封装,是MappedStatement对Mapper配置文件提取后将SQL的相关信息封装后都集中了BoundSql中,具体是怎么集中封装的,下面会分析    BoundSql boundSql = ms.getBoundSql(parameterObject);    CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql); //2    return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);  }

  • 1.MappedStatement的getBoundSql
public BoundSql getBoundSql(Object parameterObject) {  //2 DynamicSqlSource的getBoundSql    BoundSql boundSql = sqlSource.getBoundSql(parameterObject);    List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();  //将得到的paramterMappings放入到BoundSql对象中,这点是非常重要的    if (parameterMappings == null || parameterMappings.size() <= 0) {      boundSql = new BoundSql(configuration, boundSql.getSql(), parameterMap.getParameterMappings(), parameterObject);    }
    • 2.DynamicSqlSource的getBoundSql(因为使用了动态SQL,所以使用的DynamicSqlSource,关于各种情况具体是怎么封装sql的,也就是关于BoundSql对象以及SqlSource接口和相应的一些实现类,会有一个专门的博客介绍,对于Mybatis源码的一些关键类,都应该有单独的博客介绍,但是我们还是先有一个大体的认识)

 public BoundSql getBoundSql(Object parameterObject) {    DynamicContext context = new DynamicContext(configuration, parameterObject);    //3.将Mapper配置文件中的SQL组装起来,在初始化的过程中,该SQL对应的处理节点分成了两个StaticSqlNode,和一个ForEachSqlNode。   rootSqlNode.apply(context);    SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(configuration);    Class<?> parameterType = parameterObject == null ? Object.class : parameterObject.getClass();   //4.得到SqlSource对象,最重要的是parse方法让我们得到paramtermappings,这个对象对我们执行参数化sql时非常重要    SqlSource sqlSource = sqlSourceParser.parse(context.getSql(), parameterType, context.getBindings());    BoundSql boundSql = sqlSource.getBoundSql(parameterObject);    for (Map.Entry<String, Object> entry : context.getBindings().entrySet()) {      boundSql.setAdditionalParameter(entry.getKey(), entry.getValue());    }    return boundSql;  }
    • 3.DynamicContext的创建
  public static final String PARAMETER_OBJECT_KEY = "_parameter";  public static final String DATABASE_ID_KEY = "_databaseId";public DynamicContext(Configuration configuration, Object parameterObject) {    if (parameterObject != null && !(parameterObject instanceof Map)) {      MetaObject metaObject = configuration.newMetaObject(parameterObject);      bindings = new ContextMap(metaObject);    } else {      bindings = new ContextMap(null);    }    bindings.put(PARAMETER_OBJECT_KEY, parameterObject);    bindings.put(DATABASE_ID_KEY, configuration.getDatabaseId());  }

这里没有特别难理解的地方,就是DynamicContext中有个ContextMap类型(同样继承了HashMap)的属性bindings,这个属性是非常重要的,然后将两个元素放入这个Map中,这里需要注意的是我们会将Method封装参数放入Map中,并且对应的Key是_parameter。一看到_parameter,我们肯定就想到了在学习编写Mapper配置文件中使用过这个。

3.(1)MixedSqlNode

  private List<SqlNode> contents;  public MixedSqlNode(List<SqlNode> contents) {    this.contents = contents;  } public boolean apply(DynamicContext context) {    for (SqlNode sqlNode : contents) {      sqlNode.apply(context);    }    return true;  }

这里的contents就对应的两个StaticSqlNode和一个ForEachSqlNode,如何拼接ForEachSqlNode,以及如何提取ps.setParameter()所用到的信息是问题的关键。

public boolean apply(DynamicContext context) {    Map<String, Object> bindings = context.getBindings();    final Iterable<?> iterable = evaluator.evaluateIterable(collectionExpression, bindings);    if (!iterable.iterator().hasNext()) {      return true;    }    boolean first = true;    applyOpen(context);    int i = 0;    for (Object o : iterable) {      DynamicContext oldContext = context;      if (first) {        context = new PrefixedContext(context, "");      } else {        if (separator != null) {          context = new PrefixedContext(context, separator);        } else {          context = new PrefixedContext(context, "");        }      }      int uniqueNumber = context.getUniqueNumber();      if (o instanceof Map.Entry) { // Issue #709         @SuppressWarnings("unchecked")         Map.Entry<Object, Object> mapEntry = (Map.Entry<Object, Object>) o;        applyIndex(context, mapEntry.getKey(), uniqueNumber);        applyItem(context, mapEntry.getValue(), uniqueNumber);      } else {        applyIndex(context, i, uniqueNumber);        applyItem(context, o, uniqueNumber);      }      contents.apply(new FilteredDynamicContext(configuration, context, index, item, uniqueNumber));      if (first) first = !((PrefixedContext) context).isPrefixApplied();      context = oldContext;      i++;    }    applyClose(context);    return true;  }
    • 4.最后我们需要了解下parse方法,注意这个方法的具体实现里面得到了paramterMappings,这是非常重要的。
  public SqlSource parse(String originalSql, Class<?> parameterType, Map<String, Object> additionalParameters) {    ParameterMappingTokenHandler handler = new ParameterMappingTokenHandler(configuration, parameterType, additionalParameters);    //在这个过程中会把 #{} 里的信息赋给paramterMappings    GenericTokenParser parser = new GenericTokenParser("#{", "}", handler);    String sql = parser.parse(originalSql);    return new StaticSqlSource(configuration, sql, handler.getParameterMappings());  }
  • 2.BaseExecutor的query方法
  public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {    BoundSql boundSql = ms.getBoundSql(parameter);    //生成一个CacheKey    CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);    return query(ms, parameter, rowBounds, resultHandler, key, boundSql); }

终于分析到这个方法的最后一行代码了。

BaseExecutor的query方法

  @SuppressWarnings("unchecked")  public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {    ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());    if (closed) throw new ExecutorException("Executor was closed.");    if (queryStack == 0 && ms.isFlushCacheRequired()) {      clearLocalCache();    }    List<E> list;    try {      queryStack++;      //查看是不是已经有查询过了,有的话直接在本地缓存中取出来      list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;      if (list != null) {        //如果是存储过程,需要把输出处理一下,具体处理目前没有细看        handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);      } else {        //如果没有本地缓存或者使用了resultHandler,需要到数据库中去查。        list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);      }    } finally {      queryStack--;    }    if (queryStack == 0) {      for (DeferredLoad deferredLoad : deferredLoads) {        deferredLoad.load();      }      deferredLoads.clear(); // issue #601      if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {        clearLocalCache(); // issue #482      }    }    return list;  }

queryFromDatabase

  private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {    List<E> list;    localCache.putObject(key, EXECUTION_PLACEHOLDER);    try {      list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);    } finally {      localCache.removeObject(key);    }    localCache.putObject(key, list);    if (ms.getStatementType() == StatementType.CALLABLE) {      localOutputParameterCache.putObject(key, parameter);    }    return list;  }

SimpleExecutor

  public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {    Statement stmt = null;    try {      Configuration configuration = ms.getConfiguration();      //在这里初始化了除了Excutor的其他三大组件,注意了,这行代码完成的工作很重要,而Executor对象则在初始化得到DefaultSqlSession对象的时候就已经注入到其中了。      StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);      stmt = prepareStatement(handler, ms.getStatementLog());      return handler.<E>query(stmt, resultHandler);    } finally {      closeStatement(stmt);    }  }

在这个方法里面,我们完成了得到结果的所有流程。

1、我们先得到Configuration对象。

2、利用COnfiguration对象去生成其他三大组件对象,用三大组件对象完成查询的具体操作。

3、利用三大组件进行具体得到结果