SqlSession执行源码分析

针对以下代码

public class MybatisUtils {
private static SqlSessionFactory sqlSessionFactory;
static{
InputStream inputStream = null;
try {
        //使用输入流读取文件
String resource = "mybatis-config.xml";
inputStream = Resources.getResourceAsStream(resource);
//现在我们分析这一行代码
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
public static SqlSession getSqlSession(){
return sqlSessionFactory.openSession();
}
 

该行代码:

      sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

1.实例化SqlSessionFactoryBuilde()对象。

2.执行build方法

  • 实际上build方法有9个,这里只列出相关的2个。

//先走这里,实际是为了走下面的build方法,把enviroment和properties都给null
public SqlSessionFactory build(InputStream inputStream) {
return build(inputStream, null, null);
}
//真正要走的build方法
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
try {
//a.这一行其实不重要,就是通过几个构造器把InputStream换成了XMLConfigBuilder
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
//b.通过XMLConfigBuilder.parse()对xml文件解析
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.
}
}
}

a.实例化XMLConfigBuilder

        //a.实例化XMLConfigBuilder
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);

a-1.XMLConfigBuilder构造器

 
//后面两个参数都是空,可以忽略,因此关键是new XPathParser(inputStream, true, props, new XMLMapperEntityResolver())
public XMLConfigBuilder(InputStream inputStream, String environment, Properties props) {
this(new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), environment, props);
}

a-2. XPathParser构造器

  
public XPathParser(InputStream inputStream, boolean validation, Properties variables, EntityResolver entityResolver) {
  //一般走的构造器properties是空的,但是如果给了properties,那么就会在这里赋值给configuration的属性properties
  commonConstructor(validation, variables, entityResolver);
//在这里把InputStream类换成Document类
this.document = createDocument(new InputSource(inputStream));
}

b-1.通过XMLConfigBuilder.parse()对xml文件解析

        //b.通过XMLConfigBuilder.parse()对xml文件解析
(parser.parse())

b-2.parse方法

  public Configuration parse() {
if (parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
}
parsed = true;
//parseConfiguration方法解析xml
parseConfiguration(parser.evalNode("/configuration"));
return configuration;
}

b-3.parseConfiguration方法解析xml

//这个方法里就可以看到很多我们熟悉的设置了:properties,settings,typeAliases...
private void parseConfiguration(XNode root) {
try {
//issue #117 read properties first
//properties设置,以这个为例
propertiesElement(root.evalNode("properties")); Properties settings = settingsAsProperties(root.evalNode("settings"));
loadCustomVfs(settings);
loadCustomLogImpl(settings);
typeAliasesElement(root.evalNode("typeAliases"));
pluginElement(root.evalNode("plugins"));
objectFactoryElement(root.evalNode("objectFactory"));
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
reflectorFactoryElement(root.evalNode("reflectorFactory"));
settingsElement(settings);
// read it after objectFactory and objectWrapperFactory issue #631
environmentsElement(root.evalNode("environments"));
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);
}
}

b-4.propertiesElement(root.evalNode("properties"));

  private void propertiesElement(XNode context) throws Exception {
if (context != null) {
//把xml文件<properties>中<property>的所有参数名、参数值以键值对放在defaults中
Properties defaults = context.getChildrenAsProperties();
//获取resource和url的参数值
String resource = context.getStringAttribute("resource");
String url = context.getStringAttribute("url");
//resource和url只能有1个
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.");
}
//把db.properties文件中的值放入到defaults中,注意上面是xml的properties配置,这里是文件
//因此properties文件是后覆盖的,优先级比xml文件要高
if (resource != null) {
defaults.putAll(Resources.getResourceAsProperties(resource));
} else if (url != null) {
defaults.putAll(Resources.getUrlAsProperties(url));
}
//查看configuration是否已经有属性了,如果有,就覆盖到defaults中
//这个实际上是build构造器中输入的属性值,在new SqlSessionFactoryBuilder().build(inputStream)方法中有输入的话,会在上文的a代码中写入到configuration类中。
//说明这里的构造器输入的优先级是最高的
Properties vars = configuration.getVariables();
if (vars != null) {
defaults.putAll(vars);
}
parser.setVariables(defaults);
//把default传入到configuration类中
configuration.setVariables(defaults);
}
}

