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方法,接下来我们来看 ...
随机推荐
- Percentage&Last zero&Convert from char to float
Percentage g_rate = wg_header-rate. condense g_rate. SHIFT g_rate RIGHT DELETING TRAILING '0'. REPLA ...
- thinkphp 视图(一)
视图 View <?php namespace app\index\controller; class Index{ public function index(){ return view() ...
- adb、monkey常用命令
adb.monkey常用命令 1.查看连接设备 adb devices 2.连接android模拟器 adb connect 127.0.0.1:62001 3.安装软件 adb install 这个 ...
- 3.Redis高级功能
3.Redis高级功能3.1 慢查询分析3.1.1 慢查询的两个配置参数3.1.2 最佳实践3.1.3 单线程架构3.2 Redis Shell3.2.1 redis-cli 详解3.2.2 redi ...
- CentOS 系统 git clone出错
CentOS 操作系统 安装npm git clone 项目时出现类似如下错误: fatal: unable to access 'https://github.com/creationix/nvmg ...
- Linux module 添加到bashrc 和临时ifort编译器 以及python2和3的配置
第一步vim ~/.bashrc按键盘的i然后source /home/export/online1/bjpara/para/modules/scripts/cn-module.sh最后:x! bas ...
- Oracle学习——第一章
Oracle数据库特点:安全性高,数据类型丰富 Oracle是由美国甲骨文公司开发的一款数据库产品 -------------------------------------------------- ...
- django学习install apps注册错了的影响
今天在学习例子的时候 不注意吧settings.py里面的INSTALL APPS 的APP应用名称写错了 应该是blog 写成了myblog 结果导致python manage.py makemi ...
- SignalR 服务器系统配置要求
SignalR 所支持的服务器版本..NET Framework 版本.IIS和其他组件. SignalR操作系统要求 SignalR组件能够运行在下面的服务器和客户端操作系统.需要注意的是使用Web ...
- 10.18 nslookup:域名查询工具
功能说明 nslookup命令是常用的域名解析查询工具. 如果系统没有nslookup命令,则需要安装下面的软件包: yum-y inatall bind-otil9 语法格式 nslookup ...