mybatis源码解析第四课-mybatis的SQL执行过程解析

Mybatis源码详解 基于3.5.10的版本对Mybatis源码从加载到执行全流程的解析,通过与传统JDBC方法的对比更深一步的了解Mybatis帮我们做了什么,以及是如何做的 阅读详情

目录
前言 mybatis重要类解析
1、mybatis的sql执行代码总览
2、创建session工厂实例解析
3、创建session解析
4、执行查询操作解析
5、session关闭解析

前言 mybatis重要类解析

Executor:SQL执行操作的接口定义;
BaseExecutor:抽象类,内部实现了一级缓存,采用了模版模式设计;
SimpleExecutor:sql操作的具体实现类,框架默认的执行器,直接操作数据库;
CachingExecutor:二级缓存实现类,采用了静态代理模式,只做二级缓存处理,sql的具体操作交给SimpleExecutor实现;

Cache:缓存接口定义;
PerpetualCache:缓存具体实现类,采用hashmap实现数据缓存;
SynchronziedCache:采用了静态代理模式,具体的数据缓存交给PerpetualCache实现;

在这里插入图片描述

第一、mybatis的sql执行代码总览

String resource  =  "mybatis-config.xml";
//读取myabtis核心配置文件
InputStream inputStream  =  Resources.getResourceAsStream(resource);
//创建session工厂实例
SqlSessionFactory sqlSessionFactory =  new SqlSessionFactoryBuilder().build(inputStream,"dev");
//创建session
SqlSession session = sqlSessionFactory.openSession();
 try {
     Map<String,Object> params =new HashMap<String,Object>();
     params.put("code","441");
     //执行查询操作
     Tz_disease_item blog = (Tz_disease_item) session.selectOne("com.test.dao.DiseaseItemMapper.getDiseaseItemXml", params);
 } finally {
    //关闭session
    session.close();
 }

第二、创建session工厂实例解析

XMLConfigBuilder类,解析mybatis-config.xml核心配置文件

 private void parseConfiguration(XNode root) {
    try {
      //issue #117 read properties first

      //解析properties放入到configuration
      propertiesElement(root.evalNode("properties"));
      
      //解析settings放入到configuration
      Properties settings = settingsAsProperties(root.evalNode("settings"));
      loadCustomVfs(settings);
      
      //解析类的别名配置
      typeAliasesElement(root.evalNode("typeAliases"));
      
      //MyBatis允许你在已映射语句执行过程中的某一点进行拦截调用
      pluginElement(root.evalNode("plugins"));
      
      objectFactoryElement(root.evalNode("objectFactory"));
      objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
      reflectorFactoryElement(root.evalNode("reflectorFactory"));
      settingsElement(settings);
      
      // 解析数据库配置环境
      environmentsElement(root.evalNode("environments"));
      
      databaseIdProviderElement(root.evalNode("databaseIdProvider"));
      //返回结果从数据库类型到java类型处理器
      typeHandlerElement(root.evalNode("typeHandlers"));
      
      //sqlmap文件解析加载
      mapperElement(root.evalNode("mappers"));
    } catch (Exception e) {
      throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
    }
  }

第三、创建session

1、创建Transaction对象
通过Environment创建一个事务工厂,由工厂来创建Transaction对象
2、创建Executor对象
源码类Configuration

  public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
    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 {
      //默认的sql执行器
      executor = new SimpleExecutor(this, transaction);
    }
    if (cacheEnabled) {
     //默认开启二级缓存
      executor = new CachingExecutor(executor);
    }
    executor = (Executor) interceptorChain.pluginAll(executor);
    return executor;
  }

通过传入事务对象和Executor的执行类型来创建Executor,executor的默认执行类型ExecutorType.SIMPLE。在创建SimpleExecutor对象时默认调用父类的构造方法实例化一级缓存;
sql的执行器相关的类采用了模版模式设计
Executor:执行sql接口的定义
BaseExecutor:抽象类,提供增删改查对外的接口,不负责具体实现,并内置一级缓存;
CachingExecutor:提供增删改查对外的接口,不负责具体实现,并内置二级缓存;
SimpleExecutor:增删改查的具体实现类,直接操作数据库;
3、创建DefaultSqlSession对象

第四、执行查询操作解析

1、DefaultSqlSession类
1> 通过传如的命名空间和sql的id找到对应的SQL操作类MappedStatement;
2> wrapCollection对传如的参数做处理
如果传如的不是map,将转换为map;

  @Override
  public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
    try {
     //找到SQL操作类,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();
    }
  }

