mybatis 学习四 源码分析 mybatis如何执行的一条sql
总体三部分,创建sessionfactory,创建session,执行sql获取结果
1,创建sessionfactory
//mybatis配置文件
String resource = "conf.xml";
InputStream is = TestMybatis.class.getClassLoader().getResourceAsStream(resource);
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(is);
1,xml转换成Configuration对象
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
try {
//environment表示要使用哪个db,properties 表示资源信息
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
return build(parser.parse());
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error building SqlSession.", e);
} finally {
ErrorContext.instance().reset();
try {
inputStream.close();
} catch (IOException e) {
// Intentionally ignore. Prefer previous error.
}
}
}
//将xml中的所有节点都解析成对象信息
private void parseConfiguration(XNode root) {
try {
propertiesElement(root.evalNode("properties")); //issue #117 read properties first
typeAliasesElement(root.evalNode("typeAliases"));
pluginElement(root.evalNode("plugins"));
objectFactoryElement(root.evalNode("objectFactory"));
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
settingsElement(root.evalNode("settings"));
environmentsElement(root.evalNode("environments")); // read it after objectFactory and objectWrapperFactory issue #631
databaseIdProviderElement(root.evalNode("databaseIdProvider"));
typeHandlerElement(root.evalNode("typeHandlers"));
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}
private void propertiesElement(XNode context) throws Exception {
if (context != null) {
//获取xml里面设置的
Properties defaults = context.getChildrenAsProperties();
//获取指定的资源文件里的
String resource = context.getStringAttribute("resource");
String url = context.getStringAttribute("url");
if (resource != null && url != null) {
throw new BuilderException("The properties element cannot specify both a URL and a resource based property file reference. Please specify one or the other.");
}
//覆盖掉了xml里配置的
if (resource != null) {
defaults.putAll(Resources.getResourceAsProperties(resource));
} else if (url != null) {
defaults.putAll(Resources.getUrlAsProperties(url));
}
Properties vars = configuration.getVariables();
//覆盖掉了上面的
if (vars != null) {
defaults.putAll(vars);
}
parser.setVariables(defaults);
configuration.setVariables(defaults);
}
}
private void environmentsElement(XNode context) throws Exception {
if (context != null) {
//如果没有指定,那么直接使用默认的
if (environment == null) {
environment = context.getStringAttribute("default");
}
for (XNode child : context.getChildren()) {
String id = child.getStringAttribute("id");
if (isSpecifiedEnvironment(id)) {
//获取事物factory
TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));
//获取datasourcefactory
DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));
DataSource dataSource = dsFactory.getDataSource();
Environment.Builder environmentBuilder = new Environment.Builder(id)
.transactionFactory(txFactory)
.dataSource(dataSource);
configuration.setEnvironment(environmentBuilder.build());
}
}
}
}
private TransactionFactory transactionManagerElement(XNode context) throws Exception {
if (context != null) {
//根据type来决定要实例化哪个factory出来
String type = context.getStringAttribute("type");
Properties props = context.getChildrenAsProperties();
TransactionFactory factory = (TransactionFactory) resolveClass(type).newInstance();
factory.setProperties(props);
return factory;
}
throw new BuilderException("Environment declaration requires a TransactionFactory.");
}
private DataSourceFactory dataSourceElement(XNode context) throws Exception {
if (context != null) {
//根据type来决定实例化哪个factory
String type = context.getStringAttribute("type");
Properties props = context.getChildrenAsProperties();
DataSourceFactory factory = (DataSourceFactory) resolveClass(type).newInstance();
factory.setProperties(props);
return factory;
}
throw new BuilderException("Environment declaration requires a DataSourceFactory.");
}
最重要的是mappers的转换,这里的方法就把所有的sql语句就转换过来了,将来要找的话,也是这里的源泉。
2,将这个转换好的configuration对象组装成一个DefaultSqlSessionFactory,就可以供外部使用了。
//返回sessionfactory
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
2,创建sqlsession
1,首先从confinguration对象中,找到对应的事物factory,这里有两种facroty。
public void commit() throws SQLException {
if (connection != null && !connection.getAutoCommit()) {
if (log.isDebugEnabled()) {
log.debug("Committing JDBC Connection [" + connection + "]");
}
//这里就是直接使用的jdbc的提交
connection.commit();
}
} public void rollback() throws SQLException {
if (connection != null && !connection.getAutoCommit()) {
if (log.isDebugEnabled()) {
log.debug("Rolling back JDBC Connection [" + connection + "]");
}
//这里就是直接使用的jdbc的回滚
connection.rollback(); } }
//提交和回滚什么都不做,让别人来管理
public void commit() throws SQLException {
// Does nothing
} public void rollback() throws SQLException {
// Does nothing
}
2,然后从configuration中获取到对应的datasource,这里就可能有三种了
private Connection doGetConnection(Properties properties) throws SQLException {
//初始化驱动
initializeDriver();
//jdbc直接获取一个连接
Connection connection = DriverManager.getConnection(url, properties);
configureConnection(connection);
return connection;
}
//方法内容过多,可以自己去看 这里是从一个集合中pop出来一个连接直接使用。
private PooledConnection popConnection(String username, String password) throws SQLException {
//从jndi上下文中直接获取数据源并返回
if (properties.containsKey(INITIAL_CONTEXT)
&& properties.containsKey(DATA_SOURCE)) {
Context ctx = (Context) initCtx.lookup(properties.getProperty(INITIAL_CONTEXT));
dataSource = (DataSource) ctx.lookup(properties.getProperty(DATA_SOURCE));
} else if (properties.containsKey(DATA_SOURCE)) {
dataSource = (DataSource) initCtx.lookup(properties.getProperty(DATA_SOURCE));
}
3,将得到的datasource和事物facroty一起用来,获取一个tx。
public Executor newExecutor(Transaction transaction, ExecutorType executorType, boolean autoCommit) {
executorType = executorType == null ? defaultExecutorType : executorType;
executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
Executor executor;
if (ExecutorType.BATCH == executorType) {
executor = new BatchExecutor(this, transaction);
} else if (ExecutorType.REUSE == executorType) {
executor = new ReuseExecutor(this, transaction);
} else {
executor = new SimpleExecutor(this, transaction);
}
//这里查看是否二级缓存开启了,如果开启了,那么返回的是一个被装饰了的执行器 用来使用缓存
if (cacheEnabled) {
executor = new CachingExecutor(executor, autoCommit);
}
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
}
4,得到这个执行器之后,组装成sqlsession然后返回供外部使用。
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null;
try {
//得到db信息
final Environment environment = configuration.getEnvironment();
//得到事物factory
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
//根据数据源得到事物
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
//得到执行器
final Executor executor = configuration.newExecutor(tx, execType, autoCommit);
//执行器和配置信息对象一起组装了session返回。
return new DefaultSqlSession(configuration, executor);
} 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();
}
}
3,执行sql查询。
1,首先会根据sql的id去configuration对象中找到对应的sql,返回的是一个 MappedStatement,这里面有所有这个sql的信息,
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
try {
//根据id找到对应的sql的封装对象 statement
MappedStatement ms = configuration.getMappedStatement(statement);
//这里的执行器有可能是缓存执行器也可能是默认执行器
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();
}
}
2,执行执行器的查询方法,如果二级缓存开启了那么会使用CachingExecutor的query,这里会先去这个MappedStatement里的缓存里找,如果找不到,在去执行sql,
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);
if (!dirty) {
cache.getReadWriteLock().readLock().lock();
try {
//这里从二级缓存中找一下
@SuppressWarnings("unchecked")
List<E> cachedList = (List<E>) cache.getObject(key);
if (cachedList != null) return cachedList;
} finally {
cache.getReadWriteLock().readLock().unlock();
}
}
//执行sql查询
List<E> 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);
}
3,二级缓存里如果找不到
//一级缓存查找
list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
if (list != null) {
handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
} else {
//sqldb查找
list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
mybatis 学习四 源码分析 mybatis如何执行的一条sql的更多相关文章
- 【转】MaBatis学习---源码分析MyBatis缓存原理
[原文]https://www.toutiao.com/i6594029178964673027/ 源码分析MyBatis缓存原理 1.简介 在 Web 应用中,缓存是必不可少的组件.通常我们都会用 ...
- DotNetty网络通信框架学习之源码分析
DotNetty网络通信框架学习之源码分析 有关DotNetty框架,网上的详细资料不是很多,有不多的几个博友做了简单的介绍,也没有做深入的探究,我也根据源码中提供的demo做一下记录,方便后期查阅. ...
- Yii2 源码分析 入口文件执行流程
Yii2 源码分析 入口文件执行流程 1. 入口文件:web/index.php,第12行.(new yii\web\Application($config)->run()) 入口文件主要做4 ...
- 精尽MyBatis源码分析 - MyBatis初始化(四)之 SQL 初始化(下)
该系列文档是本人在学习 Mybatis 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释(Mybatis源码分析 GitHub 地址.Mybatis-Spring 源码分析 GitHub ...
- 精尽 MyBatis 源码分析 - MyBatis 初始化(一)之加载 mybatis-config.xml
该系列文档是本人在学习 Mybatis 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释(Mybatis源码分析 GitHub 地址.Mybatis-Spring 源码分析 GitHub ...
- 精尽MyBatis源码分析 - MyBatis初始化(二)之加载Mapper接口与XML映射文件
该系列文档是本人在学习 Mybatis 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释(Mybatis源码分析 GitHub 地址.Mybatis-Spring 源码分析 GitHub ...
- 精尽 MyBatis 源码分析 - MyBatis 初始化(三)之 SQL 初始化(上)
该系列文档是本人在学习 Mybatis 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释(Mybatis源码分析 GitHub 地址.Mybatis-Spring 源码分析 GitHub ...
- 精尽MyBatis源码分析 - MyBatis 的 SQL 执行过程(一)之 Executor
该系列文档是本人在学习 Mybatis 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释(Mybatis源码分析 GitHub 地址.Mybatis-Spring 源码分析 GitHub ...
- MyBatis源码分析-MyBatis初始化流程
MyBatis 是支持定制化 SQL.存储过程以及高级映射的优秀的持久层框架.MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集.MyBatis 可以对配置和原生Map使用简 ...
随机推荐
- linux c编程:进程控制(四)进程关系
每一个进程除了有一个进程ID外,还属于一个进程组. 进程组是一个或多个进程的集合,通常情况下,他们是在同一作业中结合起来的,同一进程组的个进程接受来自同一终端的各种信号. 每一个进程组有一个唯一的进 ...
- 【足迹C++primer】32、定制操作_1
版权声明:本文为博主原创文章,未经博主同意不得转载. https://blog.csdn.net/cutter_point/article/details/32066151 定制操作 向算法传递函数 ...
- ubuntu下使用free命令查看内存实际占用(待补充)
free不带选项运行会显示一个以kb为单位的默认输出 free -h人类能看懂的方式显示 free -m MB的方式显示 free -g GB方式显示 used=total-free即total=us ...
- [原创]java WEB学习笔记16:JSP指令(page,include),JSP标签(forwar,include,param)
本博客为原创:综合 尚硅谷(http://www.atguigu.com)的系统教程(深表感谢)和 网络上的现有资源(博客,文档,图书等),资源的出处我会标明 本博客的目的:①总结自己的学习过程,相当 ...
- 利用Tkinter做的自动生成JSONSchema的小工具
前面讲到可以使用JSONSchema做json数据校验, 但是每个接口数据都手动写jsonschema太痛苦了, 就写了个小脚本,可以直接复制接口文档的mock数据然后生成一个简单的jsonschem ...
- HTTPSQS(HTTP Simple Queue Service)消息队列
HTTPSQS(HTTP Simple Queue Service)是一款基于 HTTP GET/POST 协议的轻量级开源简单消息队列服务,使用 Tokyo Cabinet 的 B+Tree Key ...
- HDU 4089 Activation:概率dp + 迭代【手动消元】
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4089 题意: 有n个人在排队激活游戏,Tomato排在第m个. 每次队列中的第一个人去激活游戏,有可能 ...
- MySQL中NOT IN语句对NULL值的处理
与使用in时不同: 在使用in 时: SELECT COUNT(name) FROM CVE WHERE name NOT IN ('CVE-1999-0001', 'CVE-1999-0002'); ...
- eclipse自动提示功能没了的解决办法
由于重新配置了环境,并且eclipse也是装的4.2的,今天用的时候发现了,居然没有自动提示功能,也就是当一个对象居然点不出他的相关方法.后来网上搜索了下,成功的 办法是. 1.我window-> ...
- hdu-5646 DZY Loves Partition(贪心)
题目链接: DZY Loves Partition Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 262144/262144 K ( ...