b-5.返回到了最初的new SqlSessionFactoryBuilder().build(inputStream)方法的主线中

注意这时候我们走完的是parser.parser()

       //b.通过XMLConfigBuilder.parse()对xml文件解析
return build(parser.parse());

实际上,接着要走的是build(configuration)

 public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}

返回了 DefaultSqlSessionFactory的示例

public class MybatisUtils {
private static SqlSessionFactory sqlSessionFactory;
static{
InputStream inputStream = null;
try {
String resource = "mybatis-config.xml";
inputStream = Resources.getResourceAsStream(resource);
//现在SqlSessionFactory实例化成功了,注意这里其实是多态:
//类似:SqlSessionFactory sqlSessionFactory = new DefaultSqlSessionFactory();
//SqlSessionFactory是一个接口,被DefaultSqlSessionFactory继承
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}

//创建对象
public static SqlSession getSqlSession(){
//因此这个openSession方法实际上是被DefaultSqlSessionFactory重写的方法
return sqlSessionFactory.openSession();
}
}

3.DefaultSqlSessionFactory.openSession();

 @Override
public SqlSession openSession(ExecutorType execType, boolean autoCommit) {
return openSessionFromDataSource(execType, null, autoCommit);
}

3-1.openSessionFromDataSource(execType, null, autoCommit);

 private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null;
try {
// 获取mybati-config.xml中配置的environment对象
final Environment environment = configuration.getEnvironment();
// 获取事务工厂对象
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
// 创建事务
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
// 创建Executor对象
final Executor executor = configuration.newExecutor(tx, execType);
// 创建DefaultSqlSession对象
return new DefaultSqlSession(configuration, executor, autoCommit);
} 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();
}
}
  1. SqlSession也是多态,实际对象是DefaultSqlSession

  2. 事务和Executor在创建SqlSession以前就已经创建了

  3. Executor的源码如下,简单浏览应该是执行sql的类。下次再继续查看

public interface Executor {

ResultHandler NO_RESULT_HANDLER = null;

int update(MappedStatement ms, Object parameter) throws SQLException;

<E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey cacheKey, BoundSql boundSql) throws SQLException;

<E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException;

<E> Cursor<E> queryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds) throws SQLException;

List<BatchResult> flushStatements() throws SQLException;

void commit(boolean required) throws SQLException;

void rollback(boolean required) throws SQLException;

CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql);

boolean isCached(MappedStatement ms, CacheKey key);

void clearLocalCache();

void deferLoad(MappedStatement ms, MetaObject resultObject, String property, CacheKey key, Class<?> targetType);

Transaction getTransaction();

void close(boolean forceRollback);

boolean isClosed();

void setExecutorWrapper(Executor executor);

}

MyBatis(六):SqlSession执行源码分析的更多相关文章

  1. Alink漫谈(十六) :Word2Vec源码分析 之 建立霍夫曼树

    Alink漫谈(十六) :Word2Vec源码分析 之 建立霍夫曼树 目录 Alink漫谈(十六) :Word2Vec源码分析 之 建立霍夫曼树 0x00 摘要 0x01 背景概念 1.1 词向量基础 ...

  2. Mybatis 插件使用及源码分析

    Mybatis 插件 Mybatis插件主要是通过JDK动态代理实现的,插件可以针对接口中的方法进行代理增强,在Mybatis中比较重要的接口如下: Executor :sql执行器,包含多个实现类, ...

  3. 【Java入门提高篇】Day23 Java容器类详解(六)HashMap源码分析(中)

    上一篇中对HashMap中的基本内容做了详细的介绍,解析了其中的get和put方法,想必大家对于HashMap也有了更好的认识,本篇将从了算法的角度,来分析HashMap中的那些函数. HashCod ...

  4. 处理器适配器(handlerAdapter)执行源码分析(涉及到适配器模式)(九)

    适配器:实现很多接口统一管理. DispatcherServlet 组建的默认配置 HttpRequestHandlerAdapter,SimpleControllerHandlerAdapter,A ...

  5. Netty源码分析-- 处理客户端接入请求(八)

    这一节我们来一起看下,一个客户端接入进来是什么情况.首先我们根据之前的分析,先启动服务端,然后打一个断点. 这个断点打在哪里呢?就是NioEventLoop上的select方法上. 然后我们启动一个客 ...

  6. Vue系列---理解Vue.nextTick使用及源码分析(五)

    _ 阅读目录 一. 什么是Vue.nextTick()? 二. Vue.nextTick()方法的应用场景有哪些? 2.1 更改数据后,进行节点DOM操作. 2.2 在created生命周期中进行DO ...

  7. 11.深入k8s:kubelet工作原理及源码分析

    转载请声明出处哦~,本篇文章发布于luozhiyun的博客:https://www.luozhiyun.com 源码版本是1.19 kubelet信息量是很大的,通过我这一篇文章肯定是讲不全的,大家可 ...

  8. (五)myBatis架构以及SQlSessionFactory,SqlSession,通过代理执行crud源码分析---待更

    MyBatis架构 首先MyBatis大致上可以分为四层: 1.接口层:这个比较容易理解,就是指MyBatis暴露给我们的各种方法,配置,可以理解为你import进来的各种类.,告诉用户你可以干什么 ...

  9. 精尽 MyBatis 源码分析 - SqlSession 会话与 SQL 执行入口

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