2、CachingExecutor类,内置了二级缓存
1>CacheKey 的实体包括
(1)sql语句的命名空间和id
(2)分页开始位置
(3)查询数据量
(4)查询语句
(5)入参
(6)数据库环境
将引用对象作为hashmap的key特殊说明,CacheKey重写了equals和hashCode方法,用来确保创建的两个实例在hashmap中能取到相同的值,具体可研究hashmap源码。

  @Override
  public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
    //得到SQL语句
    BoundSql boundSql = ms.getBoundSql(parameterObject);
    //创建缓存的key实体
    CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
    //执行查询操作
    return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
  }

  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.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
          //将查询结果放入到一个临时hashmap中,在session提交或关闭时放入二级缓存
          tcm.putObject(cache, key, list); // issue #578 and #116
        }
        return list;
      }
    }
    return delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
  }

BaseExecutor类,内置了一级缓存

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--;
    }
    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);
    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;
  }

第五、session关闭解析

session在关闭时会清空一级缓存,并将查询结果放入到二级缓存;

TransactionalCache类,将查询结果放入到二级缓存

  private void flushPendingEntries() {
    for (Map.Entry<Object, Object> entry : entriesToAddOnCommit.entrySet()) {
     //将查询结果放入到二级缓存
      delegate.putObject(entry.getKey(), entry.getValue());
    }
    for (Object entry : entriesMissedInCache) {
      if (!entriesToAddOnCommit.containsKey(entry)) {
        delegate.putObject(entry, null);
      }
    }
  }

BaseExecutor类,清空一级缓存

  public void clearLocalCache() 
    if (!closed) {
      localCache.clear();
      localOutputParameterCache.clear();
    }
  }
MyBatis源码剖析 在二级缓存的设计上,MyBatis大量的运用了装饰者模式,如CacheExecutor,以及各种Cache接口的装饰器。二级缓存实现了sqlSession之间的缓存数据共享,属于namespace级别;二级缓存具有丰富的缓存策略;二级缓存可由多个装饰器与基础缓存组合而成;二级缓存工作由一个缓存装饰器CacheExecutor和一个事务型预缓存TransactionalCache完成。问题:在开发过程中很多时候我们并不总是需要在获取用户信息的时候加载订单信息,此时就是我们所说的延迟加载。 阅读详情

相关推荐

Mybatis源码解析之数据库连接与SQL执行

文章目录1、背景介绍2、创建SqlSession3、数据库连接4、执行SQL语句5、常见面试题 这一篇文章主要MyBatis拿到配置文件之后,即获取到数据源配置信息和SQL语句之后,然后再从源码解读分析MyBatis是如何进行数据库连接和SQL语句执行。关于MyBatis是如何解析mybatis-config.xml文件,获取到数据源的请看这篇文章: Mybatis源码解析之数据源和SQL构建 1...

qq_36526036的博客 1767

Mybatis解析(二)

