总体三部分,创建sessionfactory,创建session,执行sql获取结果

1,创建sessionfactory

     这里其实主要做的事情就是将xml的所有配置信息转换成一个Configuration对象,然后用这个对象组装成factory返回。

     //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中所有配置信息转换成对象的具体方法了

  //将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);
}
}
          主要看三个吧
          properties的转换,从这里的逻辑可以看出来资源信息的加载顺序,在之前的章节里有说过。

  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);
}
}
          environments的转换,这里首先会看是否传进来了一个id,如果么有,那么用默认的。
    

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());
}
}
}
}
          其次,获取事物factory的时候,会根据配置的transactionManager属性来获取看是用JdbcTransactionFactory还是ManagedTransactionFactory,这里会涉及到事物的一些处理机制,两种类处理方法不一样的。
    

  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.");
}
          再次,获取datasourcefactory的时候,会根据配置的dataSource属性来看是使用UnpooledDataSourceFactory 还是pooledDataSourceFactory ,还是JndiDataSourceFactory。这里会涉及到数据源的获取方式,是连接池,还是单次链接,还是jndi。

    

  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。

     一种JdbcTransactionFactory,直观地讲,就是JdbcTransaction是使用的java.sql.Connection 上的commit和rollback功能,JdbcTransaction只          是相当于对java.sql.Connection事务处理进行了一次包装(wrapper),Transaction的事务管理都是通过java.sql.Connection实现的。

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(); } }
     一种是ManagedTransactionFactory, ManagedTransaction让容器来管理事务Transaction的整个生命周期,意思就是说,使用ManagedTransaction的commit和rollback功能不会对事务有任何的影响,它什么都不会做,它将事务管理的权利移交给了容器来实现。

  //提交和回滚什么都不做,让别人来管理
public void commit() throws SQLException {
// Does nothing
} public void rollback() throws SQLException {
// Does nothing
}

2,然后从configuration中获取到对应的datasource,这里就可能有三种了

     一种是UnpooledDataSourceFactory,这种的话,每次调用都会产生一次连接。耗费资源。

  private Connection doGetConnection(Properties properties) throws SQLException {
  //初始化驱动
initializeDriver();
  //jdbc直接获取一个连接
Connection connection = DriverManager.getConnection(url, properties);
configureConnection(connection);
return connection;
}
     一种是pooledDataSourceFactory,这种的话,会有一个连接池的东西在里面,使用的时候每次就从连接池里用就行,

  //方法内容过多,可以自己去看  这里是从一个集合中pop出来一个连接直接使用。
