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、利用三大组件进行具体得到结果
- Executor
- Executor
- Executor
- Executor
- Executor
- Executor
- Executor
- Executor
- Executor
- Executor
- 【Java】【Executor】Executor 简介
- Executor框架
- Executor框架
- Executor 生命周期
- Executor框架
- Executor & ExecutorService
- Executor框架
- Executor框架
- RadASM资源链接错误RC2104及找不到resource.h解决方案
- LeetCode--Triangle
- 每天30行代码——随机梯度上升算法
- QT调用dll、外部程序调用QT的dll.md
- 百度校招笔试-度度熊回家
- Executor
- 拓展欧几里德
- python 螺旋, 五角星游戏
- 《Angular之部署刷新404错误解决》
- UVA
- C语言中关于内存的分配
- 二极管的那点事
- 进程的同步
- 树莓派安装wordpress