Mybatis解析二第七部分:Mybatis缓存7.1 ⼀级缓存7.2 ⼆级缓存 第七部分:Mybatis缓存 7.1 ⼀级缓存 ①、在⼀个sqlSession中,对User表根据id进⾏两次查询,查看他们发出sql语句的情况 @Test public void test1(){ //根据 sqlSessionFactory 产⽣ session SqlSession sqlSession = sessionFactory.openSession(); UserMapper userMapper = sql

weixin_43380444的博客 981

Mybatis源码解析 —— Sql解析详解

通过这篇文章,我们能够学习一个Sql生成器的构建思路。也能够更加全面的了解到Mybatis Sql解析所支持的功能。

Mr_SeaTurtle_的博客 5084

mybatis源码解读篇

文章目录mybatis初始化原理mybatis执行流程1. 委托configuration获取mapper2. configuration通过mapperRegistry(type,sqlSession)获取mapper3. MapperRegistry.getMapper(type, sqlSession)4. mapperMethod构造(mapper参数解析)5. 实际执行通过SQLSession执行对应方法6. 执行前解析方法参数7. SQLSession里的执行又丢给executor8. 构造sq

用针戳左手中指指头的博客 1165

深度解读MyBatis源码 - 从SqlSession到SQL执行入口

Hello!大家好,今天我们来一场深度的MyBatis源码之旅。本篇文章的目标是让你彻底搞懂MyBatis中SqlSession的工作原理及其与SQL执行的关系。SqlSession作为MyBatis与数据库沟通的“桥梁”,在数据持久化的流程中扮演了核心角色。SqlSession可以说是MyBatis操作数据库的“会话窗口”,也是我们用来实现增删查改的最基础工具。虽然MyBatis帮我们封装了很多方便的功能,但理解它的内部原理能帮我们写出更优雅、更高效的代码。

喵手的博客 1477

win10删除vmware12后,重新安装失败(遗留注册表问题)解决总结

之前安装过一次VMware虚拟机,后来由于某些已遗忘原因把它卸载了,但是没有卸载完全,为以后安装留下了一些麻烦,现在根据我google很多内容总结后写下了自己的解决办法。 一些我搜的的可以用的VM12的注册码 5A02H-AU243-TZJ49-GTC7K-3C61N ZZ510-DYX47-48ERP-D6XZG-YCKU2 YV5MU-2FX5H-M841P-LZX5X-PYHX8

Jeason的博客 2万+

Mybatis 核心源码分析

一、Mybatis 整体执行流程 二、Mybatis 层次结构 三、源码分析

Julie的博客 1万+

亚马逊商品评论爬取与情感分析实战:Python+BeautifulSoup高效解析(附反爬技巧)

本文详细介绍了使用Python和BeautifulSoup库高效爬取亚马逊商品评论并进行情感分析的完整实战流程。文章从环境搭建、核心代码编写入手,重点讲解了如何通过设置请求头、添加延迟等防封策略应对反爬机制,确保数据抓取的稳定性。最后,利用TextBlob库对评论文本进行情感分析,并通过可视化图表直观展示产品口碑,为电商选品与市场调研提供数据支持。

weixin_29046611的博客 1164

MyBatis 源码分析 - SQL 的执行过程

文章目录* 本文速览1.简介2.SQL 执行过程分析2.1 SQL 执行入口分析2.1.1 为 Mapper 接口创建代理对象2.1.2 执行代理逻辑2.1.2.1 创建 MapperMethod 对象① 创建 SqlCommand 对象② 创建 MethodSignature 对象2.1.2.2 执行 execute 方法2.2 查询语句的执行过程分析2.2.1 selectOne 方法分析2.2.2 获取 BoundSql2.2.2.1 DynamicContext2.2.2.2 解析 SQL 片段2.

ThinkWon的博客 4461

比较几款国际主流的电子元器件及物料清单管理软件 – 工具汇总

Arenasolution 老牌的基于云计算物料清单管理软件,bom.com就是他们家的,10天免费试用,功能被分成4个不同的产品, 和传统ERP功能上有重叠,偏向于物流生产规划,一定程度可以取代ERP的生产规划模块,平均价格70美元/月/席位。网页客户端,不支持离线   SiliconExpert 曾经的老牌物料清单管理软件,后被Arrow 收购,14天免费试用,功能被分成4个不同的产品

u012062784的专栏 7235

【Unity】Unity性能分析解决方案——UPR

由Unity中国团队提供的一套性能优化解决方案,其是一系列性能工具的集合,并可将收集的数据进行可视化。

Davidorzs的博客 1008

【MyBatis 源码解析】MyBatis执行SQL源码解析(来看看mybatis是如果封装JDBC的)

看完视频之后自己去 debug 的源码,纯个人理解,仅供参考

qq_55477069的博客 2152

用python提取PDF中各类文本内容的方法

用python提取PDF中各类文本内容的方法

安静的软件工程师 6086

源码解析MyBatis Sharding-Jdbc SQL语句执行流程详解

本文以SQL执行序列图开始,以SQL执行流程图结束。详细介绍了Mybatis Sharding-jdbc SQL语句的执行流程,重点讲述了java.sql.Connection、java.sql.Statement对象的创建流程、SQL结果集的处理,并重点阐述了 Mybaits与Sharding-jdbc整合时分库分表(sharding-jdbc的路由机制的触发时机)。

中间件兴趣圈 3277

基于STM32H750的便携式双通道示波器与信号发生器AFE设计

示波器和信号发生器是电子测量与嵌入式系统调试的基础仪器,其核心在于模拟前端(AFE)对宽动态范围、高精度采样与低失真输出的协同实现。原理上需兼顾输入信号调理(衰减/放大/偏置)、ADC/DAC接口匹配、触发同步及电源完整性设计。技术价值体现在以低成本器件达成±15V输入/±10V输出、16位等效采样精度与85kHz带宽的工程平衡。典型应用场景包括电机驱动波形观测、传感器信号验证、电源环路测试及通信电平分析。本方案聚焦STM32H750平台,深度融合硬件拓扑与片上资源特性,为嵌入式教学与原型开发提供可复现、易

weixin_35755188的博客 107

CUDA&PCL 点云体素下采样

CUDA&PCL 点云体素下采样

dayuhaitang1的博客 1268

Mybatis从源码分析——启动到解析配置文件再到执行SQL语句过程

try {//将XML配置文件构建为Configuration配置类// 通过加载配置文件流构建一个SqlSessionFactory DefaultSqlSessionFactory// 数据源 执行器 DefaultSqlSessiontry {// 执行查询 底层执行jdbc}finally {从配置文件,通常是XML配置文件,获得SqlSessionFactory从SqlSessionFactory中获得SqlSession。

qq_44027353的博客 1013

通过miniconda conda安装jupyer的时候报错: InvalidArchiveError(“Error with archive

摘要:本文针对Windows系统使用Conda时出现的InvalidArchiveError错误(文件路径过长或压缩包损坏),提供了解决方案。建议以管理员身份运行PowerShell并执行命令启用长路径支持(LongPathsEnabled),然后重启电脑或终端。作者通过该方法成功解决了jupyter安装问题。附有错误截图和解决后的成功截图。

猿小白的博客 786

Mybatis SQL执行过程

MyBatis中还有很多很多多西都值得我们学习,像前文提到的Statement对象究竟是如何获取的,以及最后的ResultHandler是如何处理结果集对象的,都值得我们研究。首先从应用入手,MyBatis是如何执行SQL的,如何获取返回的结果集,了解了执行SQL的大致过程后,再带着问题逐步深挖源码。doQuery方法的具体实现是交给子类的,我们平时用到的也就是SimpleExecutor,接下来来看下SimpleExecutor是如何实现doQuery方法的。我们知道,MyBatis是对JDBC的封装。

z1ztai的博客 722

数学建模模型总结

这是本人在两年内准备参加数学建模竞赛搜集到的模型资料,已经经过了本人的筛选和整理,希望能够对准备参加数学建模竞赛的学弟学妹们提供一些帮助!

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

Mybatis源码学习-sql执行流程

在mapperRegistry 从已知的knownMappers 一个集合中获取 Mapper代理对象工厂,再由MapperProxyFactory 去创建一个实例,最终返回,BaseExecutor 中有一个 localCache ,应该就是Mybatis的一级缓存,CachingExecutor 中的是二级缓存,当缓存中不存在时,则从数据库中查询。在 mapperRegistry 中有一个 mapperRegistry的方法,我们在这里打一个断点,然后重新启动,观察是什么时候进入到这个方法的。

qq_45325916的博客 1115

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

MyBatis源码之MyBatis中SQL语句执行过程

本文是MyBatis源码之MyBatis中SQL语句执行过程,使用图文并茂的方式,讲解了SQL语句执行过程,调用了哪些方法,和这些方法是如何调用的。

随机的未知——用已知去面对未知 1557

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

mybatis查询过程源码解析

本文只讲解mybatis的源码 ,不涉及过多的用法讲解,用法请百度。 mybatis查询执行步骤(举SELECT分析) 第一步:获取mapperProxy 第二步:获取mapperMethod 第三步:找到对应的执行类型(SELECT\UPDATE\INSERT\DELETE) 第四步:查询前的缓存处理,判断是否需要从二级、一级缓存中取数据(先二级再一级) 第五步:真正调用用jdbc调数据库,先预编译、再注入参数 第六步:进行结果集解析填充 ...

Lisam Blog 4147

MyBatis中一个SQL语句的执行过程解析

MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。 平时用MyBatis框架开发时,配置好config.xml和mapper.xml映射文件和定义好java接口,就可以操作数据库了 mybatis配置文...

董广明的博客 970

MyBatis 源码解析——SQL解析

不想被抓取,随便写点.....

k7c9。 2010

Mybatis源码解析之数据源和SQL构建

这里写自定义目录标题1、Mybatis官网简介2、Demo搭建2.1、maven依赖2.2、mybatis配置2.3、mapper文件配置2.3、测试类2、源码解析 1、Mybatis官网简介 MyBatis一款半自动的ORM持久层框架,它支持自定义SQL、存储过程以及高级映射。 MyBatis对JDBC进行了封装,几乎免除了所有的 JDBC 代码以及设置参数和获取结果集的工作。 MyBatis...

qq_36526036的博客 2077
上一篇: Spring中注解@Scope详解
下一篇: maven将本地jar打包进入项目
hnlcwx2917
博客等级 码龄12年 42粉丝 55原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值