(3)一起来看下使用mybatis框架的select语句的源码执行流程吧
本文是作者原创,版权归作者所有.若要转载,请注明出处.本文以简单的select语句为例,只贴我觉得比较重要的源码,其他不重要非关键的就不贴了
主流程和insert语句差不多,这里主要讲不同的流程,前面配置解析就不看了
SqlSession sqlSession = sqlSessionFactory.openSession();
//通过动态代理实现接口 ,用动态代理对象去帮我们执行SQL
//这里生成mapper实际类型是org.apache.ibatis.binding.MapperProxy
DemoMapper mapper = sqlSession.getMapper(DemoMapper.class);
//这里用生成的动态代理对象执行
String projId="0124569b738e405fb20b68bfef37f487";
String sectionName="标段";
List<ProjInfo> projInfos = mapper.selectAll(projId, sectionName);
System.out.println(projInfos);
//sqlSession.commit();
sqlSession.close();
点进去,看下方法sqlSession.getMapper(DemoMapper.class),这个调到下面这个方法
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
//这里就是动态代理生成的代理对象
final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
if (mapperProxyFactory == null) {
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
}
try {
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception e) {
throw new BindingException("Error getting mapper instance. Cause: " + e, e);
}
}
我们看下mapperProxyFactory.newInstance(sqlSession)这个方法
public T newInstance(SqlSession sqlSession) {
final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
继续点进去看
protected T newInstance(MapperProxy<T> mapperProxy) {
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
好,很明显了,这里用的jdk的动态代理,关于动态代理,这里就不赘述了,下次有机会在设计模式里专门讲一下.所以这里返回的是代理对象
继续往下看,我们看mapper.selectAll(projId, sectionName)这个方法debug进去,调的是jdk动态代理的invoke方法,里面就是具体的查询逻辑了
//这里method是接口DemoMapper的selectAll方法
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
// 如果目标方法是Object类继承来的,直接调用目标方法
//method.getDeclaringClass()是接口DemoMapper的类对象,这里结果为false,跳过这一步
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
//这里method是接口DemoMapper的selectAll方法,所以结果也为false,跳过这一步
} else if (method.isDefault()) {
if (privateLookupInMethod == null) {
return invokeDefaultMethodJava8(proxy, method, args);
} else {
return invokeDefaultMethodJava9(proxy, method, args);
}
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
//这里将method放入缓存
// 从缓存中获取MapperMethod 对象,如果没有就创建新的并添加
final MapperMethod mapperMethod = cachedMapperMethod(method);
//这是真正的执行方法
return mapperMethod.execute(sqlSession, args);
}
缓存的东西,我们下次再说,我们继续点进去看
//就是这个执行方法
public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
//command.getType()此时是select
switch (command.getType()) {
case INSERT: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.insert(command.getName(), param));
break;
}
case UPDATE: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.update(command.getName(), param));
break;
}
case DELETE: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.delete(command.getName(), param));
break;
}
//进入这个分支
case SELECT:
//这里结果为false,不进这里
if (method.returnsVoid() && method.hasResultHandler()) {
executeWithResultHandler(sqlSession, args);
result = null;
//这里结果为真,进这里
} else if (method.returnsMany()) {
result = executeForMany(sqlSession, args);
//这里结果为false,不进这里
} else if (method.returnsMap()) {
result = executeForMap(sqlSession, args);
//这里结果为false,不进这里
} else if (method.returnsCursor()) {
result = executeForCursor(sqlSession, args);
} else {
//
Object param = method.convertArgsToSqlCommandParam(args);
//处理参数完成后后param是hashmap类型,key有两种,一种是#{}里面的参数名,
// 另一种是(param1, param2, ...),value只有一种,就是我们的实际参数
//这里执行sql得到查询结果result,跟进去看下
result = sqlSession.selectOne(command.getName(), param);
if (method.returnsOptional()
&& (result == null || !method.getReturnType().equals(result.getClass()))) {
result = Optional.ofNullable(result);
}
}
break;
case FLUSH:
result = sqlSession.flushStatements();
break;
default:
throw new BindingException("Unknown execution method for: " + command.getName());
}
if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {
throw new BindingException("Mapper method '" + command.getName()
+ " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
}
return result;
}
继续往下看
private <E> Object executeForMany(SqlSession sqlSession, Object[] args) {
List<E> result;
Object param = method.convertArgsToSqlCommandParam(args);
if (method.hasRowBounds()) {
RowBounds rowBounds = method.extractRowBounds(args);
result = sqlSession.selectList(command.getName(), param, rowBounds);
} else {
//进这里,点进去看
result = sqlSession.selectList(command.getName(), param);
}
// issue #510 Collections & arrays support
if (!method.getReturnType().isAssignableFrom(result.getClass())) {
if (method.getReturnType().isArray()) {
return convertToArray(result);
} else {
return convertToDeclaredCollection(sqlSession.getConfiguration(), result);
}
}
return result;
}
继续
//这个是真正的sql执行方法了,statement是具体的方法名com.lusaisai.dao.DemoMapper.selectOne
//parameter是参数名和真是的参数
@Override
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
try {
//configuration对象根据statement,得到关于sql语句的相关信息
//这里得到的ms包含sql语句
MappedStatement ms = configuration.getMappedStatement(statement);
//这里就是执行sql语句
return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
继续跟
//执行语句这个方法,跟进去看下
@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);
//这个跟进去看戏,应该是真正的jdbc操作了
return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
继续
@Override
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, boundSql);
@SuppressWarnings("unchecked")
List<E> list = (List<E>) tcm.getObject(cache, key);
if (list == null) {
list = delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
tcm.putObject(cache, key, list); // issue #578 and #116
}
return list;
}
}
//前面是缓存处理跟进去
return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
这里继续
@Override
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();
}
// issue #601
deferredLoads.clear();
if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
// issue #482
clearLocalCache();
}
}
return list;
}
继续
private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
List<E> list;
//key值就是待执行的sql
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;
}
继续
@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.query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
跟一下return那行
@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
//这里强转成预编译对象PreparedStatement
PreparedStatement ps = (PreparedStatement) statement;
ps.execute();
return resultSetHandler.handleResultSets(ps);
}
好,我们看到PreparedStatement对象了,继续
public List<Object> handleResultSets(Statement stmt) throws SQLException {
ErrorContext.instance().activity("handling results").object(mappedStatement.getId());
final List<Object> multipleResults = new ArrayList<>();
int resultSetCount = 0;
ResultSetWrapper rsw = getFirstResultSet(stmt);
List<ResultMap> resultMaps = mappedStatement.getResultMaps();
int resultMapCount = resultMaps.size();
validateResultMapsCount(rsw, resultMapCount);
while (rsw != null && resultMapCount > resultSetCount) {
ResultMap resultMap = resultMaps.get(resultSetCount);
handleResultSet(rsw, resultMap, multipleResults, null);
//这里已经执行完sql,生成结果集了
rsw = getNextResultSet(stmt);
cleanUpAfterHandlingResultSet();
resultSetCount++;
}
String[] resultSets = mappedStatement.getResultSets();
if (resultSets != null) {
while (rsw != null && resultSetCount < resultSets.length) {
ResultMapping parentMapping = nextResultMaps.get(resultSets[resultSetCount]);
if (parentMapping != null) {
String nestedResultMapId = parentMapping.getNestedResultMapId();
ResultMap resultMap = configuration.getResultMap(nestedResultMapId);
handleResultSet(rsw, resultMap, null, parentMapping);
}
rsw = getNextResultSet(stmt);
cleanUpAfterHandlingResultSet();
resultSetCount++;
}
}
return collapseSingleResultList(multipleResults);
}
继续跟我注释的那行
private ResultSetWrapper getNextResultSet(Statement stmt) {
// Making this method tolerant of bad JDBC drivers
try {
if (stmt.getConnection().getMetaData().supportsMultipleResultSets()) {
// Crazy Standard JDBC way of determining if there are more results
//这里的stmt已经包含可执行的sql了
if (!(!stmt.getMoreResults() && stmt.getUpdateCount() == -1)) {
ResultSet rs = stmt.getResultSet();
if (rs == null) {
return getNextResultSet(stmt);
} else {
return new ResultSetWrapper(rs, configuration);
}
}
}
} catch (Exception e) {
// Intentionally ignored.
}
return null;
}
最后再贴张网上找的图

