Mybatis源码解析,一步一步从浅入深(七):执行查询
一,前言
我们在文章:Mybatis源码解析,一步一步从浅入深(二):按步骤解析源码的最后一步说到执行查询的关键代码:
result = sqlSession.selectOne(command.getName(), param);
selelectOne方法有两个参数:
第一个参数是:com.zcz.learnmybatis.dao.UserDao.findUserById
第二个参数是:1(Integer类型,就是我们传入的参数id:1,我们是期望通过这个id查询到我们想要的结果)
因为接下来的代码比较复杂,而且容易迷路。那么我们就定下来本片文章的目的:
1,sql语句是什么时候,在哪里执行的?
2,我们传入参数id是怎么参与执行的?
为了更情绪的分析这两个问题的答案,我将分析的过程分为三步:
1,在SqlSession中的执行过程
2,在Excutor中的执行过程
3,在Statement中的执行过程
二,在代码的执行过程中分析问题
1,代码在SqlSession中的执行过程
在文章:Mybatis源码解析,一步一步从浅入深(二):按步骤解析源码中,我们已经知道了,使用的sqlSession是DefaultSqlSession,那显而易见,要首先看一下selectOne的源码了:
DefaultSqlSession implements SqlSession
public <T> T selectOne(String statement, Object parameter) {
// Popular vote was to return null on 0 results and throw exception on too many.
// 执行查询
List<T> list = this.<T>selectList(statement, parameter);
if (list.size() == 1) {
//如果返回的list的大小是1,则返回第一个元素
return list.get(0);
} else if (list.size() > 1) {
//如果大于1,则抛出异常
throw new TooManyResultsException("Expected one result (or null) to be returned by selectOne(), but found: " + list.size());
} else {
// 如果小于1,则返回null
return null;
}
}
到这里可以明白一件事情,原来selectOne是调用selectList执行查询的,只不过是取了返回值的第一个元素。
我们传入的参数id,就是第5行代码的第二个参数,继续分析第5行的源代码:
DefaultSqlSession implements SqlSession
public <E> List<E> selectList(String statement, Object parameter) {
// 调用了另外一个三个参数的重载方法,
return this.selectList(statement, parameter, RowBounds.DEFAULT);
}
继续跟踪:
DefaultSqlSession implements SqlSession
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
try {
//从configuration中取出解析userDao-mapping.xml文件是生成的MappedStatement
MappedStatement ms = configuration.getMappedStatement(statement);
// 这里的executor是CachingExecutor
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();
}
}
看源代码的第5行,还记得在文章:Mybatis源码解析,一步一步从浅入深(五):mapper节点的解析中,mapper文件中的每一个select,insert,update,delete标签,都会解析成一个MappedStatement类的实例对象吗?而且这个实例对象是存放在configuration中的。然后执行第7行代码,这里的executor是CachingExecutor实例对象。到这里SqlSession中的代码流程就结束了,我们进入Executor中的执行过程。
2,代码在在Excutor中的执行过程
看看源代码:
CachingExecutor implements Executor
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
//取出sql语句
BoundSql boundSql = ms.getBoundSql(parameterObject);
CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
同样的,查询参数id也是在这个方法的第二个参数,而且在取出sql语句的方法中,对查询参数进行了检查。继续跟踪这个方法中的第6行代码:
CachingExecutor implements Executor
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
throws SQLException {
//使用缓存
Cache cache = ms.getCache();
if (cache != null) {
flushCacheIfRequired(ms);
if (ms.isUseCache() && resultHandler == null) {
ensureNoOutParams(ms, parameterObject, boundSql);
@SuppressWarnings("unchecked")
List<E> list = (List<E>) tcm.getObject(cache, key);
if (list == null) {
list = delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
tcm.putObject(cache, key, list); // issue #578. Query must be not synchronized to prevent deadlocks
}
return list;
}
} return delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
继续跟踪第20行代码:
BaseExecutor implements Executor
@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 {
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;
}
继续跟踪第16行代码:
BaseExecutor implements Executor
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;
}
继续跟踪第6行代码:
SimpleExecutor extends BaseExecutor
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);
}
}
看到第3行了吗?这里声明了一个Statement,是不是很熟悉?是的,这就是我们在使用原始的JDBC进行查询的时候,用到的,那么我们的问题是不是在这里就有了答案了呢?这里先留一个标记。
代码执行到这里之后呢,Executor部分的流程就结束了,接下来是在Statement中的执行过程。
3,代码在Statement中的执行过程
继续看源码:
//第一段源码
RoutingStatementHandler implements StatementHandler
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
return delegate.<E>query(statement, resultHandler);
}
//第二段源码
PreparedStatementHandler extends BaseStatementHandler
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
ps.execute();
return resultSetHandler.<E> handleResultSets(ps);
}
在源代码的第10行,是不是也很熟悉?同样也是使用了JDBC进行的查询。似乎这一段的源代码很简单,但其实不是的resultSetHandler中也是做了一部分工作的,这里就不详细描述了。代码到这里就结束了,似乎我们没有看到文章开头的两个问题的答案。看来应该就是在上面标记的地方了。
4,问题的答案
关键代码:stmt = prepareStatement(handler, ms.getStatementLog());
prepareStatement源码:
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Statement stmt;
//从事务中获取数据库连接
Connection connection = getConnection(statementLog);
// 获取Statement
stmt = handler.prepare(connection);
// 为Statement设置查询参数
handler.parameterize(stmt);
return stmt;
}
public Statement prepare(Connection connection) throws SQLException {
ErrorContext.instance().sql(boundSql.getSql());
Statement statement = null;
try {
//初始化Statement
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);
}
}
protected Statement instantiateStatement(Connection connection) throws SQLException {
String sql = boundSql.getSql();
if (mappedStatement.getKeyGenerator() instanceof Jdbc3KeyGenerator) {
String[] keyColumnNames = mappedStatement.getKeyColumns();
if (keyColumnNames == null) {
return connection.prepareStatement(sql, PreparedStatement.RETURN_GENERATED_KEYS);
} else {
return connection.prepareStatement(sql, keyColumnNames);
}
} else if (mappedStatement.getResultSetType() != null) {
return connection.prepareStatement(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
} else {
//从数据库连接中获取statement
return connection.prepareStatement(sql);
}
}
看到第14行,哈哈这里就是我们要的答案了。这样一来我们的两个问题,就都有答案了。
因为执行查询的这个过程比较复杂,如果真的要详细的全部解释清楚的话,估计还得10几篇文章要写。鉴于作者能力和时间,就大概的展示一下这个过程吧。
远程不易,转载请声明出处:https://www.cnblogs.com/zhangchengzi/p/9712446.html
Mybatis源码解析,一步一步从浅入深(七):执行查询的更多相关文章
- Mybatis源码解析,一步一步从浅入深(一):创建准备工程
Spring SpringMVC Mybatis(简称ssm)是一个很流行的java web框架,而Mybatis作为ORM 持久层框架,因其灵活简单,深受青睐.而且现在的招聘职位中都要求应试者熟悉M ...
- Mybatis源码解析,一步一步从浅入深(二):按步骤解析源码
在文章:Mybatis源码解析,一步一步从浅入深(一):创建准备工程,中我们为了解析mybatis源码创建了一个mybatis的简单工程(源码已上传github,链接在文章末尾),并实现了一个查询功能 ...
- Mybatis源码解析,一步一步从浅入深(三):实例化xml配置解析器(XMLConfigBuilder)
在上一篇文章:Mybatis源码解析,一步一步从浅入深(二):按步骤解析源码 ,中我们看到 代码:XMLConfigBuilder parser = new XMLConfigBuilder(read ...
- Mybatis源码解析,一步一步从浅入深(四):将configuration.xml的解析到Configuration对象实例
在Mybatis源码解析,一步一步从浅入深(二):按步骤解析源码中我们看到了XMLConfigBuilder(xml配置解析器)的实例化.而且这个实例化过程在文章:Mybatis源码解析,一步一步从浅 ...
- Mybatis源码解析,一步一步从浅入深(五):mapper节点的解析
在上一篇文章Mybatis源码解析,一步一步从浅入深(四):将configuration.xml的解析到Configuration对象实例中我们谈到了properties,settings,envir ...
- Mybatis源码解析,一步一步从浅入深(六):映射代理类的获取
在文章:Mybatis源码解析,一步一步从浅入深(二):按步骤解析源码中我们提到了两个问题: 1,为什么在以前的代码流程中从来没有addMapper,而这里却有getMapper? 2,UserDao ...
- 【MyBatis源码解析】MyBatis一二级缓存
MyBatis缓存 我们知道,频繁的数据库操作是非常耗费性能的(主要是因为对于DB而言,数据是持久化在磁盘中的,因此查询操作需要通过IO,IO操作速度相比内存操作速度慢了好几个量级),尤其是对于一些相 ...
- Mybatis源码解析-DynamicSqlSource和RawSqlSource的区别
XMLLanguageDriver是ibatis的默认解析sql节点帮助类,其中的方法其会调用生成DynamicSqlSource和RawSqlSource这两个帮助类,本文将对此作下简单的简析 应用 ...
- Mybatis源码解析(四) —— SqlSession是如何实现数据库操作的?
Mybatis源码解析(四) -- SqlSession是如何实现数据库操作的? 如果拿一次数据库请求操作做比喻,那么前面3篇文章就是在做请求准备,真正执行操作的是本篇文章要讲述的内容.正如标题一 ...
随机推荐
- jmeter+Fiddler:通过Fiddler抓包生成jmeter脚本
Fiddler是目前最常用的抓包工具之一,它作为客户端和服务器端之间的代理,记录客户端和服务器之间的所有请求(http/https),可以针对特定的请求过滤,分析请求和响应的数据.设置断点.调试.修改 ...
- ajax前台数据到后台
var username = $("#id").val(); var user={"userAccount":username,"userPasswo ...
- CocosBuilder 学习笔记(3) AnimationManager 与 ccbi 文件解析
[CocosBuilder]学习笔记目录 1. 相关的类 先介绍和AnimationManager相关的几个类: CCBSequence 时间线.有成员duration(时间线时间,默认10秒).na ...
- 【Leetcode】【简单】【122. 买卖股票的最佳时机 II】【JavaScript】
题目描述 122. 买卖股票的最佳时机 II 给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格. 设计一个算法来计算你所能获取的最大利润.你可以尽可能地完成更多的交易(多次买卖一支股票) ...
- ssh延迟加载问题的解决方案
1. 什么是延迟加载问题 ? 业务层查询的数据 关闭session 之后...web层获取延迟加载的数据失败. 例如:查询订单没有查询客户,需要显示客户,session已经关闭,无法查询 2. 如何解 ...
- Spring框架介绍及使用(转载)
原文链接 Spring框架—控制反转(IOC) 1 Spring框架概述1.1 什么是SpringSpring是一个开源框架,Spring是于2003 年兴起的一个轻量级的Java 开发框架,由Rod ...
- LR模型常见问题
信息速览 基础知识介绍-广义线性回归 逻辑斯蒂回归模型推导 逻辑斯蒂回归常见问题 补充知识信息点 基础知识: 机器学习对结果的形式分类: 分类算法 回归算法 LR:logistic regressio ...
- Python——最美丽的编程语言
之前总听别人说Python有多厉害,有多好,我是不以为然的.但是当我开始接触Python的时候,我就知道它被誉为最美编程语言不是没有道理的.简短的代码,清晰明了的逻辑,方便快捷的编程工具让它注定会大放 ...
- JS-特效 ~ 02. 屏幕滚动事件、 DTD、scroll、顶部悬浮导航、两侧跟随广告、返回顶部小火煎
ceil 向上取整 floor 向下取整 round 四舍五入 缓动动画 动画原理 = 盒子位置 + 步长 清除定时器 步长越来越小 盒子位置 = 盒子本身位置 + (目标位置-本身位置)/n(最好为 ...
- React-Native组件样式合集
最近在阅读RN的文档,但有一点深感遗憾的是——官方对绝大多数RN组件没有用Gif图或者静态图的方式呈现给大家. 所以我通过百度查询,一个一个的查到了这些RN组件的UI表现图,下面呈现给大家 阅前必 ...