mybatis随笔五之Executor
在上一篇文章我们分析到了mapper接口方法的实现实际上是交由代理类来实现的,并最终调用Executor来查询,接下来我们对
executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER)这个方法进行分析。
@Override
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
BoundSql boundSql = ms.getBoundSql(parameterObject);
CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
ms.getBoundSql内部调用RawSqlSource的getBoundSql方法,该方法又调用了StaticSqlSource的getBoundSql方法,并在该方法内部初始化了一个BoundSql对象,如下是BoundSql的参数
private String sql; //需要执行的sql语句
private List<ParameterMapping> parameterMappings; //参数与数据库列的对应关系
private Object parameterObject; //查询传递的参数
private Map<String, Object> additionalParameters;
private MetaObject metaParameters;
createCacheKey是调用的BaseExecutor方法根据mappedStatement的id,rowBounds的offset、limit值、要执行的sql语句、传递的参数、environment的id来创建cacheKey。
在query的时候查看是否有cache,如果有则使用cache结果,否则使用内部delegate的query方法,这里跳转到了BaseExecutor的query方法,该方法内部又使用了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;
}
方法比较简单将查询结果保存在chche中,调用doQuery方法,BaseExecutor的doQuery方法是个抽象方法,因此这里实际使用的是子类SimpleExecutor的doQuery方法
@Override
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();
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
stmt = prepareStatement(handler, ms.getStatementLog());
return handler.<E>query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
我们先看看newStatementHandler这个方法,这个方法主要做了两件事情,实例化了一个RoutingStatementHandler对象,将拦截目标是statementHandler的拦截器构成拦截链。
public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
return statementHandler;
}
RoutingStatementHandler采用的是装饰设计模式,内部delegate委托的是PreparedStatementHandler对象,因此它的构造方法内部去创建了一个PreparedStatementHandler对象
protected BaseStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
this.configuration = mappedStatement.getConfiguration();
this.executor = executor;
this.mappedStatement = mappedStatement;
this.rowBounds = rowBounds; this.typeHandlerRegistry = configuration.getTypeHandlerRegistry();
this.objectFactory = configuration.getObjectFactory(); if (boundSql == null) { // issue #435, get the key before calculating the statement
generateKeys(parameterObject);
boundSql = mappedStatement.getBoundSql(parameterObject);
} this.boundSql = boundSql; this.parameterHandler = configuration.newParameterHandler(mappedStatement, parameterObject, boundSql);
this.resultSetHandler = configuration.newResultSetHandler(executor, mappedStatement, rowBounds, parameterHandler, resultHandler, boundSql);
}
构建的过程中也初始化了parameterHandler、resultSetHandler两个对象,顾名思义一个是用来处理参数的一个是用来处理结果的。
所以在创建StatementHandler的同时其余两个handler也被创建出来了。
接下来使用prepareStatement来构建参数。
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Statement stmt;
Connection connection = getConnection(statementLog);
stmt = handler.prepare(connection);
handler.parameterize(stmt);
return stmt;
}
在这个方法内部,我们最关注的是倒数二、三句。
handler.prepare类似mapper交由statementHandler的代理对象来执行,若没有针对其的拦截方法则还是调用RoutingStatementHandler的prepare方法。
public Statement prepare(Connection connection) throws SQLException {
ErrorContext.instance().sql(boundSql.getSql());
Statement statement = null;
try {
statement = instantiateStatement(connection);
setStatementTimeout(statement);
setFetchSize(statement);
return statement;
} catch (SQLException e) {
closeStatement(statement);
throw e;
} catch (Exception e) {
closeStatement(statement);
throw new ExecutorException("Error preparing statement. Cause: " + e, e);
}
}
这里主要做了几件事:返回preparedStatement对象、设置查询超时时间、设置每次批量返回的结果行数。
handler.parameterize(stmt)方法类似也是交由statementHandler的代理对象来执行,最终也使用RoutingStatementHandler的parameterize方法。
public void parameterize(Statement statement) throws SQLException {
parameterHandler.setParameters((PreparedStatement) statement);
}
在这个方法里主要是根据定义的入参的javaType、jdbcType类型来选择合适的typeHandler来设置参数,因为我们使用的是long类型,因此typeHandler使用的是LongTypeHandler。
这样我们就把preparedStatement所需的参数全部填充了,最终进入handler.<E>query(stmt, resultHandler)方法。
query方法也会先调用拦截链的方法,最后使用RoutingStatementHandler的query方法。
@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
ps.execute();
return resultSetHandler.<E> handleResultSets(ps);
}
mybatis随笔五之Executor的更多相关文章
- MyBatis 系列五 之 关联映射
MyBatis 系列五 之 关联映射 一对多的关联映射 一对多关联查询多表数据 1.1在MyBatis映射文件中做如下配置 <!--一对多单向的连接两表的查询--> <resultM ...
- mybatis 学习五 二级缓存不推荐使用
mybatis 二级缓存不推荐使用 一 mybatis的缓存使用. 大体就是首先根据你的sqlid,参数的信息自己算出一个key值,然后你查询的时候,会先把这个key值去缓存中找看有没有value,如 ...
- myBatis源码之Executor、BaseExecutor和CachingExecutor
接下来是mybatis的执行过程,mybatis提供了一个接口Executor,Executor接口主要提供了update.query方法及事物相关的方法接口 /** * @author Clinto ...
- MyBatis学习笔记(二) Executor
一.概述 当我们打开一个SqlSession的时候,我们就完成了操作数据库的第一步,那MyBatis是如何执行Sql的呢?其实MyBatis的增删改查都是通过Executor执行的,Executor和 ...
- mybatis四大接口之 Executor
[参考文章]:Mybatis-Executor解析 1. Executor的继承结构 2. Executor(顶层接口) 定义了执行器的一些基本操作: public interface Executo ...
- mybatis随笔二之SqlSessionFactory
在上一篇文章我们已经得到了DefaultSqlSessionFactory @Override public SqlSession openSession() { return openSession ...
- Mybatis(五)Spring整合Mybatis之mapper动态代理开发
要操作的数据库: IDEA创建的Java工程,目录结构如下: 一.导包 1.spring的jar包 2.Mybatis的jar包 3.Spring+mybatis的整合包. 4.Mysql的数据库驱动 ...
- android 学习随笔五(界面)
把数据库内容显示到界面 清单文件设置为线性布局(5大布局属于ViewGroup) 在清单文件中可以增加View显示 LinearLayout ll = (LinearLayout) findViewB ...
- mybatis随笔四之MapperProxy
在上一篇文章我们已经得到了mapper的代理对象,接下来我们对demoMapper.getDemo(1)这种语句进行分析.由于返回的mapper是个代理对象,因此会进入invoke方法,接下来我们来看 ...
随机推荐
- nignx知识点总结
https://segmentfault.com/a/1190000013781162
- git创建新的分支
1.本地创建一个新的分支 git branch develop 2.切换到新创建的分支 git checkout develop 3.将新的分支发布到gitlab上 git push origin d ...
- eclipse 如何安装freemaker ftl 插件
借鉴原链接 https://blog.csdn.net/lsygood/article/details/80565933 在线安装的方法是:Help –> Install New Softw ...
- EF ++属性会更新实体
var lastBaby = await _babyRepository.FirstOrDefaultAsync(); ++lastBaby.sort; -- sort原本为1 -- 最终会生成一条语 ...
- 安卓ViewStub用法
安卓ViewStub用法 在开发应用程序的时候,经常会遇到这样的情况,在运行时动态根据条件来决定显示哪个View或某个布局. 那么最通常的想法就是把可能用到的View都写在上面,先把它们的可见性都设为 ...
- zeromq学习记录(七)订阅发布消息封装
之前也有提到 使用订阅发布 pub sub模式必须要显示定义ZMQ_SUBSCRIBE 只有以此模式定义的过滤字节开头的消息才会被订阅者收到 如果想收到所有信息 可定义subscriber.setso ...
- [uboot] (第一章)uboot流程——概述
http://blog.csdn.net/ooonebook/article/details/52939100 [uboot] uboot流程系列: [project X] tiny210(s5pv2 ...
- sqoop加载mysql数据库
./bin/sqoop list-databases --connect jdbc:mysql://node-001:3306/ --username root --password hadoop
- JavaScript日历(es5版本)
近期在知乎上看到这么一个帖子,题主说自己JavaScript都学完了,结果老师留的作业还是不会写,就是写一个日历的插件,结果楼下一堆大牛出现了,百度的阿里的纷纷站出来发表自己的看法,有人认为简单,有人 ...
- UnionFind问题总结
UnionFind就是acm中常用的并查集... 并查集常用操作 另外补充一下STL常用操作 相关问题: 547. Friend Circles 纯裸题噢... class Solution { pu ...