好,到这里我们已经看完执行流程,其实封装了很多层,并没有全都要搞懂的必要,这里的重点是用了动态代理,底层肯定是jdbc操作,当然它还加了很多缓存和各种验证的代码.关于缓存的源码,有机会下次再看吧.
(3)一起来看下使用mybatis框架的select语句的源码执行流程吧的更多相关文章
- mybatis源码专题(2)--------一起来看下使用mybatis框架的insert语句的源码执行流程吧
本文是作者原创,版权归作者所有.若要转载,请注明出处.本文以简单的insert语句为例 1.mybatis的底层是jdbc操作,我们先来回顾一下insert语句的执行流程,如下 执行完后,我们看下数据 ...
- 深入Mybatis源码——执行流程
前言 上一篇分析Mybatis是如何加载解析XML文件的,本篇紧接上文,分析Mybatis的剩余两个阶段:代理封装和SQL执行. 正文 代理封装 Mybatis有两种方式调用Mapper接口: pri ...
- Mybatis底层源码执行流程
1.通过类加载器,加载了config.xml文件 2.通过SqlSessionFactoryBuilder.build(resource)这个方法进行了config.xml的解析,解析为Configu ...
- 详解Java的MyBatis框架中SQL语句映射部分的编写
这篇文章主要介绍了Java的MyBatis框架中SQL语句映射部分的编写,文中分为resultMap和增删查改实现两个部分来讲解,需要的朋友可以参考下 1.resultMap SQL 映射XML 文件 ...
- MVC系列——MVC源码学习:打造自己的MVC框架(二:附源码)
前言:上篇介绍了下 MVC5 的核心原理,整篇文章比较偏理论,所以相对比较枯燥.今天就来根据上篇的理论一步一步进行实践,通过自己写的一个简易MVC框架逐步理解,相信通过这一篇的实践,你会对MVC有一个 ...
- 详解Mybatis拦截器(从使用到源码)
详解Mybatis拦截器(从使用到源码) MyBatis提供了一种插件(plugin)的功能,虽然叫做插件,但其实这是拦截器功能. 本文从配置到源码进行分析. 一.拦截器介绍 MyBatis 允许你在 ...
- Ext.NET 4.1 系统框架的搭建(后台) 附源码
Ext.NET 4.1 系统框架的搭建(后台) 附源码 代码运行环境:.net 4.5 VS2013 (代码可直接编译运行) 预览图: 分析图: 上面系统的构建包括三块区域:North.West和C ...
- Java并发包源码学习之AQS框架(四)AbstractQueuedSynchronizer源码分析
经过前面几篇文章的铺垫,今天我们终于要看看AQS的庐山真面目了,建议第一次看AbstractQueuedSynchronizer 类源码的朋友可以先看下我前面几篇文章: <Java并发包源码学习 ...
- 从零开始手写 spring ioc 框架,深入学习 spring 源码
IoC Ioc 是一款 spring ioc 核心功能简化实现版本,便于学习和理解原理. 创作目的 使用 spring 很长时间,对于 spring 使用非常频繁,实际上对于源码一直没有静下心来学习过 ...
随机推荐
- CSS自定义默认样式
html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, ...
- mac office软件的安装与破解
1.mac office 软件的安装及破解 http://10176523.cn/archives/29/ 下载后安装 切记不要登录 然后用这个文件 破解
- js练习- 给你一个对象,求有几层
// 比如这个a中,就有四层.如何算出这四层 const a = { b: 1, c() {}, d: { e: 2, f: { g: 3, h: { i: 4, }, }, j: { k: 5, } ...
- [TimLinux] Python IDE工具
1. 首推IDE工具PyCharm JetBrains公司推出的系列IDE工具中支持Python编译语言的开发工具,基本上可以认为是行业第一IDE工具了,分为社区版和专业版,可以创建纯Python单文 ...
- nbuoj2786 玻璃球
题目:http://www.nbuoj.com/v8.83/Problems/Problem.php?pid=2786 用2个玻璃球找到从一100层的大楼的某一层落下刚好会摔碎,如何制定最优策略? 别 ...
- hdu2199,double二分
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2199 题意:给一个Y,求满足8*x^4 + 7*x^3 + 2*x^2 + 3*x + 6 == Y的 ...
- LightOJ 1344 Aladdin and the Game of Bracelets
It's said that Aladdin had to solve seven mysteries before getting the Magical Lamp which summons a ...
- CoderForces999B- Reversing Encryption
time limit per test 1 second memory limit per test 256 megabytes input standard input output standar ...
- Python学习心得体会总结,不要采坑
前言 本文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理. 作者:樱桃小丸子0093 大家要持续关注哦,不定时更新Python知识 ...
- Python3 网络编程和并发编程总结
目录 网络编程 开发架构 OSI七层模型 socket subprocess 粘包问题 socketserver TCP UDP 并发编程 多道技术 并发和并行 进程 僵尸进程和孤儿进程 守护进程 互 ...