随机推荐

  1. 前后端分离基于Oauth2的SSO单点登录怎样做?

    一.说明 单点登录顾名思义就是在多个应用系统中,只需要登录一次,就可以访问其他相互信任的应用系统,免除多次登录的烦恼:本文主要介绍跨域间的 前后端分离 项目怎样实现单点登录,并且与 非前后端分离 的差 ...

  2. 大数据存储利器 - Hbase 基础图解

    由于疫情原因在家办公,导致很长一段时间没有更新内容,这次终于带来一篇干货,是一篇关于 Hbase架构原理 的分享. Hbase 作为实时存储框架在大数据业务下承担着举足轻重的地位,可以说目前绝大多数大 ...

  3. [Alg] 文本匹配-单模匹配-KMP

    1. 暴力求解 如下图所示.蓝色的小三角表示和sequence比较时的开始字符,绿色小三角表示失败后模式串比对的开始字符,红色框表示当前比较的字符对. 当和模式串发生不匹配时,蓝色小三角后移一位,绿色 ...

  4. VS2019 C++动态链接库的创建使用(3) - 如何导出类

    如何在动态链接库里导出一个类? ①在库头文件里增加一个类声明,class DLL1_API Point是将类内所有成员都导出,如果只导出某个成员函数,则只需在对应的成员函数前加DLL1_API即可: ...

  5. Journal of Proteome Research | Mining the Proteome Associated with Rheumatic and Autoimmune Diseases(挖掘风湿和自身免疫疾病相关的蛋白组)(解读人:黄旭蕾)

    期刊名:JPR 发表时间:(2019年12月) IF:3.780 单位:Grupo de Investigación de Reumatología (GIR), Unidad de Proteó ...

  6. 解析“60k”大佬的19道C#面试题(上)

    解析"60k"大佬的19道C#面试题(上) 先略看题目: 请简述async函数的编译方式 请简述Task状态机的实现和工作机制 请简述await的作用和原理,并说明和GetResu ...

  7. [尊老爱幼] Queen

    You are given a rooted tree with vertices numerated from 1 to n . A tree is a connected graph withou ...

  8. Qt 事件和信号的关系

    Qt的事件是windows的底层消息封装而成的.这个消息和MFC里的消息是同一概念,都是指键盘.鼠标等的按压.松开等消息.例如按下键盘后,windows系统会发出一个 WM_KEYDOWN的消息,Qt ...

  9. php 设置允许跨域请求

    php 服务端代码 <?php header('Content-Type: text/html;charset=utf-8'); header('Access-Control-Allow-Ori ...

  10. Kubernetes(K8s) 安装(使用kubeadm安装Kubernetes集群)

    背景: 由于工作发生了一些变动,很长时间没有写博客了. 概述: 这篇文章是为了介绍使用kubeadm安装Kubernetes集群(可以用于生产级别).使用了Centos 7系统. 一.Centos7 ...