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方法,接下来我们来看 ...
随机推荐
- 七、eclipse添加离线约束,使不联网也能有一些代码的提示,例如dubbo
eclipse添加离线约束,使不联网也能有一些代码的提示,例如dubbo 1.将dubbo.xsd文件放到一个无中文目录下 2.eclipse->windows->preferences- ...
- CSS实现左侧多级菜单栏
首先看要实现的效果, 主要是关心技术实现, 所以没怎么美化 我也是初学html, 所以写的比较啰嗦 1. 使用列表将内容显示出来 <!DOCTYPE html><html>&l ...
- em,px,rem的区别
px:IE无法调整px作为单位的字体大小. 是像素,是相对长度单位,是相对于显示器屏幕分辨率而言的. em:相对于当前对象内文本的字体尺寸.1em=16px: rem:在设置字体大小时,虽然仍然是相对 ...
- ORM常用字段介绍
Django中的ORM Django项目使用MySQL数据库 1. 在Django项目的settings.py文件中,配置数据库连接信息: DATABASES = { "default&qu ...
- 使用jquery load遇到一个问题解决
1.环境 动态加载页面后给页面中的控件动态赋值,赋完更新后一闪值就没了. 2.原因 load()方法是异步加载,页面没有加载完就开始赋值,导致情况出现. 3.解决 加载完成后赋值 $(&qu ...
- python模块:xml.dom.minidom
"""Simple implementation of the Level 1 DOM. Namespaces and other minor Level 2 featu ...
- Shell脚本学习-数组
跟着RUNOOB网站的教程学习的笔记 Shell数组 数组中可以存放多个值,Bash Shell只支持一维数组(不支持多维数组),初始化时不需要定义数组大小(与PHP类似). 与大部分编程语言类似,数 ...
- Papers | 图像/视频增强 + 深度学习
目录 I. ARCNN 1. Motivation 2. Contribution 3. Artifacts Reduction Convolutional Neural Networks (ARCN ...
- 引用数据类型 Scanner和 Random
键盘录入Scanner 获取键盘录入的数据,对获取数据的具体操作进行了封装,只需要调用方法,即可得到键盘录入的数据 A:导包 import java.util.Scanner; ...
- FreeRTOS的内存管理
FreeRTOS提供了几个内存堆管理方案,有复杂的也有简单的.其中最简单的管理策略也能满足很多应用的要求,比如对安全要求高的应用,这些应用根本不允许动态内存分配的. FreeRTOS也允许你自己实现内 ...