private PooledConnection popConnection(String username, String password) throws SQLException {
     一种是JndiDataSourceFactory, 对于JNDI类型的数据源DataSource的获取就比较简单,MyBatis定义了一个JndiDataSourceFactory工厂来创建通过JNDI形式生成的DataSource。

    //从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。

然后用这个tx重新组装成mybatis自己的一个执行器Executor,这里需要注意,如果二级缓存的开关开启了,那么得到的会是一个CachingExecutor,将来执行查询的时候,就会使用这个去执行,会先使用二级缓存,然后在一级缓存,然后再db查询。

  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,二级缓存里如果找不到

也不会直接执行sql,会先从一级缓存里找,除非这里专门设置了flush,如果找到了,那么直接使用,找不到,执行sql查找,然后将结果设置到缓存中。这里查到数据之后,会根据自己定义的映射关系,来组装对象并返回。

    //一级缓存查找
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的更多相关文章

  1. 【转】MaBatis学习---源码分析MyBatis缓存原理

    [原文]https://www.toutiao.com/i6594029178964673027/ 源码分析MyBatis缓存原理 1.简介 在 Web 应用中,缓存是必不可少的组件.通常我们都会用 ...

  2. DotNetty网络通信框架学习之源码分析

    DotNetty网络通信框架学习之源码分析 有关DotNetty框架,网上的详细资料不是很多,有不多的几个博友做了简单的介绍,也没有做深入的探究,我也根据源码中提供的demo做一下记录,方便后期查阅. ...

  3. Yii2 源码分析 入口文件执行流程

    Yii2 源码分析  入口文件执行流程 1. 入口文件:web/index.php,第12行.(new yii\web\Application($config)->run()) 入口文件主要做4 ...

  4. 精尽MyBatis源码分析 - MyBatis初始化(四)之 SQL 初始化(下)

    该系列文档是本人在学习 Mybatis 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释(Mybatis源码分析 GitHub 地址.Mybatis-Spring 源码分析 GitHub ...

  5. 精尽 MyBatis 源码分析 - MyBatis 初始化(一)之加载 mybatis-config.xml

    该系列文档是本人在学习 Mybatis 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释(Mybatis源码分析 GitHub 地址.Mybatis-Spring 源码分析 GitHub ...

  6. 精尽MyBatis源码分析 - MyBatis初始化(二)之加载Mapper接口与XML映射文件

    该系列文档是本人在学习 Mybatis 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释(Mybatis源码分析 GitHub 地址.Mybatis-Spring 源码分析 GitHub ...

  7. 精尽 MyBatis 源码分析 - MyBatis 初始化(三)之 SQL 初始化(上)

    该系列文档是本人在学习 Mybatis 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释(Mybatis源码分析 GitHub 地址.Mybatis-Spring 源码分析 GitHub ...

  8. 精尽MyBatis源码分析 - MyBatis 的 SQL 执行过程(一)之 Executor

    该系列文档是本人在学习 Mybatis 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释(Mybatis源码分析 GitHub 地址.Mybatis-Spring 源码分析 GitHub ...

  9. MyBatis源码分析-MyBatis初始化流程

    MyBatis 是支持定制化 SQL.存储过程以及高级映射的优秀的持久层框架.MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集.MyBatis 可以对配置和原生Map使用简 ...

随机推荐

  1. Pinpoint扩展插件实践笔记

    为链路(spanEvent)添加tag 背景 我们可能需要想在代码中写入特定的信息到调用链中,并且希望对里面的特定key做检索 实现思路 创建一个特定的类,只需要一个方法,再对这个类的方法进行增强,这 ...

  2. Ionic background地址写法问题

    1.背景图片 background:url(‘/img/text.jpg') 这种写法在手机上不好使 ’../img/text.jpg' 这种在手机上好使

  3. 3像素文本偏移bug 解决方案

    <style>.box1{ width:100px; height:50px; float:left; background:#dedede;_margin-right:-3px;}.bo ...

  4. MySQL 及 SQL 注入与防范方法

    所谓SQL注入,就是通过把SQL命令插入到Web表单递交或输入域名或页面请求的查询字符串,最终达到欺骗服务器执行恶意的SQL命令. 我们永远不要信任用户的输入,我们必须认定用户输入的数据都是不安全的, ...

  5. explain分析sql效果

    1.id:  代表select 语句的编号, 如果是连接查询,表之间是平等关系, select 编号都是1,从1开始. 如果某select中有子查询,则编号递增.如下一条语句2个结果 mysql> ...

  6. 算法(Algorithms)第4版 练习 1.5.23

    package com.qiusongde; import edu.princeton.cs.algs4.StdOut; public class Exercise1523 { public stat ...

  7. 分享知识-快乐自己:反射机制Demo解析

    Java-Reflect专题 基本反射简介: 1):JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象能够调用它的任意方法和属性;这种动态获取信息以及动 ...

  8. SpringBoot_03_依赖本地jar

    一.方法一 1.说明 用Maven打到本地仓库,然后直接引入 2.参考资料 Springboot 打Jar包,Maven完美解决本地Jar包自动打入Springboot Jar包中 3.执行maven ...

  9. BEC listen and translation exercise 42

    These were built for the workers towards the end of the eighteenth century, and they are still furni ...

  10. 关于CString与VARIANT(CComVariant)之间的转化

    一.VARIANT.CComVariant类与CString是什么: CString是MFC定义的字符串类,VARIANT是COM标准为了使COM组件能够被各种语言使用(vc++.vb.java.py ...