mybatis的执行流程
1.SqlSessionFactoryBuilder与SqlSessionFactory
我们一般在使用mybatis是都会通过new SqlSessionFactoryBuilder.build(...)来获取SqlSessionFactory,那么这条语句发生了什么,我们来看一看源码
(1).通过将配置文件传递给SqlSessionFactoryBuilder调用build()方法来获取SessionSessionFactory.
public SqlSessionFactory build(Reader reader) {
return build(reader, null, null); //我们跳到这个方法去看
}
public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
try {
XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties); //通过这个方法来加载我们mybatis的xml文件,解析成为XMLConfigBuilder对象。
return build(parser.parse()); //我们转到这个方法
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error building SqlSession.", e);
} finally {
ErrorContext.instance().reset();
try {
reader.close();
} catch (IOException e) {
// Intentionally ignore. Prefer previous error.
}
}
}
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config); //通过将Configuration对象最为参数来新建一个DefaultSqlSessionFactory.
}
2.SqlSessionFactory与SqlSession
(1)我们一般通过SqlSqlSessionFactory.oppenSession()来获取一个SqlSession.我们来看看源码都发生了什么。
@Override
public SqlSession openSession() {
return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false); //我们调到这个方法,会以默认的Executor来执行我们的操作。
}
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null;
try {
final Environment environment = configuration.getEnvironment(); //通过Configuration对象去获取我们的配置信息。
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
final Executor executor = configuration.newExecutor(tx, execType); //新建默认的Executor,默认为Simple
return new DefaultSqlSession(configuration, executor, autoCommit); //将Configuration,我们得到的executor作为参数来新建一个DefaultSqlSession.
} catch (Exception e) {
closeTransaction(tx); // may have fetched a connection so lets call close()
throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
(2)我们也可以通过自定义的ExecutorType来创建我们的SqlSessionFactory
public interface SqlSessionFactory {
SqlSession openSession();
SqlSession openSession(boolean autoCommit);
SqlSession openSession(Connection connection);
SqlSession openSession(TransactionIsolationLevel level);
SqlSession openSession(ExecutorType execType);
SqlSession openSession(ExecutorType execType, boolean autoCommit);
SqlSession openSession(ExecutorType execType, TransactionIsolationLevel level);
SqlSession openSession(ExecutorType execType, Connection connection);
Configuration getConfiguration();
}
(3)SqlSession介绍
sqlSession作为顶层的接口,为我们提供的数据库访问的接口方法。
3.Executor
(1)在sqlSession中实际上并没有实际的数据库操作而是交给下层的Executor来进行,Executor这一层主要负责mybatis中缓存的查询。
举个例子:
@Override
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
try {
MappedStatement ms = configuration.getMappedStatement(statement); //获取MapperStatement对象,在mybatis中我们标签<select/>,<update/>,等这些CRUD标签都会被解析为一个个的MappedStatement对象。
return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER); //调用executor的方法来查询
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
(2)我们转到executor(BaseExecutor)的方法中,注意在BaseExecutor中存储的是我们的一级缓存,关于一二级缓存在下一篇中提及,此处只讨论执行过程
@Override
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
BoundSql boundSql = ms.getBoundSql(parameter);
CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql); //查找缓存的key
return query(ms, parameter, 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;
localCache.putObject(key, EXECUTION_PLACEHOLDER); //将当前的查找key放入到缓存中
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;
}
我们转到其中一个子类中的查询数据库方法(SimpleExecutor)
@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); //通过configuration来获取StatementHandler对象
stmt = prepareStatement(handler, ms.getStatementLog());
return handler.<E>query(stmt, resultHandler); //将查询数据库的操作交给StatementHandler去实现
} finally {
closeStatement(stmt);
}
}
4.StatementHandler
statementHandler用来执行原始的Jdbc操作
@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
ps.execute(); //通过preparedStatement来执行原始的数据库操作
return resultSetHandler.<E> handleResultSets(ps); //返回结果并封装为ResultHandler对象
}
这样mybatis的一次执行就完成了。
5.最后我们在使用SqlSession时一般会使用SqlSession.getMapper来获取我们的代理类
@Override
public <T> T getMapper(Class<T> type) {
return configuration.<T>getMapper(type, this); //通过configuration来获取Mapper
}
//configuration类中的方法
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
return mapperRegistry.getMapper(type, sqlSession); //在configuration对象中的mapperRegistry来获取mapper,在mapperRegistry中保存了我们的接口信息。
}
//mapperRegistry类中的方法
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); //通过mapperProxyFactory来获取mapper实例
} catch (Exception e) {
throw new BindingException("Error getting mapper instance. Cause: " + e, e);
}
//MapperProxyFactory类中的方法
protected T newInstance(MapperProxy<T> mapperProxy) {
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy); //通过jdk动态代理来获取代理对象
} public T newInstance(SqlSession sqlSession) {
final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
通过以上来获取我们的Mapper对象。
对于一些细节,可以去查看mybatis的源码进行学习
mybatis的执行流程的更多相关文章
- Mybatis 系列10-结合源码解析mybatis 的执行流程
[Mybatis 系列10-结合源码解析mybatis 执行流程] [Mybatis 系列9-强大的动态sql 语句] [Mybatis 系列8-结合源码解析select.resultMap的用法] ...
- MyBatis详细执行流程
mybatis详细执行流程 一.通过Resource去加载全局配置文件 import org.apache.ibatis.io.Resources; import org.apache.ibatis. ...
- mybatis的执行流程 #{}和${} Mysql自增主键返回 resultMap 一对多 多对一配置
n Mybatis配置 全局配置文件SqlMapConfig.xml,配置了Mybatis的运行环境等信息. Mapper.xml文件即Sql映射文件,文件中配置了操作数据库的Sql语句.此文件需要在 ...
- 深入浅出Mybatis系列(十)---SQL执行流程分析(源码篇)
最近太忙了,一直没时间继续更新博客,今天忙里偷闲继续我的Mybatis学习之旅.在前九篇中,介绍了mybatis的配置以及使用, 那么本篇将走进mybatis的源码,分析mybatis 的执行流程, ...
- 浩哥解析MyBatis源码(一)——执行流程
原创作品,可以转载,但是请标注出处地址: 一.MyBatis简介 MyBatis框架是一种轻量级的ORM框架,当下十分流行,配合Spring+Spring MVC组成SSM框架,能够胜任几乎所有的项目 ...
- MyBatis源码解析(一)——执行流程
原创作品,可以转载,但是请标注出处地址:http://www.cnblogs.com/V1haoge/p/6603926.html 一.MyBatis简介 MyBatis框架是一种轻量级的ORM框架, ...
- 深入浅出Mybatis系列十-SQL执行流程分析(源码篇)
注:本文转载自南轲梦 注:博主 Chloneda:个人博客 | 博客园 | Github | Gitee | 知乎 最近太忙了,一直没时间继续更新博客,今天忙里偷闲继续我的Mybatis学习之旅.在前 ...
- Mybatis执行流程浅析(附深度文章推荐&面试题集锦)
首先推荐一个简单的Mybatis原理视频教程,可以作为入门教程进行学习:点我 (该教程讲解的是如何手写简易版Mybatis) 执行流程的理解 理解Mybatis的简单流程后自己手写一个,可以解决百分之 ...
- Mybatis执行流程学习之手写mybatis雏形
Mybatis是目前开发中最常用的一款基于ORM思想的半自动持久层框架,平时我们都仅仅停留在使用阶段,对mybatis是怎样运行的并不清楚,今天抽空找到一些资料自学了一波,自己写了一个mybatis的 ...
随机推荐
- set 续2
--------siwuxie095 用 set 命令进行字符串处理(这个不应只属于 set 的内容,应该归属于格式内容, 在没有 set 的情况下,格式仍旧适用) ...
- ajax传递数组及后台接收
ajax传递的是{"items":arr},其中arr=[]; 在后台String[] items=req.getParameterValues("items" ...
- [mysql]设置Ubuntu上的MySQL可以远程访问
今天在win10上用django连接安装在Ubuntu上的MySQL上,始终提示错误(can not connect mysql),但是在Ubuntu上访问是没有问题的.于是开始查找原因: 1. 33 ...
- express4.x Request对象获得参数方法小谈【原创】
最近看完慕课网 “node.js 建站攻略”后, 对mongodb 操作有了进一步认识, 为了进一步巩固该数据库知识, 于是使用学到的知识搭建一个最简单的mongoDemo. 搭建完成后已放到Gith ...
- Summary #ToBeContinue......
一.请回望暑假时的第一次作业,你对于软件工程课程的想象1)对比开篇博客你对课程目标和期待,“希望通过实践锻炼,增强计算机专业的能力和就业竞争力”,对比目前的所学所练所得,在哪些方面达到了你的期待和目标 ...
- angularjs 之 $watch
双向绑定是Angular的核心概念之一,它给我们带来了思维方式的转变:不再是DOM驱动,而是以Model为核心,在View中写上声明式标签.然后,Angular就会在后台默默的同步View的变化到Mo ...
- POJ1751 Highways 2017-04-14 15:46 70人阅读 评论(0) 收藏
Highways Time Limit: 1000MS Memory Limit: 10000K Total Submissions: 14819 Accepted: 4278 Speci ...
- 手动安装httpd服务器
首先安装apr(Apache Portable Runtime) apr-util apr-iconv 安装之前需要 前置知识: 自己手动编译安装的软件的安装位置: /usr/local bin, s ...
- .NET Framework 历史版本(2017年)
.NET简介 这个平台相信我们都知道,不过随着技术发展,现在的.NET平台也今非昔比. .NET平台类似Java平台,是微软于2000年推出的Windows操作系统的应用软件开发框架,发展至今形成巨大 ...
- 流与文本文件操作(C#)
1.流的基本概念 流是任何输入输出库的必不可少的组成部分.当你的程序需要从一个外部数据源(比如,files.other PCs或servers等)读或者写数据时,就需要用到流streams. 流是由一 ...